""" TV/composite channels, list management, channelset constants """ ## MODULE IMPORTS ######################################################## import os, cPickle, time import bt848, btdriver, btcopyright, buildprefs if buildprefs.WITH_SAA: import saa ## MODULE COPYRIGHT ###################################################### MODULE_AUTHOR = btcopyright.MY_NAME MODULE_AUTHOR_EMAIL = btcopyright.MY_EMAIL MODULE_COPYRIGHT = btcopyright.MY_COPYRIGHT MODULE_LICENSE = btcopyright.BSD_LICENSE MODULE_LICENSE_TEXT = btcopyright.BSD_LICENSE_TEXT ## MODULE CONSTANTS ###################################################### # Input norm per country - Informative only # Copied from mplayer's frequencies.h, by Nathan Laredo # # COMPREHENSIVE LIST OF FORMAT BY COUNTRY # (M) NTSC used in: # Antigua, Aruba, Bahamas, Barbados, Belize, Bermuda, Bolivia, Burma, # Canada, Chile, Colombia, Costa Rica, Cuba, Curacao, Dominican Republic, # Ecuador, El Salvador, Guam, Guatemala, Honduras, Jamaica, Japan, # South Korea, Mexico, Montserrat, Myanmar, Nicaragua, Panama, Peru, # Philippines, Puerto Rico, St Christopher and Nevis, Samoa, Suriname, # Taiwan, Trinidad/Tobago, United States, Venezuela, Virgin Islands # (B) PAL used in: # Albania, Algeria, Australia, Austria, Bahrain, Bangladesh, Belgium, # Bosnia-Herzegovinia, Brunei, Darussalam, Cambodia, Cameroon, Croatia, # Cyprus, Denmark, Egypt, Ethiopia, Equatorial Guinea, Finland, Germany, # Ghana, Gibraltar, Greenland, Iceland, India, Indonesia, Israel, Italy, # Jordan, Kenya, Kuwait, Liberia, Libya, Luxembourg, Malaysa, Maldives, # Malta, Nepal, Netherlands, New Zeland, Nigeria, Norway, Oman, Pakistan, # Papua New Guinea, Portugal, Qatar, Sao Tome and Principe, Saudi Arabia, # Seychelles, Sierra Leone, Singapore, Slovenia, Somali, Spain, # Sri Lanka, Sudan, Swaziland, Sweden, Switzeland, Syria, Thailand, # Tunisia, Turkey, Uganda, United Arab Emirates, Yemen # (N) PAL used in: (Combination N = 4.5MHz audio carrier, 3.58MHz burst) # Argentina (Combination N), Paraguay, Uruguay # (M) PAL (525/60, 3.57MHz burst) used in: # Brazil # (G) PAL used in: # Albania, Algeria, Austria, Bahrain, Bosnia/Herzegovinia, Cambodia, # Cameroon, Croatia, Cyprus, Denmark, Egypt, Ethiopia, Equatorial Guinea, # Finland, Germany, Gibraltar, Greenland, Iceland, Israel, Italy, Jordan, # Kenya, Kuwait, Liberia, Libya, Luxembourg, Malaysia, Monaco, # Mozambique, Netherlands, New Zealand, Norway, Oman, Pakistan, # Papa New Guinea, Portugal, Qatar, Romania, Sierra Leone, Singapore, # Slovenia, Somalia, Spain, Sri Lanka, Sudan, Swaziland, Sweeden, # Switzerland, Syria, Thailand, Tunisia, Turkey, United Arab Emirates, # Yemen, Zambia, Zimbabwe # (D) PAL used in: # China, North Korea, Romania, Czech Republic # (H) PAL used in: # Belgium # (I) PAL used in: # Angola, Botswana, Gambia, Guinea-Bissau, Hong Kong, Ireland, Lesotho, # Malawi, Nambia, Nigeria, South Africa, Tanzania, United Kingdom, # Zanzibar # (B) SECAM used in: # Djibouti, Greece, Iran, Iraq, Lebanon, Mali, Mauritania, Mauritus, # Morocco # (D) SECAM used in: # Afghanistan, Armenia, Azerbaijan, Belarus, Bulgaria, # Estonia, Georgia, Hungary, Zazakhstan, Lithuania, Mongolia, Moldova, # Russia, Slovak Republic, Ukraine, Vietnam # (G) SECAM used in: # Greece, Iran, Iraq, Mali, Mauritus, Morocco, Saudi Arabia # (K) SECAM used in: # Armenia, Azerbaijan, Bulgaria, Estonia, Georgia, # Hungary, Kazakhstan, Lithuania, Madagascar, Moldova, Poland, Russia, # Slovak Republic, Ukraine, Vietnam # (K1) SECAM used in: # Benin, Burkina Faso, Burundi, Chad, Cape Verde, Central African # Republic, Comoros, Congo, Gabon, Madagascar, Niger, Rwanda, Senegal, # Togo, Zaire # (L) SECAM used in: # France # Channelsets or frequency tables (rounded) # Info from mplayer's frequencies.c, by Nathan Laredo CHANSET_NTSC_JP_BCAST = [ 91, 97, 103, 171, 177, 183, 189, 193, 199, 205, 211, 217, 471, 477, 483, 489, 495, 501, 507, 513, 519, 525, 531, 537, 543, 549, 555, 561, 567, 573, 579, 585, 591, 597, 603, 609, 615, 621, 627, 633, 639, 645, 651, 657, 663, 669, 675, 681, 687, 693, 699, 705, 711, 717, 723, 729, 735, 741, 747, 753, 759, 765] CHANSET_NTSC_JP_CABLE = [109, 115, 121, 127, 133, 139, 145, 151, 157, 165, 223, 231, 237, 243, 249, 253, 259, 265, 271, 277, 283, 289, 295, 301, 307, 313, 319, 325, 331, 337, 343, 349, 355, 361, 367, 373, 379, 385, 391, 397, 403, 409, 415, 421, 427, 433, 439, 445, 451, 457, 463] CHANSET_NTSC_US_BCAST = [ 55, 61, 67, 77, 83, 175, 181, 187, 193, 199, 205, 211, 471, 477, 483, 489, 495, 501, 507, 513, 519, 525, 531, 537, 543, 549, 555, 561, 567, 573, 579, 585, 591, 597, 603, 609, 615, 621, 627, 633, 639, 645, 651, 657, 663, 669, 675, 681, 687, 693, 699, 705, 711, 717, 723, 729, 735, 741, 747, 753, 759, 765, 771, 777, 783, 789, 795, 801, 807, 813, 819, 825, 831, 837, 843, 849, 855, 861, 867, 873, 879, 885] CHANSET_NTSC_US_CABLE = [ 8, 14, 20, 26, 32, 38, 44, 50, 55, 61, 67, 73, 77, 83, 91, 97, 103, 109, 115, 121, 127, 133, 139, 145, 151, 157, 163, 169, 175, 181, 187, 193, 199, 205, 211, 217, 223, 229, 235, 241, 247, 253, 259, 265, 271, 277, 283, 289, 295, 301, 307, 313, 319, 325, 331, 337, 343, 349, 355, 361, 367, 373, 379, 385, 391, 397, 403, 409, 415, 421, 427, 433, 439, 445, 451, 457, 463, 469, 475, 481, 487, 493, 499, 505, 511, 517, 523, 529, 535, 541, 547, 553, 559, 565, 571, 577, 583, 589, 595, 601, 607, 613, 619, 625, 631, 637, 643, 649, 655, 661, 667, 673, 679, 685, 691, 697, 703, 709, 715, 721, 727, 733, 739, 745, 751, 757, 763, 769, 775, 781, 787, 793, 799] CHANSET_NTSC_US_HRC = [ 7, 13, 19, 25, 31, 37, 43, 49, 54, 60, 66, 72, 78, 84, 102, 108, 114, 120, 126, 132, 138, 144, 150, 156, 162, 168, 174, 180, 186, 192, 198, 204, 210, 216, 222, 228, 234, 240, 246, 252, 258, 264, 270, 276, 282, 288, 294, 300, 306, 312, 318, 324, 330, 336, 342, 348, 354, 360, 366, 372, 378, 384, 390, 396, 402, 408, 414, 420, 426, 432, 438, 444, 450, 456, 462, 468, 474, 480, 486, 492, 498, 504, 510, 516, 522, 528, 534, 540, 546, 552, 558, 564, 570, 576, 582, 588, 594, 600, 606, 612, 618, 624, 630, 636, 642, 648, 654, 660, 666, 672, 678, 684, 690, 696, 702, 708, 714, 720, 726, 732, 738, 744, 750, 756, 762, 768, 774, 780, 786, 792, 798, 900, 960] CHANSET_PAL_ARGENTINA = [ 56, 62, 68, 78, 84, 122, 128, 134, 140, 146, 152, 158, 164, 170, 176, 182, 188, 194, 200, 206, 212, 218, 224, 230, 236, 242, 248, 254, 260, 266, 272, 278, 284, 290, 296, 302, 308, 314, 320, 326, 332, 338, 344, 350, 356, 362, 368, 374, 380, 386, 392, 398, 404, 410, 416, 422, 428, 434, 440, 446, 452, 458, 464, 470, 476, 482, 488, 494, 500, 506, 512, 518, 524, 530, 536, 542, 548, 554, 560, 566, 572, 578, 584, 590, 596, 602, 608, 614, 620, 626, 632, 638, 644] CHANSET_PAL_AUSTRALIA = [ 46, 57, 64, 86, 95, 102, 138, 175, 182, 189, 196, 209, 216, 527, 534, 541, 548, 555, 562, 569, 576, 591, 604, 611, 618, 625, 632, 639, 646, 653, 660, 667, 674, 681, 688, 695, 702, 709, 716, 723, 730, 737, 744, 751, 758, 765, 772, 779, 786, 793, 800, 807, 814] CHANSET_PAL_CN_BCAST = [ 50, 58, 66, 77, 85, 112, 120, 128, 136, 144, 152, 160, 168, 176, 184, 192, 200, 208, 216, 224, 232, 240, 248, 256, 264, 272, 280, 288, 296, 304, 312, 320, 328, 336, 344, 352, 360, 368, 376, 384, 392, 400, 408, 416, 424, 432, 440, 448, 456, 463, 471, 479, 487, 495, 503, 511, 515, 519, 527, 535, 543, 559, 607, 615, 623, 631, 639, 647, 655, 663, 671, 679, 687, 695, 703, 711, 719, 727, 735, 743, 751, 759, 767, 775, 791, 793, 799, 807, 815, 823, 831, 839, 847, 855] CHANSET_PAL_EUR_EAST = [ 48, 50, 55, 59, 62, 69, 76, 77, 83, 85, 93, 105, 111, 112, 119, 126, 127, 133, 135, 140, 143, 147, 151, 154, 159, 161, 167, 168, 175, 182, 183, 189, 191, 196, 199, 203, 207, 210, 215, 217, 223, 224, 231, 238, 239, 245, 247, 252, 255, 259, 263, 266, 271, 273, 279, 280, 287, 294, 295, 303, 311, 319, 327, 335, 343, 351, 359, 367, 375, 383, 391, 399, 407, 415, 423, 431, 439, 447, 455, 463, 471, 479, 487, 495, 503, 511, 519, 527, 535, 543, 551, 559, 567, 575, 583, 591, 599, 607, 615, 623, 631, 639, 647, 655, 663, 671, 679, 687, 695, 703, 711, 719, 727, 735, 742, 751, 759, 767, 775, 783, 791, 799, 807, 815, 823, 831, 839, 847, 855] CHANSET_PAL_EUR_WEST = [ 48, 55, 62, 69, 76, 83, 105, 112, 119, 126, 133, 140, 147, 154, 161, 168, 175, 182, 189, 196, 203, 210, 217, 224, 231, 238, 245, 252, 259, 266, 273, 280, 294, 303, 311, 319, 327, 335, 343, 351, 359, 367, 375, 383, 391, 399, 407, 415, 423, 431, 439, 447, 455, 463, 471, 479, 487, 495, 503, 511, 519, 527, 535, 543, 551, 559, 567, 575, 583, 591, 599, 607, 615, 623, 631, 639, 647, 655, 663, 671, 679, 687, 695, 703, 711, 719, 727, 735, 742, 751, 759, 767, 775, 783, 791, 799, 807, 815, 823, 831, 839, 847, 855] CHANSET_PAL_IRELAND = [ 46, 54, 62, 175, 183, 191, 199, 207, 215, 471, 479, 487, 495, 503, 511, 519, 527, 535, 543, 551, 559, 567, 575, 583, 591, 599, 607, 615, 623, 631, 639, 647, 655, 663, 671, 679, 687, 695, 703, 711, 719, 727, 735, 742, 751, 759, 767, 775, 783, 791, 799, 807, 815, 823, 831, 839, 847, 855] CHANSET_PAL_ITALY = [ 54, 62, 82, 175, 183, 192, 201, 210, 217, 224, 471, 479, 487, 495, 503, 511, 519, 527, 535, 543, 551, 559, 567, 575, 583, 591, 599, 607, 615, 623, 631, 639, 647, 655, 663, 671, 679, 687, 695, 703, 711, 719, 727, 735, 742, 751, 759, 767, 775, 783, 791, 799, 807, 815, 823, 831, 839, 847, 855] CHANSET_PAL_NZEALAND = [ 45, 55, 62, 175, 182, 189, 196, 203, 210, 217, 224, 471, 479, 487, 495, 503, 511, 519, 527, 535, 543, 551, 559, 567, 575, 583, 591, 599, 607, 615, 623, 631, 639, 647, 655, 663, 671, 679, 687, 695, 703, 711, 719, 727, 735, 742, 751, 759, 767, 775, 783, 791, 799, 807, 815, 823, 831, 839, 847, 855] CHANSET_PAL_ZA_BCAST = [175, 183, 191, 199, 207, 215, 223, 231, 471, 479, 487, 495, 503, 511, 519, 527, 535, 543, 551, 559, 567, 575, 583, 591, 599, 607, 615, 623, 631, 639, 647, 655, 663, 671, 679, 687, 695, 703, 711, 719, 727, 735, 742, 751, 759, 767, 775, 783, 791, 799, 807, 815, 823, 831, 839, 847, 855] CHANSET_SECAM_FRANCE = [ 48, 56, 61, 64, 117, 129, 141, 160, 165, 176, 177, 184, 189, 192, 200, 201, 208, 213, 216, 225, 237, 249, 261, 273, 285, 297, 303, 311, 319, 327, 335, 343, 351, 359, 367, 375, 383, 391, 399, 407, 415, 423, 431, 439, 447, 471, 479, 487, 495, 503, 511, 519, 527, 535, 543, 551, 559, 567, 575, 583, 591, 599, 607, 615, 623, 631, 639, 647, 655, 663, 671, 679, 687, 695, 703, 711, 719, 727, 735, 742, 751, 759, 767, 775, 783, 791, 799, 807, 815, 823, 831, 839, 847, 855] CHANSET_SECAM_RUSSIA = [ 50, 59, 77, 85, 93, 111, 119, 127, 135, 143, 151, 159, 167, 175, 183, 191, 199, 207, 215, 223, 231, 239, 247, 255, 263, 271, 279, 287, 295, 303, 311, 319, 327, 335, 343, 351, 359, 367, 375, 383, 391, 399, 407, 415, 423, 431, 439, 447, 455, 463, 471, 479, 487, 503, 511, 519, 527, 535, 543, 551, 559, 567, 575, 583, 591, 599, 607, 615, 623, 631, 639, 647, 655, 663, 671, 679, 687, 695, 703, 711, 719, 727, 735, 743, 751, 759, 767, 775, 783, 791, 799, 807, 815, 823, 831, 839, 847, 855] # Channelset fancy names per input norm for convenience CHANSETS_NTSC_NAMES = [("Japan Broadcast", CHANSET_NTSC_JP_BCAST), ("Japan Cable", CHANSET_NTSC_JP_CABLE), ("US Broadcast", CHANSET_NTSC_US_BCAST), ("US Cable", CHANSET_NTSC_US_CABLE), ("US Cable HRC", CHANSET_NTSC_US_HRC)] CHANSETS_PAL_NAMES = [("Argentina", CHANSET_PAL_ARGENTINA), ("Australia", CHANSET_PAL_AUSTRALIA), ("China Broadcast", CHANSET_PAL_CN_BCAST), ("Europe East", CHANSET_PAL_EUR_EAST), ("Europe West", CHANSET_PAL_EUR_WEST), ("Ireland", CHANSET_PAL_IRELAND), ("Italy", CHANSET_PAL_ITALY), ("New Zealand", CHANSET_PAL_NZEALAND), ("South Africa Broadcast", CHANSET_PAL_ZA_BCAST)] CHANSETS_SECAM_NAMES = [("France", CHANSET_SECAM_FRANCE), ("Russia", CHANSET_SECAM_RUSSIA)] # Names for composites, used as ID COMPOSITE_NAMES = ["EXT0", "EXT1", "EXT2", "EXT3", "SVID"] # For convenience AFC_STEP = 5 CHANNEL_NAME_MAXLEN = 64 SCAN_STEP = 2 ## BTCHANNEL CLASS ####################################################### class BTChannel: """ This class describes a TV channel. """ # Constructor def __init__(self, frequency=0, name="", finetune=True): """ -> BTChannel The constructor takes an integer frequency and a string name, and assigns them to the atributes frequency and name.Unless finetune is set to False, the method fineTune() is called. Frequency and name are cut off if needed to match this: - bt848.MINFREQ (0) <= frequency <= bt848.MAXFREQ (900) or saa.MINFREQ <= frequency <= saa.MAXFREQ if applicable - len(name) <= CHANNEL_NAME_MAXLEN (64) If frequency and name are not passed, they become MINFREQ and "" and can be set with the set methods. Also tunes to the frequency. """ self.frequency, self.name = 0, "" self.setFrequency(frequency, finetune) self.setName(name) def setFrequency(self, frequency, finetune=True): """ -> void Sets the BTChannel object's frequency attribute to the integer value specified by the argument frequency (MHz). Unless finetune is set to False, the method fineTune() is called (does nothing with saa). Also tunes to the frequency. """ if type(frequency) == type(848): self.frequency = frequency if finetune: self.fineTune() else: if btdriver.use_driver == "saa": try: saa.tuner_videosource_set(saa.tuner_tunerdev()) while saa.tuner_frequency_set(self.frequency) < 0: saa.tuner_quit() saa.tuner_init() saa.tuner_if_init() time.sleep(0.1) time.sleep(0.1) except NameError: pass else: bt848.tuner_frequency_set(self.frequency) def setName(self, name): """ -> void Sets the BTChannel object's name attribute to the string specified by the argument name. Delimited to CHANNEL_NAME_MAXLEN (64) characters. """ if type(name) == type("foo"): if len(name) > CHANNEL_NAME_MAXLEN: name = name[:CHANNEL_NAME_MAXLEN] self.name = name def fineTune(self): """ -> void Attempts simple fine-tuning using AFC (bt848 module). AFC has a maximum "float" of AFC_STEP (5 MHz). If one sets the tuner frequency to f with AFC, the optimum of f to f+4 inclusive is picked (that doesn't mean an actual TV signal is there, it may also be the "loudest" noise). There's a need to distribute the AFC_STEP evenly around f: First the tuner is told to tune to f-2 (SCAN_STEP). Then AFC kicks in and sets the actual frequency to f-2, f-1, f, f+1 or f+2. If this method is used, it should probably be when the BTChannel object has just been created. Also tunes to the AFC'd frequency. If the saa driver is being used, no AFC and only tunes to freq. """ if btdriver.use_driver == "saa": try: saa.tuner_videosource_set(saa.tuner_tunerdev()) while saa.tuner_frequency_set(self.frequency) < 0: saa.tuner_quit() time.sleep(0.1) saa.tuner_init() saa.tuner_if_init() time.sleep(0.1) except NameError: pass return if not (bt848.tuner_videosource_set(bt848.tuner_tunerdev()) < 0): # Keep current AFC value afc = bt848.tuner_afc() # Turn AFC on if not (bt848.tuner_afc_set(1) < 0): if self.frequency < SCAN_STEP: # If between 0 and 2, set to 2 and let it AFC bt848.tuner_frequency_set(SCAN_STEP) else: # Set to 2 MHz lower and let it AFC bt848.tuner_frequency_set(self.frequency - SCAN_STEP) # Set frequency attribute to the AFC'd (actual) frequency self.frequency = bt848.tuner_frequency() # Restore AFC value bt848.tuner_afc_set(afc) else: bt848.tuner_frequency_set(self.frequency) ## BTCOMPOSITE CLASS ##################################################### class BTComposite: """ This class describes a composite input (or SVideo). """ def __init__(self, device=0, name=""): """ -> BTComposite The constructor takes an integer device for the input device (0 to 4 = DEV0 to 3 and SVIDEO) and a string name to give it a name. Name is assigned to the attribute name and the attribute id has the form "EXT0" or "SVID" and has the same identifying role as the frequency has in a BTChannel object. The device argument corresponds with the index of device in COMPOSITE_NAMES. Dev and name are cut off if needed to match this: - 0 <= dev <= 4 - len(name) <= CHANNEL_NAME_MAXLEN (64) If dev is the tuner/TV, it will return a None object. If dev and name are not passed, they become 0 and "" and can be set with the set methods. It's up to the programmer to exclude the TV input (often DEV1). See BTChannelList.tuner. Also tunes to the device. """ self.id, self.name = COMPOSITE_NAMES[0], "" self.setDevice(device) self.setName(name) def setDevice(self, device): """ -> void Sets the BTComposite object's id attribute to the ustring COMPOSITE_NAMES[device] if device is an integer in [0..4]. Sets nothing if < 0 or > 4. Also tunes to the device. """ if type(device) == type(0): if device >= 0 and device < len(COMPOSITE_NAMES): self.id = COMPOSITE_NAMES[device] if btdriver.use_driver == "saa": try: saa.tuner_videosource_set(device) except NameError: pass else: bt848.tuner_videosource_set(device) def setName(self, name): """ -> void Sets the BTComposite object's name attribute to the string specified by the argument name. Delimited to CHANNEL_NAME_MAXLEN (64) characters. """ if type(name) == type("foo"): if len(name) > CHANNEL_NAME_MAXLEN: name = name[:CHANNEL_NAME_MAXLEN] self.name = name ## BTCHANNELLIST CLASS ################################################### class BTChannelList: """ This class describes a channel list and ways to manage it. """ def __init__(self): """ -> BTChannelList Creates a BTChannelList object. It has an attribute channels, which is an ordered list of arbitrary size holding BTChannel and BTComposite objects. It also has a convenience attribute tuner, so that bt848.tuner_tunerdev need only be called once. If the saa driver is used the tuner device is always 1. It has methods to append, delete, move channel objects, as well as for reading and writing a representation of the data as a plain text file. It also has a few tuning methods for getting and setting the channel. The channels attribute is initialized to an empty list. BTChannelLists cycle around when moving members, so that moving one below index 0 will put it on the highest index again and vice versa. """ self.channels = [] if btdriver.use_driver == "saa": try: self.tuner = saa.tuner_tunerdev() # 1 except NameError: pass else: self.tuner = bt848.tuner_tunerdev() def len(self): """ -> int Returns the current length of the channel list. """ return len(self.channels) def index(self, obj): """ -> int Returns the channel list index of obj, or -1 if not in it. """ if self.isBTChannel(obj) or self.isBTComposite(obj): if obj in self.channels: return self.channels.index(obj) return -1 def append(self, obj, replace=False): """ -> void Appends a BTChannel/BTComposite object at the "end" of the channel list (or at "position -1"). Does nothing if obj is an object of another type. The boolean replace indicates if adding the new channel should lead to the removal of any already present channel with the same frequency or composite id, or the other way around (if already present, don't append). If omitted, replace is False. """ for item in self.channels: if self.isBTChannel(obj) and self.isBTChannel(item): if item.frequency == obj.frequency: if replace: del self.channels[self.index(item)] self.channels.append(obj) return if self.isBTComposite(obj) and self.isBTComposite(item): if item.id == obj.id: if replace: del self.channels[self.index(item)] self.channels.append(obj) return self.channels.append(obj) def isBTChannel(self, obj): """ -> bool Returns True if obj is a BTChannel object. """ if isinstance(obj, BTChannel): return True return False def isBTComposite(self, obj): """ -> bool Returns True if obj is a BTComposite object. """ if isinstance(obj, BTComposite): return True return False def clear(self): """ -> void Clears channel list, e.g. the attribute channels is set to []. """ self.channels = [] def delete(self, obj): """ -> void Deletes obj from the channel list if obj is in it. """ if obj in self.channels: del self.channels[self.index(obj)] def deleteIndex(self, index): """ -> void Deletes the object at position index from the channel list. """ if index >= 0 and index < self.len(): del self.channels[index] def moveUp(self, obj): """ -> void Moves obj one place up in the channel list if obj is in it. Cycles to index 0 if at the end of the list. """ if self.isBTChannel(obj) or self.isBTComposite(obj): if obj in self.channels: i = self.index(obj) if i == (self.len() - 1): self.channels = [obj] + self.channels[:-1] else: self.channels[i] = self.channels[i+1] self.channels[i+1] = obj def moveUpIndex(self, index): """ -> void As moveUp() but by index. Does nothing if index < 0 or too large. """ if index >= 0 and index < self.len(): self.moveUp(self.channels[index]) def moveDownIndex(self, index): """ -> void As moveDown but by index. Does nothing if index < 0 or too large. """ if index >= 0 and index < self.len(): self.moveDown(self.channels[index]) def moveDown(self, obj): """ -> void Moves obj one place down in the channel list if obj is in it. Cycles to the end of the list if at index 0. """ if self.isBTChannel(obj) or self.isBTComposite(obj): if obj in self.channels: i = self.index(obj) if i == 0: self.channels = self.channels[1:] + [obj] else: self.channels[i] = self.channels[i-1] self.channels[i-1] = obj def currentChannel(self): """ -> BTChannel or BTComposite Returns the BTChannel/BTComposite object currently tuned to. If the frequency or composite device currently tuned to is not in the channel list, it returns None. """ if btdriver.use_driver == "saa": try: v = saa.tuner_videosource() except NameError: pass else: v = bt848.tuner_videosource() if v == self.tuner: if btdriver.use_driver == "saa": try: f = saa.tuner_frequency() except NameError: pass else: f = bt848.tuner_frequency() for item in self.channels: if self.isBTChannel(item): if item.frequency == f: return item if v != -1: for item in self.channels: if self.isBTComposite(item): if item.id == COMPOSITE_NAMES[v]: return item return None def currentChannelIndex(self): """ -> int As currentChannel() but returns the list index, -1 if not in it. """ obj = self.currentChannel() if obj is None: return -1 return self.index(obj) def tuneTo(self, obj): """ -> void Tunes to the channel described by obj if obj is a BTChannel or BTComposite object and is in the list. If not it does nothing. """ if obj in self.channels: if self.isBTComposite(obj): if btdriver.use_driver == "saa": try: saa.tuner_videosource_set(COMPOSITE_NAMES.index(obj.id)) except NameError: pass else: bt848.tuner_videosource_set(COMPOSITE_NAMES.index(obj.id)) bt848.tuner_audiosource_set(1) else: if btdriver.use_driver == "saa": try: if saa.tuner_videosource() != self.tuner: saa.tuner_videosource_set(self.tuner) if saa.tuner_frequency_set(obj.frequency) < 0: saa.tuner_quit() time.sleep(0.1) saa.tuner_init() saa.tuner_if_init() time.sleep(0.1) except NameError: pass else: if bt848.tuner_videosource() != self.tuner: bt848.tuner_videosource_set(self.tuner) bt848.tuner_audiosource_set(0) bt848.tuner_frequency_set(obj.frequency) def tuneToIndex(self, index): """ -> void As tuneTo() but by index. Does nothing if index < 0 or too large. """ if index >= 0 and index < self.len(): self.tuneTo(self.channels[index]) def tuneUp(self): """ -> void Tune to the channel that is one higher in the channel list. If the current channel is the highest one it tunes to index 0. If the currently tuned is not in the list it tunes to index 0. """ index = self.currentChannelIndex() if index == -1 or index == (self.len() - 1): self.tuneToIndex(0) else: self.tuneToIndex(index + 1) def tuneDown(self): """ -> void Tune to the channel that is one lower in the channel list. If the current channel is the lowest it tunes to the highest. If the currently tuned is not in the list it tunes to index 0. """ index = self.currentChannelIndex() if index == -1: self.tuneToIndex(0) if index == 0: self.tuneToIndex(self.len() - 1) else: self.tuneToIndex(index - 1) def repr(self): """ -> void Returns an unicode string representation of the channel list. """ s = "" rjn = 2 # Right justify for number rjf = len("SVID") # Right justify for frequency, composite ID if self.len() > 100: rjn += 1 # Possible if self.len() > 1000: rjn += 1 # Hmmm.. i = 0; while i < self.len(): c = self.channels[i] s += str(i).rjust(rjn) + "\t" if self.isBTComposite(c): s += c.id.rjust(rjf) if self.isBTChannel(c): s += str(c.frequency).rjust(rjf) # name is unicode, makes the whole string unicode s += "\t" + c.name + "\n" i += 1 return s def writePickle(self, path): """ -> bool Attempts to write a cPickle of the BTChannelList object to path. Returns True on success, False if path is not a file or can't be written to, or if pickling fails. """ try: fd = open(path, "w") cPickle.dump(self, fd) fd.close() return True except OSError, PickleError: return False def writeText(self, path): """ -> bool Attempts to write the repr() text of the channel list to path. Returns True on success, False if path is not a file or can't be written to. """ try: fd = open(path, "w") fd.write(self.repr()) fd.close() return True except OSError: return False def readPickle(self, path): """ -> bool Attempts to read and unpickle the BTChannelList object stored in path. Returns True if successfully read, unpickled, and having set the channels attribute to its. Returns False if path is not a file or can't be read from, or if unpickling fails or the cPickle doesn't contain a BTChannelList object. """ try: fd = open(path, "r") res = cPickle.load(fd) fd.close() except OSError, PickleError: return False if isinstance(res, BTChannelList): self.channels = res.channels self.tuner = res.tuner return True return False ## END ################################################################### if __name__ == "__main__": print "This is a module. Import it instead."