403 lines
19 KiB
Python
403 lines
19 KiB
Python
# 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 <http://www.gnu.org/licenses/>.
|
|
#
|
|
# -*- coding: utf-8 -*-
|
|
|
|
from globals import *
|
|
from cqueue import *
|
|
from library import Library
|
|
from autotune import Autotune
|
|
from builder import Builder
|
|
from backup import Backup
|
|
from multiroom import Multiroom
|
|
from server import HTTP
|
|
|
|
class Tasks():
|
|
def __init__(self, service):
|
|
self.service = service
|
|
self.jsonRPC = service.jsonRPC
|
|
self.player = service.player
|
|
self.monitor = service.monitor
|
|
self.cache = SETTINGS.cache
|
|
self.quePriority = CustomQueue(priority=True,service=self.service)
|
|
|
|
|
|
def log(self, msg, level=xbmc.LOGDEBUG):
|
|
return log('%s: %s'%(self.__class__.__name__,msg),level)
|
|
|
|
|
|
def _que(self, func, priority=-1, *args, **kwargs):# priority -1 autostack, 1 Highest, 5 Lowest
|
|
if priority == -1: priority = self.quePriority.qsize + 1
|
|
self.log('_que, priority = %s, func = %s, args = %s, kwargs = %s' % (priority,func.__name__, args, kwargs))
|
|
self.quePriority._push((func, args, kwargs), priority)
|
|
|
|
|
|
def _initialize(self):
|
|
tasks = [self.chkInstanceID,
|
|
self.chkSettings,
|
|
self.chkDirs,
|
|
self.chkWelcome,
|
|
self.chkDebugging,
|
|
self.chkBackup,
|
|
self.chkHTTP,
|
|
self.chkPVRBackend,]
|
|
for func in tasks: self._que(func,1)
|
|
self.log('_initialize, finished...')
|
|
|
|
|
|
def chkSettings(self):
|
|
self.service.currentSettings = dict(SETTINGS.getCurrentSettings())
|
|
self.log('chkSettings, currentSettings = %s'%(self.service.currentSettings))
|
|
|
|
|
|
def chkInstanceID(self):
|
|
self.log('chkInstanceID')
|
|
PROPERTIES.getInstanceID()
|
|
|
|
|
|
@cacheit(expiration=datetime.timedelta(days=28), checksum=1)
|
|
def chkWelcome(self):
|
|
hasAutotuned = SETTINGS.hasAutotuned()
|
|
self.log('chkWelcome, hasAutotuned = %s'%(hasAutotuned))
|
|
if not hasAutotuned:
|
|
return BUILTIN.executescript('special://home/addons/%s/resources/lib/utilities.py, Show_Wiki_QR'%(ADDON_ID))
|
|
|
|
|
|
def chkDebugging(self):
|
|
self.log('chkDebugging')
|
|
if SETTINGS.getSettingBool('Debug_Enable'):
|
|
if DIALOG.yesnoDialog(LANGUAGE(32142),autoclose=4):
|
|
self.log('_chkDebugging, disabling debugging.')
|
|
SETTINGS.setSettingBool('Debug_Enable',False)
|
|
DIALOG.notificationDialog(LANGUAGE(32025))
|
|
self.jsonRPC.toggleShowLog(SETTINGS.getSettingBool('Debug_Enable'))
|
|
|
|
|
|
def chkBackup(self):
|
|
self.log('chkBackup')
|
|
Backup().hasBackup()
|
|
|
|
|
|
def chkHTTP(self):
|
|
self.log('chkHTTP')
|
|
HTTP(service=self.service)
|
|
|
|
|
|
def chkServers(self):
|
|
self.log('chkServers')
|
|
Multiroom(service=self.service).chkServers()
|
|
|
|
|
|
def chkPVRBackend(self):
|
|
self.log('chkPVRBackend')
|
|
if hasAddon(PVR_CLIENT_ID,True,True,True,True):
|
|
if not SETTINGS.hasPVRInstance():
|
|
SETTINGS.setPVRPath(USER_LOC, SETTINGS.getFriendlyName())
|
|
|
|
|
|
def _chkQueTimer(self):
|
|
self._chkEpochTimer('chkVersion' , self.chkVersion , 21600)
|
|
self._chkEpochTimer('chkKodiSettings' , self.chkKodiSettings , 3600)
|
|
self._chkEpochTimer('chkServers' , self.chkServers , 300)
|
|
self._chkEpochTimer('chkDiscovery' , self.chkDiscovery , 300)
|
|
self._chkEpochTimer('chkRecommended' , self.chkRecommended , 900)
|
|
self._chkEpochTimer('chkLibrary' , self.chkLibrary , 3600)
|
|
|
|
self._chkEpochTimer('chkFiles' , self.chkFiles , 300)
|
|
self._chkEpochTimer('chkURLQUE' , self.chkURLQUE , 300)
|
|
self._chkEpochTimer('chkJSONQUE' , self.chkJSONQUE , 300)
|
|
self._chkEpochTimer('chkLOGOQUE' , self.chkLOGOQUE , 600)
|
|
|
|
self._chkPropTimer('chkPVRRefresh' , self.chkPVRRefresh , 1)
|
|
self._chkPropTimer('chkFillers' , self.chkFillers , 2)
|
|
self._chkPropTimer('chkUpdate' , self.chkUpdate , 3)
|
|
|
|
|
|
def _chkEpochTimer(self, key, func, runevery=900, priority=-1, nextrun=None, *args, **kwargs):
|
|
if nextrun is None: nextrun = PROPERTIES.getPropertyInt(key, default=0) # nextrun == 0 => force que
|
|
epoch = int(time.time())
|
|
if epoch >= nextrun:
|
|
self.log('_chkEpochTimer, key = %s, last run %s' % (key, epoch - nextrun))
|
|
PROPERTIES.setPropertyInt(key, (epoch + runevery))
|
|
return self._que(func, priority, *args, **kwargs)
|
|
|
|
|
|
def _chkPropTimer(self, key, func, priority=-1, *args, **kwargs):
|
|
key = '%s.%s' % (ADDON_ID, key)
|
|
if PROPERTIES.getEXTPropertyBool(key):
|
|
self.log('_chkPropTimer, key = %s' % (key))
|
|
PROPERTIES.clrEXTProperty(key)
|
|
self._que(func, priority, *args, **kwargs)
|
|
|
|
|
|
@cacheit(expiration=datetime.timedelta(minutes=10))
|
|
def getOnlineVersion(self):
|
|
try: ONLINE_VERSION = re.compile('" version="(.+?)" name="%s"'%(ADDON_NAME)).findall(str(requestURL(ADDON_URL)))[0]
|
|
except: ONLINE_VERSION = ADDON_VERSION
|
|
return ONLINE_VERSION
|
|
|
|
|
|
def chkVersion(self):
|
|
update = False
|
|
ONLINE_VERSION = self.getOnlineVersion()
|
|
if ADDON_VERSION < ONLINE_VERSION:
|
|
update = True
|
|
DIALOG.notificationDialog(LANGUAGE(30073)%(ONLINE_VERSION))
|
|
elif ADDON_VERSION > (SETTINGS.getCacheSetting('lastVersion', checksum=ADDON_VERSION) or '0.0.0'):
|
|
SETTINGS.setCacheSetting('lastVersion',ADDON_VERSION, checksum=ADDON_VERSION)
|
|
BUILTIN.executescript('special://home/addons/%s/resources/lib/utilities.py, Show_Changelog'%(ADDON_ID))
|
|
self.log('chkVersion, update = %s, installed version = %s, online version = %s'%(update,ADDON_VERSION,ONLINE_VERSION))
|
|
SETTINGS.setSetting('Update_Status',{'True':'[COLOR=yellow]%s [B]v.%s[/B][/COLOR]'%(LANGUAGE(32168),ONLINE_VERSION),'False':'None'}[str(update)])
|
|
|
|
|
|
def chkKodiSettings(self):
|
|
self.log('chkKodiSettings')
|
|
MIN_GUIDEDAYS = SETTINGS.setSettingInt('Min_Days' ,self.jsonRPC.getSettingValue('epg.pastdaystodisplay' ,default=1))
|
|
MAX_GUIDEDAYS = SETTINGS.setSettingInt('Max_Days' ,self.jsonRPC.getSettingValue('epg.futuredaystodisplay' ,default=3))
|
|
OSD_TIMER = SETTINGS.setSettingInt('OSD_Timer',self.jsonRPC.getSettingValue('pvrmenu.displaychannelinfo',default=5))
|
|
|
|
|
|
def chkDirs(self):
|
|
self.log('chkDirs')
|
|
[FileAccess.makedirs(folder) for folder in [LOGO_LOC,FILLER_LOC,TEMP_LOC] if not FileAccess.exists(os.path.join(folder,''))]
|
|
|
|
|
|
def chkFiles(self):
|
|
self.log('chkFiles')
|
|
self.chkDirs()
|
|
if not (FileAccess.exists(LIBRARYFLEPATH) & FileAccess.exists(CHANNELFLEPATH) & FileAccess.exists(M3UFLEPATH) & FileAccess.exists(XMLTVFLEPATH) & FileAccess.exists(GENREFLEPATH)): self._que(self.chkLibrary,2)
|
|
|
|
|
|
def chkDiscovery(self):
|
|
self.log('chkDiscovery')
|
|
timerit(Multiroom(service=self.service)._chkDiscovery)(1.0)
|
|
|
|
|
|
def chkRecommended(self):
|
|
self.log('chkRecommended')
|
|
try:
|
|
library = Library(service=self.service)
|
|
library.searchRecommended()
|
|
del library
|
|
except Exception as e: self.log('chkRecommended failed! %s'%(e), xbmc.LOGERROR)
|
|
|
|
|
|
def chkLibrary(self, force=PROPERTIES.getPropertyBool('ForceLibrary')):
|
|
try:
|
|
library = Library(service=self.service)
|
|
library.importPrompt() #refactor feature
|
|
complete = library.updateLibrary(force)
|
|
del library
|
|
if complete:
|
|
self._que(self.chkChannels,3)
|
|
if force: PROPERTIES.setPropertyBool('ForceLibrary',False)
|
|
else:
|
|
self._que(self.chkLibrary,2,force)
|
|
self.log('chkLibrary, force = %s, complete = %s'%(force,complete))
|
|
except Exception as e: self.log('chkLibrary failed! %s'%(e), xbmc.LOGERROR)
|
|
|
|
|
|
def chkUpdate(self):
|
|
ids = PROPERTIES.getUpdateChannels()
|
|
if ids:
|
|
channels = self.getVerifiedChannels()
|
|
channels = [citem for id in ids for citem in channels if citem.get('id') == id]
|
|
self.log('chkUpdate, channels = %s\nid = %s'%(len(channels),ids))
|
|
self._que(self.chkChannels,3,channels)
|
|
|
|
|
|
def chkChannels(self, channels: list=[]):
|
|
save = False
|
|
complete = False
|
|
builder = Builder(service=self.service)
|
|
hasAutotuned = SETTINGS.hasAutotuned()
|
|
hasFirstRun = PROPERTIES.hasFirstRun()
|
|
hasEnabledServers = PROPERTIES.hasEnabledServers()
|
|
|
|
if not channels:
|
|
save = True
|
|
channels = builder.getVerifiedChannels()
|
|
SETTINGS.setSetting('Select_Channels','[B]%s[/B] Channels'%(len(channels)))
|
|
PROPERTIES.setChannels(len(channels) > 0)
|
|
self.service.currentChannels = channels #update service channels
|
|
|
|
if len(channels) > 0:
|
|
if not hasAutotuned: SETTINGS.setAutotuned(complete)
|
|
complete, updated = builder.build(channels)
|
|
self.log('chkChannels, channels = %s, complete = %s, updated = %s'%(len(channels),complete,updated))
|
|
if complete:
|
|
if save:
|
|
builder.channels.setChannels(channels)
|
|
if updated: PROPERTIES.setPropTimer('chkPVRRefresh')
|
|
if SETTINGS.getSettingBool('Build_Filler_Folders'): self._que(self.chkFillers,2,channels)
|
|
else: self._que(self.chkChannels,3,channels)
|
|
elif not hasAutotuned: return SETTINGS.setAutotuned(Autotune()._runTune())
|
|
elif hasEnabledServers: return PROPERTIES.setPropTimer('chkPVRRefresh')
|
|
del builder
|
|
if not hasFirstRun: PROPERTIES.setFirstRun(complete)
|
|
|
|
|
|
def chkLOGOQUE(self):
|
|
if not PROPERTIES.isRunning('chkLOGOQUE') and PROPERTIES.hasFirstRun():
|
|
with PROPERTIES.chkRunning('chkLOGOQUE'):
|
|
updated = False
|
|
library = Library(service=self.service)
|
|
resources = library.resources
|
|
queuePool = (SETTINGS.getCacheSetting('queueLOGO', json_data=True) or {})
|
|
params = randomShuffle(queuePool.get('params',[]))
|
|
for i in list(range(QUEUE_CHUNK)):
|
|
if self.service._interrupt():
|
|
self.log("chkLOGOQUE, _interrupt")
|
|
break
|
|
elif len(params) > 0:
|
|
param = params.pop(0)
|
|
updated = True
|
|
self.log("chkLOGOQUE, queuing = %s\n%s"%(len(params),param))
|
|
if param.get('name','').startswith('getLogoResources'):
|
|
self._que(resources.getLogoResources, 10+i, *param.get('args',()), **param.get('kwargs',{}))
|
|
elif param.get('name','').startswith('getTVShowLogo'):
|
|
self._que(resources.getTVShowLogo, 10+i, *param.get('args',()), **param.get('kwargs',{}))
|
|
queuePool['params'] = setDictLST(params)
|
|
if updated and len(queuePool['params']) == 0: PROPERTIES.setPropertyBool('ForceLibrary',True)
|
|
self.log('chkLOGOQUE, remaining = %s'%(len(queuePool['params'])))
|
|
SETTINGS.setCacheSetting('queueLOGO', queuePool, json_data=True)
|
|
del library
|
|
|
|
|
|
def chkJSONQUE(self):
|
|
if not PROPERTIES.isRunning('chkJSONQUE') and PROPERTIES.hasFirstRun():
|
|
with PROPERTIES.chkRunning('chkJSONQUE'):
|
|
queuePool = (SETTINGS.getCacheSetting('queueJSON', json_data=True) or {})
|
|
params = queuePool.get('params',[])
|
|
for i in list(range(QUEUE_CHUNK)):
|
|
if self.service._interrupt():
|
|
self.log("chkJSONQUE, _interrupt")
|
|
break
|
|
elif len(params) > 0:
|
|
param = params.pop(0)
|
|
self.log("chkJSONQUE, queuing = %s\n%s"%(len(params),param))
|
|
self._que(self.jsonRPC.sendJSON,5+1, param)
|
|
queuePool['params'] = setDictLST(params)
|
|
self.log('chkJSONQUE, remaining = %s'%(len(queuePool['params'])))
|
|
SETTINGS.setCacheSetting('queueJSON', queuePool, json_data=True)
|
|
|
|
|
|
def chkURLQUE(self):
|
|
if not PROPERTIES.isRunning('chkURLQUE') and PROPERTIES.hasFirstRun():
|
|
with PROPERTIES.chkRunning('chkURLQUE'):
|
|
queuePool = (SETTINGS.getCacheSetting('queueURL', json_data=True) or {})
|
|
params = queuePool.get('params',[])
|
|
for i in list(range(QUEUE_CHUNK)):
|
|
if self.service._interrupt():
|
|
self.log("chkURLQUE, _interrupt")
|
|
break
|
|
elif len(params) > 0:
|
|
param = params.pop(0)
|
|
self.log("chkURLQUE, queuing = %s\n%s"%(len(params),param))
|
|
self._que(requestURL,1, param)
|
|
queuePool['params'] = setDictLST(params)
|
|
self.log('chkURLQUE, remaining = %s'%(len(queuePool['params'])))
|
|
SETTINGS.setCacheSetting('queueURL', queuePool, json_data=True)
|
|
|
|
|
|
def chkPVRRefresh(self):
|
|
self.log('chkPVRRefresh')
|
|
self._que(self.chkPVRToggle,1)
|
|
|
|
|
|
def chkPVRToggle(self):
|
|
if self.service.monitor.isIdle and not (self.player.isPlaying() | BUILTIN.isScanning() | BUILTIN.isRecording()): togglePVR(False,True)
|
|
else: timerit(PROPERTIES.setPropTimer)(FIFTEEN,['chkPVRRefresh'])
|
|
|
|
|
|
def chkFillers(self, channels=[]):
|
|
self.log('chkFillers')
|
|
if not channels: channels = self.getVerifiedChannels()
|
|
pDialog = DIALOG.progressBGDialog(header='%s, %s'%(ADDON_NAME,LANGUAGE(32179)))
|
|
for idx, ftype in enumerate(FILLER_TYPES):
|
|
if not FileAccess.exists(os.path.join(FILLER_LOC,ftype.lower(),'')):
|
|
pDialog = DIALOG.progressBGDialog(int(idx*50//len(ftype)), pDialog, message='%s: %s'%(ftype,int(idx*100//len(ftype)))+'%', header='%s, %s'%(ADDON_NAME,LANGUAGE(32179)))
|
|
FileAccess.makedirs(os.path.join(FILLER_LOC,ftype.lower(),''))
|
|
|
|
genres = self.getGenreNames()
|
|
for idx, citem in enumerate(channels):
|
|
for ftype in FILLER_TYPES[1:]:
|
|
for genre in genres:
|
|
if not FileAccess.exists(os.path.join(FILLER_LOC,ftype.lower(),genre.lower(),'')):
|
|
pDialog = DIALOG.progressBGDialog(int(idx*50//len(channels)), pDialog, message='%s: %s'%(genre,int(idx*100//len(channels)))+'%', header='%s, %s'%(ADDON_NAME,LANGUAGE(32179)))
|
|
FileAccess.makedirs(os.path.join(FILLER_LOC,ftype.lower(),genre.lower()))
|
|
|
|
if not FileAccess.exists(os.path.join(FILLER_LOC,ftype.lower(),citem.get('name','').lower())):
|
|
if ftype.lower() == 'adverts': IGNORE = IGNORE_CHTYPE + MOVIE_CHTYPE
|
|
else: IGNORE = IGNORE_CHTYPE
|
|
if citem.get('name') and not citem.get('radio',False) and citem.get('type') not in IGNORE:
|
|
pDialog = DIALOG.progressBGDialog(int(idx*50//len(channels)), pDialog, message='%s: %s'%(citem.get('name'),int(idx*100//len(channels)))+'%', header='%s, %s'%(ADDON_NAME,LANGUAGE(32179)))
|
|
FileAccess.makedirs(os.path.join(FILLER_LOC,ftype.lower(),citem['name'].lower()))
|
|
pDialog = DIALOG.progressBGDialog(100, pDialog, message=LANGUAGE(32025), header='%s, %s'%(ADDON_NAME,LANGUAGE(32179)))
|
|
|
|
|
|
@cacheit(expiration=datetime.timedelta(minutes=15),json_data=False)
|
|
def getGenreNames(self):
|
|
self.log('getGenres')
|
|
try:
|
|
library = Library(self.service)
|
|
tvgenres = library.getTVGenres()
|
|
moviegenres = library.getMovieGenres()
|
|
genres = set([tvgenre.get('name') for tvgenre in tvgenres if tvgenre.get('name')] + [movgenre.get('name') for movgenre in moviegenres if movgenre.get('name')])
|
|
del library
|
|
return list(genres)
|
|
except Exception as e:
|
|
self.log('getGenres failed! %s'%(e), xbmc.LOGERROR)
|
|
return []
|
|
|
|
|
|
def chkSettingsChange(self, settings={}):
|
|
nSettings = dict(SETTINGS.getCurrentSettings())
|
|
for setting, value in list(settings.items()):
|
|
actions = {'User_Folder' :{'func':self.setUserPath ,'kwargs':{'old':value,'new':nSettings.get(setting)}},
|
|
'Debug_Enable' :{'func':self.jsonRPC.toggleShowLog ,'kwargs':{'state':SETTINGS.getSettingBool('Debug_Enable')}},
|
|
'Overlay_Enable' :{'func':PROPERTIES.setPendingRestart,'kwargs':{}},
|
|
'Enable_OnInfo' :{'func':PROPERTIES.setPendingRestart,'kwargs':{}},
|
|
'Disable_Trakt' :{'func':PROPERTIES.setPendingRestart,'kwargs':{}},
|
|
'Rollback_Watched' :{'func':PROPERTIES.setPendingRestart,'kwargs':{}},
|
|
'Store_Duration' :{'func':PROPERTIES.setPendingRestart,'kwargs':{}},
|
|
'Seek_Tolerance' :{'func':PROPERTIES.setPendingRestart,'kwargs':{}},
|
|
'Seek_Threshold' :{'func':PROPERTIES.setPendingRestart,'kwargs':{}},
|
|
'Idle_Timer' :{'func':PROPERTIES.setPendingRestart,'kwargs':{}},
|
|
'Run_While_Playing' :{'func':PROPERTIES.setPendingRestart,'kwargs':{}},
|
|
'Restart_Percentage':{'func':PROPERTIES.setPendingRestart,'kwargs':{}},}
|
|
|
|
if nSettings.get(setting) != value and actions.get(setting):
|
|
action = actions.get(setting)
|
|
self.log('chkSettingsChange, detected change in %s: %s => %s\naction = %s'%(setting,value,nSettings.get(setting),action))
|
|
self._que(action.get('func'),1,*action.get('args',()),**action.get('kwargs',{}))
|
|
return nSettings
|
|
|
|
|
|
def setUserPath(self, old, new):
|
|
with PROPERTIES.interruptActivity():
|
|
self.log('setUserPath, old = %s, new = %s'%(old,new))
|
|
dia = DIALOG.progressDialog(message='%s\n%s'%(LANGUAGE(32050),old))
|
|
FileAccess.copyFolder(old, new, dia)
|
|
SETTINGS.setPVRPath(new,prompt=True,force=True)
|
|
PROPERTIES.setPendingRestart()
|
|
DIALOG.progressDialog(100, dia)
|
|
|
|
|
|
def getVerifiedChannels(self):
|
|
return Builder(service=self.service).getVerifiedChannels() |