class AutoBouquetsMaker_FrequencyFinder(Screen): skin = skin_downloadBar() def __init__(self, session, args=0): print "[ABM-FrequencyFinder][__init__] Starting..." print "[ABM-FrequencyFinder][__init__] args", args self.session = session Screen.__init__(self, session) Screen.setTitle(self, _("FrequencyFinder")) self.skinName = ["AutoBouquetsMaker"] self.frontend = None self.rawchannel = None self["background"] = Pixmap() self["action"] = Label(_("Starting scanner")) self["status"] = Label("") self["progress"] = ProgressBar() self["progress_text"] = Progress() self["Frontend"] = FrontendStatus( frontend_source=lambda: self.frontend, update_interval=100) self["actions"] = ActionMap(["SetupActions"], { "cancel": self.keyCancel, }, -2) self.selectedNIM = -1 # -1 is automatic selection self.uhf_vhf = "uhf" self.networkid = 0 # this is an onid, not a regional network id self.restrict_to_networkid = False if args: # These can be added in ABM config at some time in the future if "feid" in args: self.selectedNIM = args["feid"] if "uhf_vhf" in args: self.uhf_vhf = args["uhf_vhf"] if "networkid" in args: self.networkid = args["networkid"] if "restrict_to_networkid" in args: self.restrict_to_networkid = args["restrict_to_networkid"] self.isT2tuner = False # unlikely any modern internal terrestrial tuner can't play T2, but some USB tuners can't self.session.postScanService = None self.index = 0 self.frequency = 0 self.system = eDVBFrontendParametersTerrestrial.System_DVB_T self.lockTimeout = 50 # 100ms for tick - 5 sec self.snrTimeout = 100 # 100ms for tick - 10 sec #self.bandwidth = 8 # MHz self.scanTransponders = [] if self.uhf_vhf == "uhf_vhf": bandwidth = 7 for a in range(5, 13): # channel for b in (eDVBFrontendParametersTerrestrial.System_DVB_T, eDVBFrontendParametersTerrestrial.System_DVB_T2 ): # system self.scanTransponders.append({ "frequency": channel2freq(a, bandwidth), "system": b, "bandwidth": bandwidth }) if self.uhf_vhf in ("uhf", "uhf_vhf"): bandwidth = 8 for a in range(21, 70): # channel for b in (eDVBFrontendParametersTerrestrial.System_DVB_T, eDVBFrontendParametersTerrestrial.System_DVB_T2 ): # system self.scanTransponders.append({ "frequency": channel2freq(a, bandwidth), "system": b, "bandwidth": bandwidth }) self.transponders_found = [] self.transponders_unique = {} # self.custom_dir = os.path.dirname(__file__) + "/../custom" # self.customfile = self.custom_dir + "/CustomTranspondersOverride.xml" # self.removeFileIfExists(self.customfile) self.providers_dir = os.path.dirname(__file__) + "/../providers" self.providersfile = self.providers_dir + "/terrestrial_finder.xml" self.network_name = None self.onClose.append(self.__onClose) self.onFirstExecBegin.append(self.firstExec) def showError(self, message): question = self.session.open(MessageBox, message, MessageBox.TYPE_ERROR) question.setTitle(_("ABM frequency finder")) self.close() def showAdvice(self, message): question = self.session.open(MessageBox, message, MessageBox.TYPE_INFO) question.setTitle(_("ABM frequency finder")) self.close() def keyCancel(self): self.close() def firstExec(self): png = resolveFilename(SCOPE_CURRENT_SKIN, "FrequencyFinder/background.png") if not png or not fileExists(png): png = "%s/images/background.png" % os.path.dirname( sys.modules[__name__].__file__) self["background"].instance.setPixmapFromFile(png) if len(self.scanTransponders) > 0: self["action"].setText(_('Starting search...')) self["status"].setText(_("Scanning for active transponders")) self.progresscount = len(self.scanTransponders) self.progresscurrent = 1 self["progress_text"].range = self.progresscount self["progress_text"].value = self.progresscurrent self["progress"].setRange((0, self.progresscount)) self["progress"].setValue(self.progresscurrent) self.timer = eTimer() self.timer.callback.append(self.search) self.timer.start(100, 1) else: self.showError(_('No frequencies to search')) def search(self): if self.index < len(self.scanTransponders): self.system = self.scanTransponders[self.index]["system"] self.bandwidth = self.scanTransponders[self.index]["bandwidth"] self.frequency = self.scanTransponders[self.index]["frequency"] print "[ABM-FrequencyFinder][Search] Scan frequency %d (ch %s)" % ( self.frequency, getChannelNumber(self.frequency)) print "[ABM-FrequencyFinder][Search] Scan system %d" % self.system print "[ABM-FrequencyFinder][Search] Scan bandwidth %d" % self.bandwidth self.progresscurrent = self.index self["progress_text"].value = self.progresscurrent self["progress"].setValue(self.progresscurrent) self["action"].setText( _("Tuning %s MHz (ch %s)") % (str(self.frequency / 1000000), getChannelNumber( self.frequency))) self["status"].setText( ngettext("Found %d unique transponder", "Found %d unique transponders", len(self.transponders_unique)) % len(self.transponders_unique)) self.index += 1 if self.frequency in self.transponders_found or self.system == eDVBFrontendParametersTerrestrial.System_DVB_T2 and self.isT2tuner == False: print "[ABM-FrequencyFinder][Search] Skipping T2 search of %s MHz (ch %s)" % ( str(self.frequency / 1000000), getChannelNumber(self.frequency)) self.search() return self.searchtimer = eTimer() self.searchtimer.callback.append(self.getFrontend) self.searchtimer.start(100, 1) else: if len({ k: v for k, v in self.transponders_unique.items() if v["system"] == eDVBFrontendParametersTerrestrial.System_DVB_T }) > 0: # check DVB-T transponders exist if self.frontend: self.frontend = None del (self.rawchannel) self["action"].setText(_("Saving data")) if self.session.postScanService: self.session.nav.playService(self.session.postScanService) self.session.postScanService = None # self.saveTransponderList() # message = "Transponder frequencies updated.\nDo you want to continue with a scan for services." # question = self.session.openWithCallback(self.scanMessageCallback, MessageBox, message, type=MessageBox.TYPE_YESNO, default=True) # question.setTitle(_("ABM frequency finder")) self.saveProviderFile() message = 'New provider created called "%s terrestrial".\n Disable the existing ABM terrestrial provider and perform an ABM scan with the new one.' % self.strongestTransponder[ "network_name"] self.showAdvice(message) elif len(self.transponders_unique) > 0: print "[ABM-FrequencyFinder][Search] Only DVB-T2 multiplexes found. Insufficient data to create a provider file." self.showError( _('Only DVB-T2 multiplexes found. Insufficient data to create a provider file.' )) else: print "[ABM-FrequencyFinder][Search] No terrestrial multiplexes found." self.showError(_('No terrestrial multiplexes found.')) def config_mode(self, nim): # Workaround for OpenATV > 5.3 try: return nim.config_mode except AttributeError: return nim.isCompatible( "DVB-T") and nim.config_mode_dvbt or "nothing" def getFrontend(self): print "[ABM-FrequencyFinder][getFrontend] searching for available tuner" nimList = [] if self.selectedNIM < 0: # automatic tuner selection for nim in nimmanager.nim_slots: if self.config_mode(nim) not in ( "nothing", ) and (nim.isCompatible("DVB-T2") or (nim.isCompatible("DVB-S") and nim.canBeCompatible("DVB-T2"))): nimList.append(nim.slot) self.isT2tuner = True if len(nimList) == 0: print "[ABM-FrequencyFinder][getFrontend] No T2 tuner found" for nim in nimmanager.nim_slots: if self.config_mode(nim) not in ("nothing", ) and ( nim.isCompatible("DVB-T") or (nim.isCompatible("DVB-S") and nim.canBeCompatible("DVB-T"))): nimList.append(nim.slot) if len(nimList) == 0: print "[ABM-FrequencyFinder][getFrontend] No terrestrial tuner found." self.showError(_('No terrestrial tuner found.')) return else: # manual tuner selection, and subsequent iterations nim = nimmanager.nim_slots[self.selectedNIM] if self.config_mode(nim) not in ("nothing", ) and ( nim.isCompatible("DVB-T2") or (nim.isCompatible("DVB-S") and nim.canBeCompatible("DVB-T2"))): nimList.append(nim.slot) self.isT2tuner = True if len(nimList) == 0: print "[ABM-FrequencyFinder][getFrontend] User selected tuner is not T2 compatible" if self.config_mode(nim) not in ( "nothing", ) and (nim.isCompatible("DVB-T") or (nim.isCompatible("DVB-S") and nim.canBeCompatible("DVB-T"))): nimList.append(nim.slot) if len(nimList) == 0: print "[ABM-FrequencyFinder][getFrontend] User selected tuner not configured" self.showError(_('Selected tuner is not configured.')) return if len(nimList) == 0: print "[ABM-FrequencyFinder][getFrontend] No terrestrial tuner found." self.showError(_('No terrestrial tuner found.')) return resmanager = eDVBResourceManager.getInstance() if not resmanager: print "[ABM-FrequencyFinder][getFrontend] Cannot retrieve Resource Manager instance" self.showError(_('Cannot retrieve Resource Manager instance')) return if self.selectedNIM < 0: # automatic tuner selection print "[ABM-FrequencyFinder][getFrontend] Choosing NIM" # stop pip if running if self.session.pipshown: self.session.pipshown = False del self.session.pip print "[ABM-FrequencyFinder][getFrontend] Stopping PIP." # Find currently playing NIM currentlyPlayingNIM = None currentService = self.session and self.session.nav.getCurrentService() frontendInfo = currentService and currentService.frontendInfo() frontendData = frontendInfo and frontendInfo.getAll(True) if frontendData is not None: currentlyPlayingNIM = frontendData.get("tuner_number", None) del frontendInfo del currentService current_slotid = -1 if self.rawchannel: del (self.rawchannel) self.frontend = None self.rawchannel = None nimList.reverse() # start from the last for slotid in nimList: if current_slotid == -1: # mark the first valid slotid in case of no other one is free current_slotid = slotid self.rawchannel = resmanager.allocateRawChannel(slotid) if self.rawchannel: print "[ABM-FrequencyFinder][getFrontend] Nim found on slot id %d" % ( slotid) current_slotid = slotid break if current_slotid == -1: print "[ABM-FrequencyFinder][getFrontend] No valid NIM found" self.showError(_('No valid NIM found for terrestrial.')) return if not self.rawchannel: # if we are here the only possible option is to close the active service if currentlyPlayingNIM in nimList: slotid = currentlyPlayingNIM print "[ABM-FrequencyFinder][getFrontend] Nim found on slot id %d but it's busy. Stopping active service" % slotid self.session.postScanService = self.session.nav.getCurrentlyPlayingServiceReference( ) self.session.nav.stopService() self.rawchannel = resmanager.allocateRawChannel(slotid) if self.rawchannel: print "[ABM-FrequencyFinder][getFrontend] The active service was stopped, and the NIM is now free to use." current_slotid = slotid if not self.rawchannel: if self.session.nav.RecordTimer.isRecording(): print "[ABM-FrequencyFinder][getFrontend] Cannot free NIM because a recording is in progress" self.showError( _('Cannot free NIM because a recording is in progress') ) return else: print "[ABM-FrequencyFinder][getFrontend] Cannot get the NIM" self.showError(_('Cannot get the NIM')) return print "[ABM-FrequencyFinder][getFrontend] Will wait up to %i seconds for tuner lock." % ( self.lockTimeout / 10) self.selectedNIM = current_slotid # Remember for next iteration self.frontend = self.rawchannel.getFrontend() if not self.frontend: print "[ABM-FrequencyFinder][getFrontend] Cannot get frontend" self.showError(_('Cannot get frontend')) return self.rawchannel.requestTsidOnid() self.tsid = None self.onid = None self.demuxer_id = self.rawchannel.reserveDemux() if self.demuxer_id < 0: print >> log, "[ABM-FrequencyFinder][getFrontend] Cannot allocate the demuxer." self.showError(_('Cannot allocate the demuxer.')) return self.frontend.tune( setParamsFe(setParams(self.frequency, self.system, self.bandwidth))) self.lockcounter = 0 self.locktimer = eTimer() self.locktimer.callback.append(self.checkTunerLock) self.locktimer.start(100, 1) def checkTunerLock(self): self.dict = {} self.frontend.getFrontendStatus(self.dict) if self.dict["tuner_state"] == "TUNING": if self.lockcounter < 1: # only show this once in the log per retune event print "[ABM-FrequencyFinder][checkTunerLock] TUNING" elif self.dict["tuner_state"] == "LOCKED": print "[ABM-FrequencyFinder][checkTunerLock] LOCKED" self["action"].setText( _("Reading %s MHz (ch %s)") % (str(self.frequency / 1000000), getChannelNumber( self.frequency))) self.tsidOnidtimer = eTimer() self.tsidOnidtimer.callback.append(self.tsidOnidWait) self.tsidOnidtimer.start(100, 1) return elif self.dict["tuner_state"] in ("LOSTLOCK", "FAILED"): print "[ABM-FrequencyFinder][checkTunerLock] TUNING FAILED" self.search() return self.lockcounter += 1 if self.lockcounter > self.lockTimeout: print "[ABM-FrequencyFinder][checkTunerLock] Timeout for tuner lock" self.search() return self.locktimer.start(100, 1) def tsidOnidWait(self): self.getCurrentTsidOnid() if self.tsid is not None and self.onid is not None: print "[ABM-FrequencyFinder][tsidOnidWait] tsid & onid found", self.tsid, self.onid self.signalQualityWait() return print "[ABM-FrequencyFinder][tsidOnidWait] tsid & onid wait failed" self.search() return def signalQualityWait(self): self.readNIT() # by the time this is completed SNR should be stable signalQuality = self.frontend.readFrontendData( iFrontendInformation.signalQuality) if signalQuality > 0: found = { "frequency": self.frequency, "tsid": self.tsid, "onid": self.onid, "system": self.system, "bandwidth": self.bandwidth, "signalQuality": signalQuality, "network_name": self.network_name, "custom_transponder_needed": self.custom_transponder_needed } self.transponders_found.append(self.frequency) tsidOnidKey = "%x:%x" % (self.tsid, self.onid) if (tsidOnidKey not in self.transponders_unique or self.transponders_unique[tsidOnidKey]["signalQuality"] < signalQuality) and (not self.restrict_to_networkid or self.networkid == self.onid): self.transponders_unique[tsidOnidKey] = found print "[ABM-FrequencyFinder][signalQualityWait] transponder details", found self.search() return print "[ABM-FrequencyFinder][signalQualityWait] Failed to collect SNR" self.search() def getCurrentTsidOnid(self, from_retune=False): adapter = 0 demuxer_device = "/dev/dvb/adapter%d/demux%d" % (adapter, self.demuxer_id) start = time.time() # for debug info sdt_pid = 0x11 sdt_current_table_id = 0x42 mask = 0xff tsidOnidTimeout = 5 # maximum time allowed to read the service descriptor table (seconds) self.tsid = None self.onid = None fd = dvbreader.open(demuxer_device, sdt_pid, sdt_current_table_id, mask, self.selectedNIM) if fd < 0: print "[ABM-FrequencyFinder][getCurrentTsidOnid] Cannot open the demuxer" return None timeout = datetime.datetime.now() timeout += datetime.timedelta(0, tsidOnidTimeout) while True: if datetime.datetime.now() > timeout: print "[ABM-FrequencyFinder][getCurrentTsidOnid] Timed out" break section = dvbreader.read_sdt(fd, sdt_current_table_id, 0x00) if section is None: time.sleep(0.1) # no data.. so we wait a bit continue if section["header"]["table_id"] == sdt_current_table_id: self.tsid = section["header"]["transport_stream_id"] self.onid = section["header"]["original_network_id"] break print "[ABM-FrequencyFinder][getCurrentTsidOnid] Read time %.1f seconds." % ( time.time() - start) dvbreader.close(fd) def readNIT(self): adapter = 0 demuxer_device = "/dev/dvb/adapter%d/demux%d" % (adapter, self.demuxer_id) start = time.time() # for debug info nit_current_pid = 0x10 nit_current_table_id = 0x40 nit_other_table_id = 0x00 # don't read other table self.network_name = None self.custom_transponder_needed = True if nit_other_table_id == 0x00: mask = 0xff else: mask = nit_current_table_id ^ nit_other_table_id ^ 0xff nit_current_timeout = 20 # maximum time allowed to read the network information table (seconds) nit_current_version_number = -1 nit_current_sections_read = [] nit_current_sections_count = 0 nit_current_content = [] nit_current_completed = False fd = dvbreader.open(demuxer_device, nit_current_pid, nit_current_table_id, mask, self.selectedNIM) if fd < 0: print "[ABM-FrequencyFinder][readNIT] Cannot open the demuxer" return timeout = datetime.datetime.now() timeout += datetime.timedelta(0, nit_current_timeout) while True: if datetime.datetime.now() > timeout: print "[ABM-FrequencyFinder][readNIT] Timed out reading NIT" break section = dvbreader.read_nit(fd, nit_current_table_id, nit_other_table_id) if section is None: time.sleep(0.1) # no data.. so we wait a bit continue if section["header"][ "table_id"] == nit_current_table_id and not nit_current_completed: if section["header"][ "version_number"] != nit_current_version_number: nit_current_version_number = section["header"][ "version_number"] nit_current_sections_read = [] nit_current_sections_count = section["header"][ "last_section_number"] + 1 nit_current_content = [] if section["header"][ "section_number"] not in nit_current_sections_read: nit_current_sections_read.append( section["header"]["section_number"]) nit_current_content += section["content"] if 'network_name' in section["header"] and section[ "header"]["network_name"] != "Unknown": self.network_name = section["header"]["network_name"] if len(nit_current_sections_read ) == nit_current_sections_count: nit_current_completed = True if nit_current_completed: break dvbreader.close(fd) if not nit_current_content: print "[ABM-FrequencyFinder][readNIT] current transponder not found" return print "[ABM-FrequencyFinder][readNIT] NIT read time %.1f seconds." % ( time.time() - start) # descriptor_tag 0x5A is DVB-T, descriptor_tag 0x7f is DVB-T transponders = [ t for t in nit_current_content if "descriptor_tag" in t and t["descriptor_tag"] in ( 0x5A, 0x7f) and t["original_network_id"] == self.onid and t["transport_stream_id"] == self.tsid ] # this should only ever have a length of one transponder print "[ABM-FrequencyFinder][readNIT] transponders", transponders if transponders: if transponders[0]["descriptor_tag"] == 0x5A: # DVB-T self.system = eDVBFrontendParametersTerrestrial.System_DVB_T else: # must be DVB-T2 self.system = eDVBFrontendParametersTerrestrial.System_DVB_T2 if "frequency" in transponders[0] and abs( (transponders[0]["frequency"] * 10) - self.frequency) < 1000000: self.custom_transponder_needed = False if self.frequency != transponders[0]["frequency"] * 10: print "[ABM-FrequencyFinder][readNIT] updating transponder frequency from %.03f MHz to %.03f MHz" % ( self.frequency / 1000000, transponders[0]["frequency"] / 100000) self.frequency = transponders[0]["frequency"] * 10 # def saveTransponderList(self): # # make custom transponders file content # customTransponderList = [] # customTransponderList.append('<provider>\n') # customTransponderList.append('\t<customtransponders>\n') # for tsidOnidKey in self.iterateUniqueTranspondersByFrequency(): # transponder = self.transponders_unique[tsidOnidKey] # customTransponderList.append('\t\t<customtransponder key="_OVERRIDE_" frequency="%d" transport_stream_id="%04x" system="%d"/><!-- original_network_id="%04x" signalQuality="%05d" -->\n' % (transponder["frequency"], transponder["tsid"], transponder["system"], transponder["onid"], transponder["signalQuality"])) # customTransponderList.append('\t</customtransponders>\n') # customTransponderList.append('</provider>\n') # # # save to ABM custom folder # outFile = open(self.customfile, "w") # outFile.write(''.join(customTransponderList)) # outFile.close() # print "[ABM-FrequencyFinder][saveTransponderList] Custom transponders file saved." def saveProviderFile(self): customProviderList = [] self.strongestTransponder = self.transponders_unique[ self.iterateUniqueTranspondersBySignalQuality()[-1]] for tsidOnidKey in self.iterateUniqueTranspondersBySignalQuality( )[:: -1]: # iterate in reverse order and select the first system 0 transponder transponder = self.transponders_unique[tsidOnidKey] if transponder["system"] == 0: self.strongestTransponder = transponder break network_name = re.sub( r'&(?![A-Za-z]+[0-9]*;|#[0-9]+;|#x[0-9a-fA-F]+;)', r'&', self.strongestTransponder["network_name"] ) # regex to avoid unencoded ampersands that are not entities customProviderList.append('<provider>\n') customProviderList.append('\t<name>%s terrestrial</name>\n' % network_name) customProviderList.append('\t<streamtype>dvbt</streamtype>\n') customProviderList.append('\t<protocol>lcn</protocol>\n') customProviderList.append('\t<dvbtconfigs>\n') customProviderList.append( '\t\t<configuration key="custom" frequency="%d" system="%d">%s terrestrial</configuration>\n' % (self.strongestTransponder["frequency"], self.strongestTransponder["system"], network_name)) customProviderList.append('\t</dvbtconfigs>\n\n') customProviderList.append('\t<customtransponders>\n') for tsidOnidKey in self.iterateUniqueTranspondersByFrequency(): transponder = self.transponders_unique[tsidOnidKey] if transponder["custom_transponder_needed"]: customProviderList.append( '\t\t<customtransponder key="custom" frequency="%d" transport_stream_id="%04x" system="%d"/><!-- original_network_id="%04x" signalQuality="%05d" channel="%s" -->\n' % (transponder["frequency"], transponder["tsid"], transponder["system"], transponder["onid"], transponder["signalQuality"], getChannelNumber(transponder["frequency"]))) else: customProviderList.append( '\t\t<!-- customtransponder key="custom" frequency="%d" transport_stream_id="%04x" system="%d"/ --><!-- original_network_id="%04x" signalQuality="%05d" channel="%s" -->\n' % (transponder["frequency"], transponder["tsid"], transponder["system"], transponder["onid"], transponder["signalQuality"], getChannelNumber(transponder["frequency"]))) customProviderList.append('\t</customtransponders>\n\n') customProviderList.append('\t<sections>\n') customProviderList.append( '\t\t<section number="1">Entertainment</section>\n') customProviderList.append( '\t\t<section number="100">High Definition</section>\n') customProviderList.append( '\t\t<section number="201">Children</section>\n') customProviderList.append('\t\t<section number="230">News</section>\n') customProviderList.append( '\t\t<section number="260">BBC Interactive</section>\n') customProviderList.append( '\t\t<section number="670">Adult</section>\n') customProviderList.append('\t</sections>\n\n') customProviderList.append('\t<swapchannels>\n') customProviderList.append( '\t\t<channel number="1" with="101"/> <!-- BBC One HD -->\n') customProviderList.append( '\t\t<channel number="2" with="102"/> <!-- BBC TWO HD -->\n') customProviderList.append( '\t\t<channel number="3" with="103"/> <!-- ITV HD -->\n') customProviderList.append( '\t\t<channel number="4" with="104"/> <!-- Channel 4 HD -->\n') customProviderList.append( '\t\t<channel number="5" with="105"/> <!-- Channel 5 HD -->\n') customProviderList.append( '\t\t<channel number="9" with="106"/> <!-- BBC FOUR HD -->\n') customProviderList.append( '\t\t<channel number="13" with="109"/> <!-- Channel 4+1 HD -->\n') customProviderList.append( '\t\t<channel number="47" with="110"/> <!-- 4seven HD -->\n') customProviderList.append( '\t\t<channel number="201" with="204"/> <!-- CBBC HD -->\n') customProviderList.append( '\t\t<channel number="202" with="205"/> <!-- CBeebies HD -->\n') customProviderList.append( '\t\t<channel number="231" with="107"/> <!-- BBC NEWS HD -->\n') customProviderList.append('\t</swapchannels>\n\n') customProviderList.append('\t<servicehacks>\n') customProviderList.append('\t<![CDATA[\n\n') customProviderList.append('tsidonidlist= [\n') for tsidOnidKey in self.iterateUniqueTranspondersByFrequency(): customProviderList.append('\t"%s",\n' % tsidOnidKey) customProviderList.append(']\n\n') customProviderList.append( 'tsidonidkey = "%x:%x" % (service["transport_stream_id"], service["original_network_id"])\n' ) customProviderList.append('if tsidonidkey not in tsidonidlist:\n') customProviderList.append('\tskip = True\n\n') customProviderList.append('\t]]>\n') customProviderList.append('\t</servicehacks>\n') customProviderList.append('</provider>\n') # save to ABM providers folder outFile = open(self.providersfile, "w") outFile.write(''.join(customProviderList)) outFile.close() print "[ABM-FrequencyFinder][saveProviderFile] Provider file saved." def iterateUniqueTranspondersByFrequency(self): # returns an iterator list for self.transponders_unique in frequency order ascending sort_list = [(x[0], x[1]["frequency"]) for x in self.transponders_unique.items()] return [ x[0] for x in sorted(sort_list, key=lambda listItem: listItem[1]) ] def iterateUniqueTranspondersBySignalQuality(self): # returns an iterator list for self.transponders_unique in SignalQuality order ascending sort_list = [(x[0], x[1]["signalQuality"]) for x in self.transponders_unique.items()] return [ x[0] for x in sorted(sort_list, key=lambda listItem: listItem[1]) ] # def scanMessageCallback(self, answer): # if answer: # self.session.open(AutoBouquetsMaker) # self.close() # def removeFileIfExists(self, filename): # try: # os.remove(filename) # except OSError as e: # if e.errno != errno.ENOENT: # errno.ENOENT = no such file or directory # raise # re-raise exception if a different error occurred def __onClose(self): if self.frontend: self.frontend = None del (self.rawchannel) if self.session.postScanService: self.session.nav.playService(self.session.postScanService) self.session.postScanService = None
class AutoBouquetsMaker(Screen): skin = skin_downloadBar() LOCK_TIMEOUT_FIXED = 100 # 100ms for tick - 10 sec LOCK_TIMEOUT_ROTOR = 1200 # 100ms for tick - 120 sec ABM_BOUQUET_PREFIX = "userbouquet.abm." def __init__(self, session, args=0): self.printconfig() self.session = session Screen.__init__(self, session) Screen.setTitle(self, _("AutoBouquetsMaker")) self.frontend = None self.rawchannel = None self.postScanService = None self.providers = Manager().getProviders() self["actions"] = ActionMap(["OkCancelActions", "ColorActions"], { "cancel": self.keyCancel, "red": self.keyCancel, }, -2) # self["background"] = Pixmap() self["action"] = Label(_("Starting scanner")) self["status"] = Label("") self["progress"] = ProgressBar() self["progress_text"] = Progress() self["tuner_text"] = Label("") self["Frontend"] = FrontendStatus( frontend_source=lambda: self.frontend, update_interval=100) # dependent providers self.dependents = {} for provider_key in self.providers: if len(self.providers[provider_key] ["dependent"]) > 0 and self.providers[provider_key][ "dependent"] in self.providers: if self.providers[provider_key][ "dependent"] not in self.dependents: self.dependents[self.providers[provider_key] ["dependent"]] = [] self.dependents[self.providers[provider_key] ["dependent"]].append(provider_key) # get ABM config string including dependents self.abm_settings_str = self.getABMsettings() self.actionsList = [] self.onFirstExecBegin.append(self.firstExec) def firstExec(self, postScanService=None): from Screens.Standby import inStandby # if not inStandby: # png = resolveFilename(SCOPE_CURRENT_SKIN, "autobouquetsmaker/background.png") # if not png or not fileExists(png): # png = "%s/../images/background.png" % os.path.dirname(sys.modules[__name__].__file__) # self["background"].instance.setPixmapFromFile(png) if len(self.abm_settings_str) > 0: if not inStandby: self["action"].setText(_('Loading bouquets...')) self["status"].setText(_("Services: 0 video - 0 radio")) self.timer = eTimer() self.timer.callback.append(self.go) self.timer.start(100, 1) else: self.showError(_('Please first setup, in configuration')) def showError(self, message): from Screens.Standby import inStandby self.releaseFrontend() self.restartService() if not inStandby: question = self.session.open(MessageBox, message, MessageBox.TYPE_ERROR) question.setTitle(_("AutoBouquetsMaker")) self.close() def keyCancel(self): self.releaseFrontend() self.restartService() self.close() def go(self): from Screens.Standby import inStandby self.manager = Manager() self.manager.setPath("/etc/enigma2") self.manager.setAddPrefix(config.autobouquetsmaker.addprefix.value) self.selectedProviders = {} self.actionsList = [] providers_tmp = self.abm_settings_str.split("|") for provider_tmp in providers_tmp: provider_config = ProviderConfig(provider_tmp) if provider_config.isValid() and Providers().providerFileExists( provider_config.getProvider()): self.actionsList.append(provider_config.getProvider()) self.selectedProviders[ provider_config.getProvider()] = provider_config if config.autobouquetsmaker.keepallbouquets.getValue(): bouquets = Manager().getBouquetsList() bouquets_tv = [] bouquets_radio = [] for bouquet in bouquets["tv"]: if bouquet["filename"][:len(self.ABM_BOUQUET_PREFIX )] == self.ABM_BOUQUET_PREFIX: continue if len(bouquet["filename"]) > 0: bouquets_tv.append(bouquet["filename"]) for bouquet in bouquets["radio"]: if bouquet["filename"][:len(self.ABM_BOUQUET_PREFIX )] == self.ABM_BOUQUET_PREFIX: continue if len(bouquet["filename"]) > 0: bouquets_radio.append(bouquet["filename"]) self.manager.setBouquetsToKeep(bouquets_tv, bouquets_radio) else: bouquets = config.autobouquetsmaker.keepbouquets.value.split("|") bouquets_tv = [] bouquets_radio = [] for bouquet in bouquets: if bouquet.endswith(".tv"): bouquets_tv.append(bouquet) elif bouquet.endswith(".radio"): bouquets_radio.append(bouquet) self.manager.setBouquetsToKeep(bouquets_tv, bouquets_radio) bouquetsToHide = {} bouquets = config.autobouquetsmaker.hidesections.value.split("|") for bouquet in bouquets: tmp = bouquet.split(":") if len(tmp) != 2: continue if tmp[0].strip() not in bouquetsToHide: bouquetsToHide[tmp[0].strip()] = [] bouquetsToHide[tmp[0].strip()].append(int(tmp[1].strip())) self.manager.setBouquetsToHide(bouquetsToHide) self.manager.load() self.progresscount = (len(self.actionsList) * 2) + 3 self.progresscurrent = 1 if not inStandby: self["progress_text"].range = self.progresscount self["progress_text"].value = self.progresscurrent self["progress"].setRange((0, self.progresscount)) self["progress"].setValue(self.progresscurrent) self.timer = eTimer() self.timer.callback.append(self.doActions) self.timer.start(100, 1) def doActions(self): from Screens.Standby import inStandby if len(self.actionsList) == 0: self.progresscurrent += 1 self["actions"].setEnabled( False ) # disable action map here so we can't abort half way through writing result to settings files if not inStandby: self["progress_text"].value = self.progresscurrent self["progress"].setValue(self.progresscurrent) self["action"].setText(_('Bouquets generation...')) self["status"].setText( _("Services: %d video - %d radio") % (self.manager.getServiceVideoRead(), self.manager.getServiceAudioRead())) self["tuner_text"].setText("") self.timer = eTimer() self.timer.callback.append(self.doBuildIndex) self.timer.start(100, 1) return self.currentAction = self.actionsList[0] del (self.actionsList[0]) self.progresscurrent += 1 if not inStandby: self["progress_text"].value = self.progresscurrent self["progress"].setValue(self.progresscurrent) self["action"].setText( _("Tuning %s...") % str(self.providers[self.currentAction]["name"])) self["status"].setText( _("Services: %d video - %d radio") % (self.manager.getServiceVideoRead(), self.manager.getServiceAudioRead())) self["tuner_text"].setText("") self.timer = eTimer() self.timer.callback.append(self.doTune) self.timer.start(100, 1) def doTune(self): print("[ABM-main][doTune] searching for tuner for %s" % self.providers[self.currentAction]["name"], file=log) from Screens.Standby import inStandby if self.providers[self.currentAction]["streamtype"] == "dvbs": transponder = self.providers[self.currentAction]["transponder"] else: bouquet_key = None providers_tmp = self.abm_settings_str.split("|") for provider_tmp in providers_tmp: provider_config = ProviderConfig(provider_tmp) provider_key = provider_config.getProvider() if self.currentAction != provider_key: continue bouquet_key = provider_config.getArea() if not bouquet_key: print("[ABM-main][doTune] No area found", file=log) self.showError(_('No area found')) return transponder = self.providers[ self.currentAction]["bouquets"][bouquet_key] self.transponder = transponder nimList = [] tunerSelectionAlgorithm = "UNKNOWN" # for debug for nim in nimmanager.nim_slots: if self.providers[self.currentAction][ "streamtype"] == "dvbs" and nim.isCompatible("DVB-S"): try: if nim.isFBCLink(): continue # do not load FBC links, only root tuners except: pass try: # OpenPLi Hot Switch compatible image if (nim.config_mode not in ("loopthrough", "satposdepends", "nothing")) and \ {"dvbs": "DVB-S", "dvbc": "DVB-C", "dvbt": "DVB-T"}.get(self.providers[self.currentAction]["streamtype"], "UNKNOWN") in [x[:5] for x in nim.getTunerTypesEnabled()]: if self.validNIM(nim.slot): nimList.append(nim.slot) tunerSelectionAlgorithm = "OpenPLi Hot Switch compatible" except AttributeError: try: if (nim.config_mode not in ("loopthrough", "satposdepends", "nothing")) and \ ((self.providers[self.currentAction]["streamtype"] == "dvbs" and nim.isCompatible("DVB-S")) or (self.providers[self.currentAction]["streamtype"] == "dvbc" and (nim.isCompatible("DVB-C") or (nim.isCompatible("DVB-S") and nim.canBeCompatible("DVB-C")))) or (self.providers[self.currentAction]["streamtype"] == "dvbt" and (nim.isCompatible("DVB-T") or (nim.isCompatible("DVB-S") and nim.canBeCompatible("DVB-T"))))): if self.validNIM(nim.slot): nimList.append(nim.slot) tunerSelectionAlgorithm = "Conventional" except AttributeError: # OpenATV > 5.3 if (self.providers[self.currentAction]["streamtype"] == "dvbs" and nim.canBeCompatible("DVB-S") and nim.config_mode_dvbs not in ("loopthrough", "satposdepends", "nothing")) or \ (self.providers[self.currentAction]["streamtype"] == "dvbc" and nim.canBeCompatible("DVB-C") and nim.config_mode_dvbc != "nothing") or \ (self.providers[self.currentAction]["streamtype"] == "dvbt" and nim.canBeCompatible("DVB-T") and nim.config_mode_dvbt != "nothing"): if self.validNIM(nim.slot): nimList.append(nim.slot) tunerSelectionAlgorithm = "OpenATV > 5.3" print("[ABM-main][doTune] tuner selection algorithm '%s'" % tunerSelectionAlgorithm, file=log) if len(nimList) == 0: print("[ABM-main][doTune] No NIMs found", file=log) self.showError( _('No NIMs found for ') + self.providers[self.currentAction]["name"]) return resmanager = eDVBResourceManager.getInstance() if not resmanager: print( "[ABM-main][doTune] Cannot retrieve Resource Manager instance", file=log) self.showError(_('Cannot retrieve Resource Manager instance')) return if self.providers[self.currentAction][ "streamtype"] == "dvbs": # If we have a choice of dishes sort the nimList so "fixed" dishes have a higher priority than "motorised". nimList = [ slot for slot in nimList if not self.isRotorSat(slot, transponder["orbital_position"]) ] + [ slot for slot in nimList if self.isRotorSat(slot, transponder["orbital_position"]) ] # stop pip if running if self.session.pipshown: self.session.pipshown = False del self.session.pip print("[ABM-main][doTune] Stopping PIP.", file=log) # find currently playing nim currentlyPlayingNIM = None currentService = self.session and self.session.nav.getCurrentService() frontendInfo = currentService and currentService.frontendInfo() frontendData = frontendInfo and frontendInfo.getAll(True) if frontendData is not None: currentlyPlayingNIM = frontendData.get("tuner_number", None) # stop currently playing service if it is using a tuner in ("loopthrough", "satposdepends"), as running in this configuration will prevent getting rawchannel on the root tuner. if self.providers[self.currentAction][ "streamtype"] == "dvbs" and currentlyPlayingNIM is not None and nimmanager.nim_slots[ currentlyPlayingNIM].isCompatible("DVB-S"): try: nimConfigMode = nimmanager.nim_slots[ currentlyPlayingNIM].config_mode except AttributeError: # OpenATV > 5.3 nimConfigMode = nimmanager.nim_slots[ currentlyPlayingNIM].config_mode_dvbs if nimConfigMode in ("loopthrough", "satposdepends"): self.postScanService = self.session.nav.getCurrentlyPlayingServiceReference( ) self.session.nav.stopService() currentlyPlayingNIM = None print( "[ABM-main][doTune] The active service was using a %s tuner, so had to be stopped (slot id %s)." % (nimConfigMode, currentlyPlayingNIM), file=log) del frontendInfo del currentService self.releaseFrontend() for current_slotid in nimList: self.rawchannel = resmanager.allocateRawChannel(current_slotid) if self.rawchannel: print("[ABM-main][doTune] Tuner %s selected%s" % (chr(ord('A') + current_slotid), (" for orbital position %d" % transponder["orbital_position"] if "orbital_position" in transponder else "")), file=log) break if not self.rawchannel: # if we are here the only possible option is to close the active service if currentlyPlayingNIM in nimList: print( "[ABM-main][doTune] Tuner %s has been selected but it's busy. Stopping currently playing service." % chr(ord('A') + currentlyPlayingNIM), file=log) self.postScanService = self.session.nav.getCurrentlyPlayingServiceReference( ) self.session.nav.stopService() self.rawchannel = resmanager.allocateRawChannel( currentlyPlayingNIM) if self.rawchannel: print( "[ABM-main][doTune] The active service was stopped, and tuner %s is now free to use." % chr(ord('A') + currentlyPlayingNIM), file=log) current_slotid = currentlyPlayingNIM if not self.rawchannel: if self.session.nav.RecordTimer.isRecording(): print( "[ABM-main][doTune] Cannot free NIM because a recording is in progress", file=log) self.showError( _('Cannot free NIM because a recording is in progress') ) return else: print("[ABM-main][doTune] Cannot get the NIM", file=log) self.showError(_('Cannot get the NIM')) return # set extended timeout for rotors self.motorised = False if self.providers[self.currentAction][ "streamtype"] == "dvbs" and self.isRotorSat( current_slotid, transponder["orbital_position"]): self.motorised = True self.LOCK_TIMEOUT = self.LOCK_TIMEOUT_ROTOR print( "[ABM-main][doTune] Motorised dish. Will wait up to %i seconds for tuner lock." % (self.LOCK_TIMEOUT // 10), file=log) else: self.LOCK_TIMEOUT = self.LOCK_TIMEOUT_FIXED print( "[ABM-main][doTune] Fixed dish. Will wait up to %i seconds for tuner lock." % (self.LOCK_TIMEOUT // 10), file=log) if not inStandby: self["tuner_text"].setText(chr(ord('A') + current_slotid)) self.frontend = self.rawchannel.getFrontend() if not self.frontend: print("[ABM-main][doTune] Cannot get frontend", file=log) self.showError(_('Cannot get frontend')) return demuxer_id = self.rawchannel.reserveDemux() if demuxer_id < 0: print("[ABM-main][doTune] Cannot allocate the demuxer.", file=log) self.showError(_('Cannot allocate the demuxer.')) return if self.providers[self.currentAction]["streamtype"] == "dvbs": params = eDVBFrontendParametersSatellite() params.frequency = transponder["frequency"] params.symbol_rate = transponder["symbol_rate"] params.polarisation = transponder["polarization"] params.fec = transponder["fec_inner"] params.inversion = transponder["inversion"] params.orbital_position = transponder["orbital_position"] params.system = transponder["system"] params.modulation = transponder["modulation"] params.rolloff = transponder["roll_off"] params.pilot = transponder["pilot"] if hasattr(eDVBFrontendParametersSatellite, "No_Stream_Id_Filter"): params.is_id = eDVBFrontendParametersSatellite.No_Stream_Id_Filter if hasattr(eDVBFrontendParametersSatellite, "PLS_Gold"): params.pls_mode = eDVBFrontendParametersSatellite.PLS_Gold if hasattr(eDVBFrontendParametersSatellite, "PLS_Default_Gold_Code"): params.pls_code = eDVBFrontendParametersSatellite.PLS_Default_Gold_Code if hasattr(eDVBFrontendParametersSatellite, "No_T2MI_PLP_Id"): params.t2mi_plp_id = eDVBFrontendParametersSatellite.No_T2MI_PLP_Id if hasattr(eDVBFrontendParametersSatellite, "T2MI_Default_Pid"): params.t2mi_pid = eDVBFrontendParametersSatellite.T2MI_Default_Pid params_fe = eDVBFrontendParameters() params_fe.setDVBS(params, False) elif self.providers[self.currentAction]["streamtype"] == "dvbt": params = eDVBFrontendParametersTerrestrial() params.frequency = transponder["frequency"] params.bandwidth = transponder["bandwidth"] params.code_rate_hp = transponder["code_rate_hp"] params.code_rate_lp = transponder["code_rate_lp"] params.inversion = transponder["inversion"] params.system = transponder["system"] params.modulation = transponder["modulation"] params.transmission_mode = transponder["transmission_mode"] params.guard_interval = transponder["guard_interval"] params.hierarchy = transponder["hierarchy"] params_fe = eDVBFrontendParameters() params_fe.setDVBT(params) elif self.providers[self.currentAction]["streamtype"] == "dvbc": params = eDVBFrontendParametersCable() params.frequency = transponder["frequency"] params.symbol_rate = transponder["symbol_rate"] params.fec_inner = transponder["fec_inner"] params.inversion = transponder["inversion"] params.modulation = transponder["modulation"] params_fe = eDVBFrontendParameters() params_fe.setDVBC(params) try: self.rawchannel.requestTsidOnid() except TypeError: # for compatibility with some third party images self.rawchannel.requestTsidOnid(self.gotTsidOnid) self.frontend.tune(params_fe) self.manager.setAdapter(0) # FIX: use the correct device self.manager.setDemuxer(demuxer_id) self.manager.setFrontend(current_slotid) self.current_slotid = current_slotid self.lockcounter = 0 self.locktimer = eTimer() self.locktimer.callback.append(self.checkTunerLock) self.locktimer.start(100, 1) def checkTunerLock(self): from Screens.Standby import inStandby fe_status_dict = {} self.frontend and self.frontend.getFrontendStatus(fe_status_dict) tuner_state = fe_status_dict.get("tuner_state", "UNKNOWN") if tuner_state == "TUNING": print("[ABM-main][checkTunerLock] TUNING", file=log) elif tuner_state == "LOCKED": print("[ABM-main][checkTunerLock] ACQUIRING TSID/ONID", file=log) self.progresscurrent += 1 if not inStandby: self["progress_text"].value = self.progresscurrent self["progress"].setValue(self.progresscurrent) self["action"].setText( _("Reading %s...") % str(self.providers[self.currentAction]["name"])) self["status"].setText( _("Services: %d video - %d radio") % (self.manager.getServiceVideoRead(), self.manager.getServiceAudioRead())) self.timer = eTimer() self.timer.callback.append(self.doScan) self.timer.start(100, 1) return elif tuner_state == "LOSTLOCK": print("[ABM-main][checkTunerLock] LOSTLOCK", file=log) elif tuner_state == "FAILED": print("[ABM-main][checkTunerLock] TUNING FAILED FATAL", file=log) self.showError( _('Tuning failed!\n\nProvider: %s\nTuner: %s\nFrequency: %d MHz\n\nPlease check affected tuner for:\n\nTuner configuration errors,\nSignal cabling issues,\nAny other reception issues.' ) % (str(self.providers[self.currentAction]["name"]), chr(ord('A') + self.current_slotid), self.transponder["frequency"] // 1000)) return self.lockcounter += 1 if self.lockcounter > self.LOCK_TIMEOUT: print("[AutoBouquetsMaker] Timeout for tuner lock, ", file=log) self.showError( _('Tuning lock timed out!\n\nProvider: %s\nTuner: %s\nFrequency: %d MHz\n\nPlease check affected tuner for:\n\nTuner configuration errors,\nSignal cabling issues,\nAny other reception issues.' ) % (str(self.providers[self.currentAction]["name"]), chr(ord('A') + self.current_slotid), self.transponder["frequency"] // 1000)) return self.locktimer.start(100, 1) # for compatibility with some third party images def gotTsidOnid(self, tsid, onid): print("[ABM-main][gotTsidOnid] tsid, onid:", tsid, onid, file=log) INTERNAL_PID_STATUS_NOOP = 0 INTERNAL_PID_STATUS_WAITING = 1 INTERNAL_PID_STATUS_SUCCESSFUL = 2 INTERNAL_PID_STATUS_FAILED = 3 if tsid is not None and onid is not None: self.pidStatus = INTERNAL_PID_STATUS_SUCCESSFUL self.tsid = tsid self.onid = onid else: self.pidStatus = INTERNAL_PID_STATUS_FAILED self.tsid = -1 self.onid = -1 self.timer.start(100, True) def doScan(self): if not self.manager.read(self.selectedProviders[self.currentAction], self.providers, self.motorised): print("[ABM-main][doScan] Cannot read data", file=log) self.showError(_('Cannot read data')) return self.doActions() def doBuildIndex(self): self.manager.save(self.providers, self.dependents) self.scanComplete() def scanComplete(self): from Screens.Standby import inStandby self.releaseFrontend() self.restartService() eDVBDB.getInstance().reloadServicelist() eDVBDB.getInstance().reloadBouquets() self.progresscurrent += 1 if not inStandby: self["progress_text"].value = self.progresscurrent self["progress"].setValue(self.progresscurrent) self["action"].setText(_('Done')) self["status"].setText( _("Services: %d video - %d radio") % (self.manager.getServiceVideoRead(), self.manager.getServiceAudioRead())) self.timer = eTimer() self.timer.callback.append(self.close) self.timer.start(2000, 1) def releaseFrontend(self): if hasattr(self, 'frontend'): del self.frontend if hasattr(self, 'rawchannel'): del self.rawchannel self.frontend = None self.rawchannel = None def restartService(self): if self.postScanService: self.session.nav.playService(self.postScanService) self.postScanService = None def isRotorSat(self, slot, orb_pos): rotorSatsForNim = nimmanager.getRotorSatListForNim(slot) if len(rotorSatsForNim) > 0: for sat in rotorSatsForNim: if sat[0] == orb_pos: return True return False def validNIM(self, slot): return self.providers[ self.currentAction]["streamtype"] != "dvbs" or self.providers[ self.currentAction]["transponder"]["orbital_position"] in [ sat[0] for sat in nimmanager.getSatListForNim(slot) ] def printconfig(self): print("[ABM-config] level: ", config.autobouquetsmaker.level.value, file=log) print("[ABM-config] providers: ", config.autobouquetsmaker.providers.value, file=log) if config.autobouquetsmaker.bouquetsorder.value: print("[ABM-config] bouquetsorder: ", config.autobouquetsmaker.bouquetsorder.value, file=log) if config.autobouquetsmaker.keepallbouquets.value: print("[ABM-config] keepbouquets: All", file=log) else: print("[ABM-config] keepbouquets: ", config.autobouquetsmaker.keepbouquets.value, file=log) if config.autobouquetsmaker.hidesections.value: print("[ABM-config] hidesections: ", config.autobouquetsmaker.hidesections.value, file=log) print("[ABM-config] add provider prefix: ", config.autobouquetsmaker.addprefix.value, file=log) print("[ABM-config] show in extensions menu: ", config.autobouquetsmaker.extensions.value, file=log) print("[ABM-config] placement: ", config.autobouquetsmaker.placement.value, file=log) print("[ABM-config] skip services on non-configured satellites: ", config.autobouquetsmaker.skipservices.value, file=log) print("[ABM-config] show non-indexed: ", config.autobouquetsmaker.showextraservices.value, file=log) if config.autobouquetsmaker.FTA_only.value: print("[ABM-config] FTA_only: ", config.autobouquetsmaker.FTA_only.value, file=log) print("[ABM-config] schedule: ", config.autobouquetsmaker.schedule.value, file=log) if config.autobouquetsmaker.schedule.value: print("[ABM-config] schedule time: ", config.autobouquetsmaker.scheduletime.value, file=log) print( "[ABM-config] schedule days: ", [("Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday")[i] for i in range(7) if config.autobouquetsmaker.days[i].value], file=log) def getABMsettings(self): providers_extra = [] providers_tmp = config.autobouquetsmaker.providers.value.split("|") for provider_str in providers_tmp: provider = provider_str.split(":", 1)[0] if provider in self.dependents: for descendent in self.dependents[provider]: providers_extra.append('|' + descendent + ':' + provider_str.split(":", 1)[1]) return config.autobouquetsmaker.providers.value + ''.join( providers_extra) def about(self): self.session.open( MessageBox, "AutoBouquetsMaker\nVersion date - 21/10/2012\n\nCoded by:\n\nSkaman and AndyBlac", MessageBox.TYPE_INFO) def help(self): self.session.open(MessageBox, "AutoBouquetsMaker\nto be coded.", MessageBox.TYPE_INFO) def cancel(self): self.close(None)