# 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 * class Utilities: def __init__(self, sysARG=sys.argv): self.log('__init__, sysARG = %s'%(sysARG)) self.sysARG = sysARG def log(self, msg, level=xbmc.LOGDEBUG): return log('%s: %s'%(self.__class__.__name__,msg),level) def qrWiki(self): with PROPERTIES.suspendActivity(): DIALOG.qrDialog(URL_WIKI,LANGUAGE(32216)%(ADDON_NAME,ADDON_AUTHOR)) def qrSupport(self): with PROPERTIES.suspendActivity(): DIALOG.qrDialog(URL_SUPPORT, LANGUAGE(30033)%(ADDON_NAME)) def qrRemote(self): with PROPERTIES.suspendActivity(): DIALOG.qrDialog('http://%s/%s'%(PROPERTIES.getRemoteHost(),'remote.html'), LANGUAGE(30165)) def qrReadme(self): with PROPERTIES.suspendActivity(): DIALOG.qrDialog(URL_README, LANGUAGE(32043)%(ADDON_NAME,ADDON_VERSION)) def qrBonjourDL(self): with PROPERTIES.suspendActivity(): DIALOG.qrDialog(URL_WIN_BONJOUR, LANGUAGE(32217)) def showChangelog(self): try: def __addColor(text): text = text.replace('- Added' ,'[COLOR=green][B]- Added:[/B][/COLOR]') text = text.replace('- Introduced' ,'[COLOR=green][B]- Introduced:[/B][/COLOR]') text = text.replace('- Addressed' ,'[COLOR=green][B]- Addressed:[/B][/COLOR]') text = text.replace('- New!' ,'[COLOR=yellow][B]- New!:[/B][/COLOR]') text = text.replace('- Optimized' ,'[COLOR=yellow][B]- Optimized:[/B][/COLOR]') text = text.replace('- Improved' ,'[COLOR=yellow][B]- Improved:[/B][/COLOR]') text = text.replace('- Modified' ,'[COLOR=yellow][B]- Modified:[/B][/COLOR]') text = text.replace('- Enhanced' ,'[COLOR=yellow][B]- Enhanced:[/B][/COLOR]') text = text.replace('- Refactored' ,'[COLOR=yellow][B]- Refactored:[/B][/COLOR]') text = text.replace('- Reworked' ,'[COLOR=yellow][B]- Reworked:[/B][/COLOR]') text = text.replace('- Tweaked' ,'[COLOR=yellow][B]- Tweaked:[/B][/COLOR]') text = text.replace('- Updated' ,'[COLOR=yellow][B]- Updated:[/B][/COLOR]') text = text.replace('- Changed' ,'[COLOR=yellow][B]- Changed:[/B][/COLOR]') text = text.replace('- Corrected' ,'[COLOR=yellow][B]- Corrected:[/B][/COLOR]') text = text.replace('- Proper' ,'[COLOR=yellow][B]- Proper:[/B][/COLOR]') text = text.replace('- Included' ,'[COLOR=yellow][B]- Changed:[/B][/COLOR]') text = text.replace('- Notice' ,'[COLOR=orange][B]- Notice:[/B][/COLOR]') text = text.replace('- Fixed' ,'[COLOR=orange][B]- Fixed:[/B][/COLOR]') text = text.replace('- Resolved' ,'[COLOR=orange][B]- Resolved:[/B][/COLOR]') text = text.replace('- Removed' ,'[COLOR=red][B]- Removed:[/B][/COLOR]') text = text.replace('- Excluded' ,'[COLOR=red][B]- Excluded:[/B][/COLOR]') text = text.replace('- Deprecated' ,'[COLOR=red][B]- Deprecated:[/B][/COLOR]') text = text.replace('- Important' ,'[COLOR=red][B]- Important:[/B][/COLOR]') text = text.replace('- Warning' ,'[COLOR=red][B]- Warning:[/B][/COLOR]') return text with BUILTIN.busy_dialog(): fle = FileAccess.open(CHANGELOG_FLE, "r") txt = __addColor(fle.read()) fle.close() DIALOG.textviewer(txt, heading=(LANGUAGE(32045)%(ADDON_NAME,ADDON_VERSION)),usemono=True) except Exception as e: self.log('showChangelog failed! %s'%(e), xbmc.LOGERROR) def qrDebug(self): def __cleanLog(content): content = re.sub('//.+?:.+?@' ,'//USER:PASSWORD@' , content) content = re.sub('.+?' ,'USER' , content) content = re.sub('.+?' ,'PASSWORD', content) content = re.sub(r"\b(?:\d{1,3}\.){3}\d{1,3}\b", '0.0.0.0' , content) return content def __cleanPayload(payload): def __getDebug(payload): #only post errors debug = payload.get('debug',{}) # [debug.pop(key) for key in list(debug.keys()) if key in ['LOGDEBUG','LOGINFO']] return debug payload['debug'] = loadJSON(__cleanLog(dumpJSON(__getDebug(payload),idnt=4))) payload['channels'] = loadJSON(__cleanLog(dumpJSON(payload.get('channels',[]),idnt=4))) payload['m3u'] = loadJSON(__cleanLog(dumpJSON(payload.get('m3u',[]),idnt=4))) [payload.pop(key) for key in ['host','remotes','bonjour','library','servers'] if key in payload] return payload def __postLog(data): try: session = requests.Session() response = session.post('https://paste.kodi.tv/' + 'documents', data=data.encode('utf-8'), headers={'User-Agent':'%s: %s'%(ADDON_ID, ADDON_VERSION)}) if 'key' in response.json(): return True, 'https://paste.kodi.tv/' + response.json()['key'] elif 'message' in response.json(): self.log('qrDebug, upload failed, paste may be too large') return False, response.json()['message'] else: self.log('qrDebug failed! %s'%response.text) return False, LANGUAGE(30191) except: self.log('qrDebug, unable to retrieve the paste url') return False, LANGUAGE(30190) with BUILTIN.busy_dialog(): payload = SETTINGS.getPayload(inclDebug=True) if not payload.get('debug',{}): return DIALOG.notificationDialog(LANGUAGE(32187)) elif not DIALOG.yesnoDialog(message=LANGUAGE(32188)): return with BUILTIN.busy_dialog(): succes, data = __postLog(dumpJSON(__cleanPayload(payload),idnt=4)) if succes: DIALOG.qrDialog(data,LANGUAGE(32189)%(data)) else: DIALOG.okDialog(LANGUAGE(32190)%(data)) def _runCPUBench(self): with BUILTIN.busy_dialog(): if hasAddon('script.pystone.benchmark',install=True, enable=True, notify=True): return BUILTIN.executebuiltin('RunScript(script.pystone.benchmark)') def _runIOBench(self): with BUILTIN.busy_dialog(): if hasAddon('script.io.benchmark',install=True, enable=True, notify=True): return BUILTIN.executebuiltin('RunScript(script.io.benchmark,%s)'%(escapeString(f'path={USER_LOC}'))) def _runLogger(self): with BUILTIN.busy_dialog(): if hasAddon('script.kodi.loguploader',install=True, enable=True, notify=True): return BUILTIN.executebuiltin('RunScript(script.kodi.loguploader)') def _runCleanup(self, full=False): self.log('_runCleanup, full = %s'%(full)) files = {LANGUAGE(30094):M3UFLEPATH, #"M3U" LANGUAGE(30095):XMLTVFLEPATH, #"XMLTV" LANGUAGE(30096):GENREFLEPATH, #"Genre" LANGUAGE(30108):CHANNELFLEPATH,#"Channels" LANGUAGE(32041):LIBRARYFLEPATH}#"Library" keys = list(files.keys()) if not full: keys = keys[:2] if DIALOG.yesnoDialog('%s ?'%(msg)): with BUILTIN.busy_dialog(), PROPERTIES.interruptActivity(): for key in keys: if FileAccess.delete(files[key]): DIALOG.notificationDialog(LANGUAGE(32127)%(key.replace(':',''))) else: DIALOG.notificationDialog('%s %s'%((LANGUAGE(32127)%(key.replace(':',''))),LANGUAGE(32052))) self._runUpdate(full) def _runReload(self): if DIALOG.yesnoDialog('%s?'%(LANGUAGE(32121)%(xbmcaddon.Addon(PVR_CLIENT_ID).getAddonInfo('name')))): PROPERTIES.setPropTimer('chkPVRRefresh') def _runRestart(self): return PROPERTIES.setPendingRestart() def _runFillers(self): return PROPERTIES.setPropTimer('chkFillers') def _runLibrary(self): PROPERTIES.setPropertyBool('ForceLibrary',True) PROPERTIES.setEpochTimer('chkLibrary') DIALOG.notificationDialog('%s %s'%(LANGUAGE(30199),LANGUAGE(30200))) def _runAutotune(self): self.log('_runAutotune') SETTINGS.setAutotuned(False) PROPERTIES.setPropTimer('chkChannels') def _runUpdate(self, full=False): self.log('_runUpdate, full = %s'%(full)) if full: PROPERTIES.setEpochTimer('chkLibrary') PROPERTIES.setEpochTimer('chkChannels') def buildMenu(self, select=None): items = [ {'label':LANGUAGE(32117) ,'label2':LANGUAGE(32120),'icon':COLOR_LOGO,'func':self._runCleanup , 'hide':True ,'args':(False,)}, #"Rebuild M3U/XMLTV" {'label':LANGUAGE(32118) ,'label2':LANGUAGE(32119),'icon':COLOR_LOGO,'func':self._runCleanup , 'hide':True ,'args':(True,)}, #"Clean Start" {'label':LANGUAGE(32121)%(PVR_CLIENT_NAME),'label2':LANGUAGE(32122),'icon':COLOR_LOGO,'func':self._runReload , 'hide':False},#"Force PVR reload" {'label':LANGUAGE(32123) ,'label2':LANGUAGE(32124),'icon':COLOR_LOGO,'func':self._runRestart , 'hide':False},#"Force PTVL reload" {'label':LANGUAGE(32159) ,'label2':LANGUAGE(33159),'icon':COLOR_LOGO,'func':self._runLibrary , 'hide':False}, {'label':LANGUAGE(32180) ,'label2':LANGUAGE(33180),'icon':COLOR_LOGO,'func':self._runFillers , 'hide':False}, {'label':LANGUAGE(32181) ,'label2':LANGUAGE(33181),'icon':COLOR_LOGO,'func':self._runAutotune , 'hide':False}, {'label':LANGUAGE(30205) ,'label2':LANGUAGE(30205),'icon':COLOR_LOGO,'func':self._runCPUBench , 'hide':False}, {'label':LANGUAGE(30208) ,'label2':LANGUAGE(30208),'icon':COLOR_LOGO,'func':self._runIOBench , 'hide':False}, ] with BUILTIN.busy_dialog(): if not SETTINGS.getSettingBool('Debug_Enable'): items = [item for item in items if not item.get('hide',False)] listItems = [LISTITEMS.buildMenuListItem(item.get('label'),item.get('label2'),item.get('icon')) for item in sorted(items,key=itemgetter('label'))] if select is None: select = DIALOG.selectDialog(listItems, '%s - %s'%(ADDON_NAME,LANGUAGE(32126)),multi=False) if not select is None: with PROPERTIES.interruptActivity(): try: selectItem = [item for item in items if item.get('label') == listItems[select].getLabel()][0] self.log('buildMenu, selectItem = %s'%selectItem) if selectItem.get('args'): selectItem['func'](*selectItem['args']) else: selectItem['func']() except Exception as e: self.log("buildMenu, failed! %s"%(e), xbmc.LOGERROR) return DIALOG.notificationDialog(LANGUAGE(32000)) else: SETTINGS.openSettings((6,1)) def openChannelManager(self, chnum: int=1): self.log('openChannelManager, chnum = %s'%(chnum)) if not PROPERTIES.isRunning('OVERLAY_MANAGER'): with PROPERTIES.chkRunning('OVERLAY_MANAGER'), PROPERTIES.interruptActivity(): with BUILTIN.busy_dialog(): from manager import Manager chmanager = Manager(MANAGER_XML, ADDON_PATH, "default", channel=chnum) del chmanager else: DIALOG.notificationDialog(LANGUAGE(32057)%(ADDON_NAME)) def openPositionUtil(self, idx): self.log('openPositionUtil, idx = %s'%(idx)) if not PROPERTIES.isRunning('OVERLAY_UTILITY'): with PROPERTIES.chkRunning('OVERLAY_UTILITY'), PROPERTIES.interruptActivity(): with BUILTIN.busy_dialog(): from overlaytool import OverlayTool overlaytool = OverlayTool(OVERLAYTOOL_XML, ADDON_PATH, "default", Focus_IDX=idx) del overlaytool def defaultChannels(self): self.log('defaultChannels') with BUILTIN.busy_dialog(): values = SETTINGS.getSettingList('Select_server') values = [cleanLabel(value) for value in values] values.insert(0,LANGUAGE(30022)) #Auto values.insert(1,LANGUAGE(32069)) select = DIALOG.selectDialog(values, LANGUAGE(30173), findItemsInLST(values, [SETTINGS.getSetting('Default_Channels')])[0], False, SELECT_DELAY, False) if not select is None: return SETTINGS.setSetting('Default_Channels',values[select]) else: return SETTINGS.setSetting('Default_Channels',LANGUAGE(30022)) def run(self): with BUILTIN.busy_dialog(): ctl = (0,1) try: param = self.sysARG[1] except: param = None #Channels if param.startswith('Channel_Manager'): ctl = (0,1) self.openChannelManager() elif param.startswith('Default_Channels'): ctl = (0,2) self.defaultChannels() #Globals elif param.startswith('Move_Channelbug'): ctl = (3,15) self.openPositionUtil(1) elif param.startswith('Move_OnNext'): ctl = (3,15) self.openPositionUtil(2) #Multi-Room elif param == 'Show_ZeroConf_QR': ctl = (5,5) self.qrBonjourDL() #Misc. Scripts elif param == 'CPU_Bench': self._runCPUBench() elif param == 'IO_Bench': self._runIOBench() elif param == 'Logger': self._runLogger() #Misc.Docs elif param == 'Utilities': ctl = (6,1) return self.buildMenu() elif param == 'Show_Wiki_QR': ctl = (6,4) return self.qrWiki() elif param == 'Show_Support_QR': ctl = (6,5) return self.qrSupport() elif param == 'Show_Remote_UI': ctl = (6,6) return self.qrRemote() elif param == 'Show_Changelog': ctl = (6,8) return self.showChangelog() #Misc. Debug elif param == 'Debug_QR': ctl = (6,1) return self.qrDebug() elif param == 'Run_Autotune': return self._runAutotune() return SETTINGS.openSettings(ctl) if __name__ == '__main__': timerit(Utilities(sys.argv).run)(0.1)