# 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 server import Discovery class Service: from jsonrpc import JSONRPC player = PLAYER() monitor = MONITOR() jsonRPC = JSONRPC() def _interrupt(self) -> bool: return PROPERTIES.isPendingInterrupt() def _suspend(self) -> bool: return PROPERTIES.isPendingSuspend() class Multiroom: def __init__(self, sysARG=sys.argv, service=None): self.log('__init__, sysARG = %s'%(sysARG)) if service is None: service = Service() self.service = service self.jsonRPC = service.jsonRPC self.cache = service.jsonRPC.cache self.sysARG = sysARG self.uuid = SETTINGS.getMYUUID() self.friendly = SETTINGS.getFriendlyName() self.remoteHost = PROPERTIES.getRemoteHost() def log(self, msg, level=xbmc.LOGDEBUG): return log('%s: %s'%(self.__class__.__name__,msg),level) @cacheit(checksum=PROPERTIES.getInstanceID(), expiration=datetime.timedelta(minutes=FIFTEEN)) def _getStatus(self): return self.jsonRPC.getSettingValue("services.zeroconf",default=False) def _chkDiscovery(self): self.log('_chkDiscovery') Discovery(service=self.service, multiroom=self) def chkServers(self, servers={}): def __chkSettings(settings): [hasAddon(id,install=True,enable=True) for k,addons in list(settings.items()) for id in addons if id.startswith(('resource','plugin'))] if isinstance(servers,bool): servers = {} #temp fix remove after a by next build if not servers: servers = self.getDiscovery() PROPERTIES.setServers(len(servers) > 0) for server in list(servers.values()): online = server.get('online',False) response = self.getRemote(server.get('remotes',{}).get('bonjour')) if response: server['online'] = True else: server['online'] = False if server.get('enabled',False): if online != server.get('online',False): DIALOG.notificationDialog('%s: %s'%(server.get('name'),LANGUAGE(32211)%({True:'green',False:'red'}[server.get('online',False)],{True:LANGUAGE(32158),False:LANGUAGE(32253)}[server.get('online',False)]))) __chkSettings(loadJSON(server.get('settings'))) SETTINGS.setSetting('Select_server','|'.join([LANGUAGE(32211)%({True:'green',False:'red'}[server.get('online',False)],server.get('name')) for server in self.getEnabled(servers)])) self.log('chkServers, servers = %s'%(len(servers))) self.setDiscovery(servers) return servers def getDiscovery(self): servers = getJSON(SERVER_LOC).get('servers',{}) if isinstance(servers,bool): servers = {} #temp fix remove after a by next build return servers def setDiscovery(self, servers={}): return setJSON(SERVER_LOC,{"servers":servers}) def getEnabled(self, servers={}): if not servers: servers = self.getDiscovery() enabled = [server for server in list(servers.values()) if server.get('enabled',False)] PROPERTIES.setEnabledServers(len(enabled) > 0) return enabled @cacheit(expiration=datetime.timedelta(minutes=FIFTEEN), json_data=True) def getRemote(self, remote): self.log("getRemote, remote = %s"%(remote)) cacheName = 'getRemote.%s'%(remote) return requestURL(remote, header={'Accept':'application/json'}, json_data=True, cache=self.cache, checksum=self.uuid, life=datetime.timedelta(days=MAX_GUIDEDAYS)) def addServer(self, payload={}): self.log('addServer, name = %s'%(payload.get('name'))) if payload and payload.get('name') and payload.get('host'): payload['online'] = True servers = self.getDiscovery() server = servers.get(payload.get('name'),{}) if not server: payload['enabled'] = not bool(SETTINGS.getSettingBool('Debug_Enable')) #set enabled by default when not debugging. self.log('addServer, adding server = %s'%(payload)) DIALOG.notificationDialog('%s: %s'%(LANGUAGE(32047),payload.get('name'))) servers[payload['name']] = payload else: payload['enabled'] = server.get('enabled',False) if payload.get('md5',server.get('md5')) != server.get('md5'): self.log('addServer, updating server = %s'%(server)) servers.update({payload['name']:payload}) if self.setDiscovery(self.chkServers(servers)): instancePath = SETTINGS.hasPVRInstance(server.get('name')) if payload.get('enabled',False) and not instancePath: changed = SETTINGS.setPVRRemote(payload.get('host'),payload.get('name')) elif not payload.get('enabled',False) and instancePath: changed = FileAccess.delete(instancePath) else: changed = False if changed: PROPERTIES.setPropTimer('chkPVRRefresh') self.log('addServer, payload changed, chkPVRRefresh = %s'%(changed)) return True def delServer(self, servers={}): self.log('delServer') def __build(idx, payload): return LISTITEMS.buildMenuListItem(payload.get('name'),'%s - %s: Channels (%s)'%(LANGUAGE(32211)%({True:'green',False:'red'}[payload.get('online',False)],{True:LANGUAGE(32158),False:LANGUAGE(32253)}[payload.get('online',False)]),payload.get('host'),len(payload.get('channels',[]))),icon=DUMMY_ICON.format(text=str(idx+1)),url=dumpJSON(payload)) with BUILTIN.busy_dialog(): if not servers: servers = self.getDiscovery() lizlst = [__build(idx, server) for idx, server in enumerate(list(servers.values()))] selects = DIALOG.selectDialog(lizlst,LANGUAGE(32183)) if not selects is None: [servers.pop(liz.getLabel()) for idx, liz in enumerate(lizlst) if idx in selects] if self.setDiscovery(self.chkServers(servers)): return DIALOG.notificationDialog(LANGUAGE(30046)) def selServer(self): self.log('selServer') def __build(idx, payload): return LISTITEMS.buildMenuListItem(payload.get('name'),'%s - %s: Channels (%s)'%(LANGUAGE(32211)%({True:'green',False:'red'}[payload.get('online',False)],{True:LANGUAGE(32158),False:LANGUAGE(32253)}[payload.get('online',False)]),payload.get('host'),len(payload.get('channels',[]))),icon=DUMMY_ICON.format(text=str(idx+1)),url=dumpJSON(payload)) with BUILTIN.busy_dialog(): servers = self.getDiscovery() lizlst = [__build(idx, server) for idx, server in enumerate(list(servers.values()))] if len(lizlst) > 0: lizlst.insert(0,LISTITEMS.buildMenuListItem('[COLOR=white][B]- %s[/B][/COLOR]'%(LANGUAGE(30046)),LANGUAGE(33046))) else: return selects = DIALOG.selectDialog(lizlst,LANGUAGE(30130),preselect=[idx for idx, listitem in enumerate(lizlst) if loadJSON(listitem.getPath()).get('enabled',False)]) if not selects is None: if 0 in selects: return self.delServer(servers) else: for idx, liz in enumerate(lizlst): if idx == 0: continue instancePath = SETTINGS.hasPVRInstance(liz.getLabel()) if idx in selects: if not servers[liz.getLabel()].get('enabled',False): DIALOG.notificationDialog(LANGUAGE(30099)%(liz.getLabel())) servers[liz.getLabel()]['enabled'] = True if not instancePath: if SETTINGS.setPVRRemote(servers[liz.getLabel()].get('host'),liz.getLabel()): PROPERTIES.setPropTimer('chkPVRRefresh') else: if servers[liz.getLabel()].get('enabled',False): DIALOG.notificationDialog(LANGUAGE(30100)%(liz.getLabel())) servers[liz.getLabel()]['enabled'] = False if instancePath: FileAccess.delete(instancePath) with BUILTIN.busy_dialog(): return self.setDiscovery(self.chkServers(servers)) def enableZeroConf(self): self.log('enableZeroConf') if SETTINGS.getSetting('ZeroConf_Status') == '[COLOR=red][B]%s[/B][/COLOR]'%(LANGUAGE(32253)): if BUILTIN.getInfoLabel('Platform.Windows','System'): BUILTIN.executescript('special://home/addons/%s/resources/lib/utilities.py, Show_ZeroConf_QR'%(ADDON_ID)) if DIALOG.yesnoDialog(message=LANGUAGE(30129)): with PROPERTIES.interruptActivity(): if self.jsonRPC.setSettingValue("services.zeroconf",True,queue=False): DIALOG.notificationDialog(LANGUAGE(32219)%(LANGUAGE(30035))) PROPERTIES.setEpochTimer('chkKodiSettings') else: DIALOG.notificationDialog(LANGUAGE(32219)%(LANGUAGE(30034))) def run(self): try: param = self.sysARG[1] except: param = None if param == 'Enable_ZeroConf': ctl = (5,1) self.enableZeroConf() elif param == 'Select_Server': ctl = (5,11) self.selServer() elif param == 'Remove_server': ctl = (5,12) return SETTINGS.openSettings(ctl) if __name__ == '__main__': timerit(Multiroom(sys.argv).run)(0.1)