# Copyright (C) 2024 Lunatixz # # # This file is part of PseudoTV Live. # # PseudoTV Live is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # PseudoTV Live is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with PseudoTV Live. If not, see . # -*- coding: utf-8 -*- from globals import * from library import Library from channels import Channels class Autotune: def __init__(self, sysARG=sys.argv): self.log('__init__, sysARG = %s'%(sysARG)) self.sysARG = sysARG self.channels = Channels() self.library = Library() def log(self, msg, level=xbmc.LOGDEBUG): return log('%s: %s'%(self.__class__.__name__,msg),level) def getCustom(self) -> dict: #return autotuned channels ie. channels > CHANNEL_LIMIT channels = self.channels.getCustom() self.log('getCustom, channels = %s'%(len(channels))) return channels def getAutotuned(self) -> dict: #return autotuned channels ie. channels > CHANNEL_LIMIT channels = self.channels.getAutotuned() self.log('getAutotuned, channels = %s'%(len(channels))) return channels def _runTune(self, prompt: bool=False, rebuild: bool=False, dia=None): customChannels = self.getCustom() autoChannels = self.getAutotuned() hasLibrary = PROPERTIES.hasLibrary() if len(autoChannels) > 0 or hasLibrary: rebuild = PROPERTIES.setEXTPropertyBool('%s.has.Predefined'%(ADDON_ID),True) #rebuild existing autotune, no prompt needed, refresh paths and logos if len(customChannels) == 0: prompt = True #begin check if prompt or recovery is needed self.log('_runTune, customChannels = %s, autoChannels = %s'%(len(customChannels),len(autoChannels))) if prompt: opt = '' msg = '%s?'%(LANGUAGE(32042)%(ADDON_NAME)) hasBackup = PROPERTIES.hasBackup() hasServers = PROPERTIES.hasServers() hasM3U = FileAccess.exists(M3UFLEPATH) if not hasLibrary else False if (hasBackup or hasServers or hasM3U): opt = LANGUAGE(32254) msg = '%s\n%s'%(LANGUAGE(32042)%(ADDON_NAME),LANGUAGE(32255)) retval = DIALOG.yesnoDialog(message=msg,customlabel=opt) if retval == 1: dia = DIALOG.progressBGDialog(header='%s, %s'%(ADDON_NAME,LANGUAGE(32021))) #Yes elif retval == 2: #Custom with BUILTIN.busy_dialog(), PROPERTIES.interruptActivity(): menu = [LISTITEMS.buildMenuListItem(LANGUAGE(30107),LANGUAGE(33310),url='special://home/addons/%s/resources/lib/utilities.py, Channel_Manager'%(ADDON_ID))] if hasM3U: menu.append(LISTITEMS.buildMenuListItem(LANGUAGE(32257),LANGUAGE(32256),url='special://home/addons/%s/resources/lib/autotune.py, Recover_M3U'%(ADDON_ID))) if hasBackup: menu.append(LISTITEMS.buildMenuListItem(LANGUAGE(32112),LANGUAGE(32111),url='special://home/addons/%s/resources/lib/backup.py, Recover_Backup'%(ADDON_ID))) if hasServers: menu.append(LISTITEMS.buildMenuListItem(LANGUAGE(30173),LANGUAGE(32215),url='special://home/addons/%s/resources/lib/multiroom.py, Select_Server'%(ADDON_ID))) select = DIALOG.selectDialog(menu,multi=False) if not select is None: return BUILTIN.executescript(menu[select].getPath()) else: return True #No else: return True for idx, ATtype in enumerate(AUTOTUNE_TYPES): if dia: dia = DIALOG.progressBGDialog(int((idx+1)*100//len(AUTOTUNE_TYPES)),dia,ATtype,'%s, %s'%(ADDON_NAME,LANGUAGE(32021))) self.selectAUTOTUNE(ATtype, autoSelect=prompt, rebuildChannels=rebuild) return True def selectAUTOTUNE(self, ATtype: str, autoSelect: bool=False, rebuildChannels: bool=False): self.log('selectAUTOTUNE, ATtype = %s, autoSelect = %s, rebuildChannels = %s'%(ATtype,autoSelect,rebuildChannels)) def __buildMenuItem(item): return LISTITEMS.buildMenuListItem(item['name'],item['type'],item['logo']) def _match(enabledItems): for item in enabledItems: for idx, liz in enumerate(lizlst): if item.get('name','').lower() == liz.getLabel().lower(): yield idx def _set(ATtype, selects=[]): for item in items: item['enabled'] = False #disable everything before selecting new items. for select in selects: if item.get('name','').lower() == lizlst[select].getLabel().lower(): item['enabled'] = True self.library.setLibrary(ATtype, items) items = self.library.getLibrary(ATtype) if len(items) == 0 and (not rebuildChannels and not autoSelect): if SETTINGS.getSettingBool('Debug_Enable'): DIALOG.notificationDialog(LANGUAGE(32018)%(ATtype)) return lizlst = poolit(__buildMenuItem)(items) if rebuildChannels:#rebuild channels.json entries selects = list(_match(self.library.getEnabled(ATtype))) elif autoSelect:#build sample channels if len(items) >= AUTOTUNE_LIMIT: selects = sorted(set(random.sample(list(set(range(0,len(items)))),AUTOTUNE_LIMIT))) else: selects = list(range(0,len(items))) else: selects = DIALOG.selectDialog(lizlst,LANGUAGE(32017)%(ATtype),preselect=list(_match(self.library.getEnabled(ATtype)))) if not selects is None: _set(ATtype, selects) return self.buildAUTOTUNE(ATtype, self.library.getEnabled(ATtype)) def buildAUTOTUNE(self, ATtype: str, items: list=[]): if not list: return def buildAvailableRange(existing): # create number array for given type, excluding existing channel numbers. if existing: existingNUMBERS = [eitem.get('number') for eitem in existing if eitem.get('number',0) > 0] # existing channel numbers else: existingNUMBERS = [] start = ((CHANNEL_LIMIT+1)*(AUTOTUNE_TYPES.index(ATtype)+1)) stop = (start + CHANNEL_LIMIT) self.log('buildAUTOTUNE, ATtype = %s, range = %s-%s, existingNUMBERS = %s'%(ATtype,start,stop,existingNUMBERS)) return [num for num in range(start,stop) if num not in existingNUMBERS] existingAUTOTUNE = self.channels.popChannels(ATtype,self.getAutotuned()) usesableNUMBERS = iter(buildAvailableRange(existingAUTOTUNE)) # available channel numbers for item in items: music = isRadio(item) citem = self.channels.getTemplate() citem.update({"id" : "", "type" : ATtype, "number" : 0, "name" : getChannelSuffix(item['name'], ATtype), "logo" : item.get('logo',LOGO), "path" : item.get('path',''), "group" : [item.get('type','')], "rules" : item.get('rules',{}), "catchup" : ('vod' if not music else ''), "radio" : music, "favorite": True}) match, eitem = self.channels.findAutotuned(citem, channels=existingAUTOTUNE) if match is None: #new autotune citem['id'] = getChannelID(citem['name'],citem['path'],citem['number']) #generate new channelid citem['number'] = next(usesableNUMBERS,0) #first available channel number PROPERTIES.setUpdateChannels(citem['id']) else: #update existing autotune citem['id'] = eitem.get('id') citem['number'] = eitem.get('number') citem['logo'] = chkLogo(eitem.get('logo',''),citem.get('logo',LOGO)) citem['favorite'] = eitem.get('favorite',False) self.log('[%s] buildAUTOTUNE, number = %s, match = %s'%(citem['id'],citem['number'],match)) self.channels.addChannel(citem) return self.channels.setChannels() def recoverM3U(self, autotune={}): from m3u import M3U stations = M3U().getStations() [autotune.setdefault(AUTOTUNE_TYPES[station.get('number')//1000],[]).append(station.get('name')) for station in stations if station.get('number') > CHANNEL_LIMIT] [self.library.enableByName(type, names) for type, names in list(autotune.items()) if len(names) > 0] return BUILTIN.executescript('special://home/addons/%s/resources/lib/utilities.py, Run_Autotune'%(ADDON_ID)) def clearLibrary(self): self.library.resetLibrary() DIALOG.notificationDialog(LANGUAGE(32025)) def clearBlacklist(self): SETTINGS.setSetting('Clear_BlackList','') DIALOG.notificationDialog(LANGUAGE(32025)) def run(self): with BUILTIN.busy_dialog(): ctl = (1,1) #settings return focus try: param = self.sysARG[1] except: param = None if param.replace('_',' ') in AUTOTUNE_TYPES: ctl = (1,AUTOTUNE_TYPES.index(param.replace('_',' '))+1) self.selectAUTOTUNE(param.replace('_',' ')) elif param == 'Clear_Autotune' : self.clearLibrary() elif param == 'Clear_BlackList': self.clearBlacklist() elif param == 'Recover_M3U': self.recoverM3U() elif param == None: return return SETTINGS.openSettings(ctl) if __name__ == '__main__': timerit(Autotune(sys.argv).run)(0.1)