498 lines
22 KiB
Python
498 lines
22 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 overlay import Background, Restart, Overlay, OnNext
|
|
from rules import RulesList
|
|
from tasks import Tasks
|
|
from jsonrpc import JSONRPC
|
|
|
|
class Player(xbmc.Player):
|
|
sysInfo = {}
|
|
pendingItem = {}
|
|
isPseudoTV = False
|
|
pendingStop = False
|
|
pendingPlay = -1
|
|
lastSubState = False
|
|
background = None
|
|
restart = None
|
|
onnext = None
|
|
overlay = None
|
|
runActions = None
|
|
|
|
"""
|
|
Player() Trigger Order
|
|
Player: onPlayBackStarted
|
|
Player: onAVChange (if playing)
|
|
Player: onAVStarted
|
|
Player: onPlayBackSeek (if seek)
|
|
Player: onAVChange (if changed)
|
|
Player: onPlayBackError
|
|
Player: onPlayBackEnded
|
|
Player: onPlayBackStopped
|
|
"""
|
|
|
|
def __init__(self, service=None):
|
|
xbmc.Player.__init__(self)
|
|
self.service = service
|
|
self.jsonRPC = service.jsonRPC
|
|
self.enableOverlay = SETTINGS.getSettingBool('Overlay_Enable')
|
|
self.infoOnChange = SETTINGS.getSettingBool('Enable_OnInfo')
|
|
self.disableTrakt = SETTINGS.getSettingBool('Disable_Trakt')
|
|
self.rollbackPlaycount = SETTINGS.getSettingBool('Rollback_Watched')
|
|
self.saveDuration = SETTINGS.getSettingBool('Store_Duration')
|
|
self.minDuration = SETTINGS.getSettingInt('Seek_Tolerance')
|
|
self.maxProgress = SETTINGS.getSettingInt('Seek_Threshold')
|
|
self.sleepTime = SETTINGS.getSettingInt('Idle_Timer')
|
|
self.runWhilePlaying = SETTINGS.getSettingBool('Run_While_Playing')
|
|
self.restartPercentage = SETTINGS.getSettingInt('Restart_Percentage')
|
|
self.OnNextMode = SETTINGS.getSettingInt('OnNext_Mode')
|
|
self.onNextPosition = SETTINGS.getSetting("OnNext_Position_XY")
|
|
|
|
|
|
def log(self, msg, level=xbmc.LOGDEBUG):
|
|
return log('%s: %s'%(self.__class__.__name__,msg),level)
|
|
|
|
|
|
def onPlayBackStarted(self):
|
|
self.pendingPlay = time.time()
|
|
self.lastSubState = BUILTIN.isSubtitle()
|
|
self.log('onPlayBackStarted, pendingPlay = %s'%(self.pendingPlay))
|
|
|
|
|
|
def onAVChange(self):
|
|
self.log('onAVChange')
|
|
|
|
|
|
def onAVStarted(self):
|
|
self.pendingPlay = -1
|
|
self.pendingStop = True
|
|
self.toggleOverlay(False)
|
|
self.pendingItem = self.getPlayerSysInfo()
|
|
self.isPseudoTV = self.pendingItem.get('isPseudoTV',False)
|
|
self.log('onAVStarted, pendingStop = %s, isPseudoTV = %s, pendingItem = %s'%(self.pendingStop,self.isPseudoTV,self.pendingItem))
|
|
self._onPlay(sysInfo=self.pendingItem)
|
|
|
|
|
|
def onPlayBackSeek(self, seek_time=None, seek_offset=None): #Kodi bug? `OnPlayBackSeek` no longer called by player during seek, issue limited to pvr?
|
|
self.log('onPlayBackSeek, seek_time = %s, seek_offset = %s'%(seek_time,seek_offset))
|
|
|
|
|
|
def onPlayBackError(self):
|
|
self.log('onPlayBackError')
|
|
self._onError()
|
|
|
|
|
|
def onPlayBackEnded(self):
|
|
self.log('onPlayBackEnded')
|
|
self.pendingStop = False
|
|
self.pendingPlay = -1
|
|
self._onChange()
|
|
|
|
|
|
def onPlayBackStopped(self):
|
|
self.log('onPlayBackStopped')
|
|
self.pendingStop = False
|
|
self.pendingPlay = -1
|
|
self._onStop()
|
|
|
|
|
|
def getPlayerSysInfo(self):
|
|
def __update(id, citem={}): #sysInfo from listitem maybe outdated, check with channels.json
|
|
channels = self.service.tasks.getVerifiedChannels()
|
|
for item in channels:
|
|
if item.get('id',random.random()) == id:
|
|
return combineDicts(citem,item)
|
|
return citem
|
|
|
|
# with self.service.lock: # Ensure thread safety
|
|
sysInfo = loadJSON(decodeString(self.getPlayerItem().getProperty('sysInfo')))
|
|
sysInfo['isPseudoTV'] = '@%s'%(slugify(ADDON_NAME)) in sysInfo.get('chid','')
|
|
sysInfo['chfile'] = BUILTIN.getInfoLabel('Filename','Player')
|
|
sysInfo['chfolder'] = BUILTIN.getInfoLabel('Folderpath','Player')
|
|
sysInfo['chpath'] = BUILTIN.getInfoLabel('Filenameandpath','Player')
|
|
|
|
if sysInfo['isPseudoTV']:
|
|
if not sysInfo.get('fitem'): sysInfo.update({'fitem':decodePlot(BUILTIN.getInfoLabel('Plot','VideoPlayer'))})
|
|
if not sysInfo.get('nitem'): sysInfo.update({'nitem':decodePlot(BUILTIN.getInfoLabel('NextPlot','VideoPlayer'))})
|
|
sysInfo.update({'citem':combineDicts(sysInfo.get('citem',{}),__update(sysInfo.get('citem',{}).get('id'))),'runtime':int(self.getPlayerTime())}) #still needed for adv. rules?
|
|
if not sysInfo.get('callback'): sysInfo['callback'] = self.jsonRPC.getCallback(sysInfo)
|
|
PROPERTIES.setEXTProperty('%s.lastPlayed.sysInfo'%(ADDON_ID),encodeString(dumpJSON(sysInfo)))
|
|
return sysInfo
|
|
|
|
|
|
def getPlayerItem(self):
|
|
try: return self.getPlayingItem()
|
|
except:
|
|
self.service.monitor.waitForAbort(0.1)
|
|
if self.isPlaying(): return self.getPlayerItem()
|
|
else: return xbmcgui.ListItem()
|
|
|
|
|
|
def getPlayerFile(self):
|
|
try: return self.getPlayingFile()
|
|
except: return self.sysInfo.get('fitem',{}).get('file')
|
|
|
|
|
|
def getPlayerTime(self):
|
|
try: return (self.getTimeLabel('Duration') or self.getTotalTime())
|
|
except: return (self.sysInfo.get('fitem',{}).get('runtime') or -1)
|
|
|
|
|
|
def getPlayedTime(self):
|
|
try: return (self.getTimeLabel('Time') or self.getTime()) #getTime retrieves Guide times not actual media time.
|
|
except: return -1
|
|
|
|
|
|
def getRemainingTime(self):
|
|
try: return self.getPlayerTime() - self.getPlayedTime()
|
|
except: return (self.getTimeLabel('TimeRemaining') or -1)
|
|
|
|
|
|
def getPlayerProgress(self):
|
|
try: return abs(int((self.getRemainingTime() / self.getPlayerTime()) * 100) - 100)
|
|
except: return int((BUILTIN.getInfoLabel('Progress','Player') or '-1'))
|
|
|
|
|
|
def getTimeLabel(self, prop: str='TimeRemaining') -> int and float: #prop='EpgEventElapsedTime'
|
|
if self.isPlaying(): return timeString2Seconds(BUILTIN.getInfoLabel('%s(hh:mm:ss)'%(prop),'Player'))
|
|
|
|
|
|
def setSubtitles(self, state: bool=True):
|
|
hasSubtitle = BUILTIN.hasSubtitle()
|
|
self.log('setSubtitles, show subtitles = %s, hasSubtitle = %s'%(state,hasSubtitle))
|
|
if not hasSubtitle: state = False
|
|
self.showSubtitles(state)
|
|
|
|
|
|
def _onPlay(self, sysInfo={}):
|
|
self.toggleBackground(False)
|
|
self.toggleOverlay(False)
|
|
self.toggleRestart(False)
|
|
self.toggleOnNext(False)
|
|
if self.isPseudoTV:
|
|
oldInfo = self.sysInfo
|
|
newChan = oldInfo.get('chid',random.random()) != sysInfo.get('chid')
|
|
self.log('_onPlay, [%s], mode = %s, isPlaylist = %s, new channel = %s'%(sysInfo.get('citem',{}).get('id'), sysInfo.get('mode'), sysInfo.get('isPlaylist',False), newChan))
|
|
if newChan: #New channel
|
|
self.runActions = RulesList([sysInfo.get('citem',{})]).runActions
|
|
self.sysInfo = self._runActions(RULES_ACTION_PLAYER_START, sysInfo.get('citem',{}), sysInfo, inherited=self)
|
|
self.toggleRestart(bool(self.restartPercentage))
|
|
PROPERTIES.setTrakt(self.disableTrakt)
|
|
self.setSubtitles(self.lastSubState) #todo allow rules to set sub preference per channel.
|
|
else: #New Program/Same Channel
|
|
self.sysInfo = sysInfo
|
|
if self.sysInfo.get('radio',False): timerit(BUILTIN.executebuiltin)(0.5,['ReplaceWindow(visualisation)'])
|
|
elif self.sysInfo.get('isPlaylist',False): timerit(BUILTIN.executebuiltin)(0.5,['ReplaceWindow(fullscreenvideo)'])
|
|
self.toggleInfo(self.infoOnChange)
|
|
|
|
self.jsonRPC.quePlaycount(oldInfo.get('fitem',{}),self.rollbackPlaycount)
|
|
self.jsonRPC._setRuntime(self.sysInfo.get('fitem',{}),self.sysInfo.get('runtime'),self.saveDuration)
|
|
|
|
|
|
def _onChange(self):
|
|
if self.sysInfo:
|
|
if not self.sysInfo.get('isPlaylist',False):
|
|
self.log('_onChange, [%s], isPlaylist = %s, callback = %s'%(self.sysInfo.get('citem',{}).get('id'),self.sysInfo.get('isPlaylist',False),self.sysInfo.get('callback')))
|
|
self.toggleBackground(self.enableOverlay)
|
|
timerit(BUILTIN.executebuiltin)(0.1,['PlayMedia(%s)'%(self.sysInfo.get('callback'))])
|
|
self.sysInfo = self._runActions(RULES_ACTION_PLAYER_CHANGE, self.sysInfo.get('citem',{}), self.sysInfo, inherited=self)
|
|
else:
|
|
self.toggleBackground(False)
|
|
self.toggleOverlay(False)
|
|
self.toggleRestart(False)
|
|
self.toggleOnNext(False)
|
|
self.toggleInfo(False)
|
|
|
|
|
|
def _onStop(self):
|
|
self.log('_onStop, id = %s'%(self.sysInfo.get('citem',{}).get('id')))
|
|
self.toggleBackground(False)
|
|
self.toggleOverlay(False)
|
|
self.toggleRestart(False)
|
|
self.toggleOnNext(False)
|
|
self.toggleInfo(False)
|
|
if self.sysInfo:
|
|
PROPERTIES.setTrakt(False)
|
|
self.jsonRPC.quePlaycount(self.sysInfo.get('fitem',{}),self.rollbackPlaycount)
|
|
if self.sysInfo.get('isPlaylist',False): xbmc.PlayList(xbmc.PLAYLIST_VIDEO).clear()
|
|
self.sysInfo = self._runActions(RULES_ACTION_PLAYER_STOP, self.sysInfo.get('citem',{}), {}, inherited=self)
|
|
|
|
|
|
def _onError(self): #todo evaluate potential for error handling.
|
|
self.log('_onError, playing file = %s'%(self.getPlayerFile()))
|
|
if self.isPseudoTV and SETTINGS.getSettingBool('Debug_Enable'):
|
|
DIALOG.notificationDialog(LANGUAGE(32000))
|
|
timerit(BUILTIN.executebuiltin)(0.5,['Number(0)'])
|
|
self.onPlayBackStopped()
|
|
|
|
|
|
def _runActions(self, action, citem={}, parameter=None, inherited=None):
|
|
if self.runActions: return self.runActions(action, citem, parameter, inherited)
|
|
else: return parameter
|
|
|
|
|
|
def toggleBackground(self, state: bool=SETTINGS.getSettingBool('Overlay_Enable')):
|
|
if state and self.background is None and self.service.monitor.isIdle:
|
|
BUILTIN.executebuiltin("Dialog.Close(all)")
|
|
self.background = Background(BACKGROUND_XML, ADDON_PATH, "default", player=self)
|
|
self.background.show()
|
|
elif not state and hasattr(self.background,'close'):
|
|
self.background = self.background.close()
|
|
else: return
|
|
self.log("toggleBackground, state = %s, background = %s"%(state,self.background))
|
|
|
|
|
|
def toggleOverlay(self, state: bool=SETTINGS.getSettingBool('Overlay_Enable')):
|
|
if state and self.overlay is None and self.isPlaying():
|
|
self.overlay = Overlay(player=self)
|
|
self.overlay.open()
|
|
elif not state and hasattr(self.overlay,'close'):
|
|
self.overlay = self.overlay.close()
|
|
else: return
|
|
self.log("toggleOverlay, state = %s, overlay = %s"%(state, self.overlay))
|
|
|
|
|
|
def toggleRestart(self, state: bool=bool(SETTINGS.getSettingInt('Restart_Percentage'))):
|
|
if state and self.restart is None and self.isPlaying():
|
|
self.restart = Restart(RESTART_XML, ADDON_PATH, "default", "1080i", player=self)
|
|
elif not state and hasattr(self.restart,'onClose'):
|
|
self.restart = self.restart.onClose()
|
|
else: return
|
|
self.log("toggleRestart, state = %s, restart = %s"%(state,self.restart))
|
|
|
|
|
|
def toggleOnNext(self, state: bool=bool(SETTINGS.getSettingInt('OnNext_Mode'))):
|
|
if state and self.onnext is None and self.isPlaying():
|
|
self.onnext = OnNext(ONNEXT_XML, ADDON_PATH, "default", "1080i", player=self, mode=self.OnNextMode, position=self.onNextPosition)
|
|
elif hasattr(self.onnext,'onClose'):
|
|
self.onnext = self.onnext.onClose()
|
|
else: return
|
|
self.log("toggleOnNext, state = %s, onnext = %s"%(state,self.onnext))
|
|
|
|
|
|
def toggleInfo(self, state: bool=SETTINGS.getSettingBool('Enable_OnInfo')):
|
|
if state and not BUILTIN.getInfoLabel('Genre','VideoPlayer') in FILLER_TYPE:
|
|
timerit(self.toggleInfo)(float(OSD_TIMER),[False])
|
|
BUILTIN.executebuiltin('ActivateWindow(fullscreeninfo)')
|
|
elif not state and BUILTIN.getInfoBool('IsVisible(fullscreeninfo)','Window'):
|
|
BUILTIN.executebuiltin('Action(back)')
|
|
BUILTIN.executebuiltin("Dialog.Close(fullscreeninfo)")
|
|
self.log('toggleInfo, state = %s'%(state))
|
|
|
|
|
|
class Monitor(xbmc.Monitor):
|
|
idleTime = 0
|
|
isIdle = False
|
|
|
|
def __init__(self, service=None):
|
|
self.log('__init__')
|
|
xbmc.Monitor.__init__(self)
|
|
self.service = service
|
|
self.jsonRPC = service.jsonRPC
|
|
|
|
|
|
def log(self, msg, level=xbmc.LOGDEBUG):
|
|
return log('%s: %s'%(self.__class__.__name__,msg),level)
|
|
|
|
|
|
def chkIdle(self):
|
|
def __chkIdle():
|
|
self.idleTime = BUILTIN.getIdle()
|
|
self.isIdle = bool(self.idleTime) | self.idleTime > FIFTEEN
|
|
self.log('__chkIdle, isIdle = %s, idleTime = %s'%(self.isIdle, self.idleTime))
|
|
|
|
def __chkResumeTime():
|
|
if self.service.player.sysInfo.get('isPlaylist',False):
|
|
file = self.service.player.getPlayingFile()
|
|
if self.service.player.sysInfo.get('fitem',{}).get('file') == file:
|
|
resume = {"position":self.service.player.getPlayedTime(),"total":self.service.player.getPlayerTime(),"file":file}
|
|
self.log('__chkResumeTime, resume = %s'%(resume))
|
|
self.service.player.sysInfo.setdefault('resume',{}).update(resume)
|
|
|
|
def __chkPlayback():
|
|
if self.service.player.pendingPlay > 0:
|
|
if not BUILTIN.isBusyDialog() and (time.time() - self.service.player.pendingPlay) > 60: self.service.player.onPlayBackError()
|
|
|
|
def __chkSleepTimer():
|
|
if self.service.player.sleepTime > 0 and (self.idleTime > (self.service.player.sleepTime * 10800)):
|
|
if not PROPERTIES.isRunning('__chkSleepTimer'):
|
|
with PROPERTIES.chkRunning('__chkSleepTimer'):
|
|
if self.sleepTimer(): self.service.player.stop()
|
|
|
|
def __chkBackground():
|
|
remaining = floor(self.service.player.getRemainingTime())
|
|
if self.isIdle and remaining <= 45:
|
|
self.log('__chkBackground, isIdle = %s, remaining = %s'%(self.isIdle, remaining))
|
|
self.service.player.toggleBackground(self.service.player.enableOverlay)
|
|
|
|
def __chkOverlay():
|
|
played = ceil(self.service.player.getPlayedTime())
|
|
if self.isIdle and played > OSD_TIMER:
|
|
self.log('__chkOverlay, isIdle = %s, played = %s'%(self.isIdle, played))
|
|
self.service.player.toggleOverlay(self.service.player.enableOverlay)
|
|
|
|
def __chkOnNext():
|
|
played = self.service.player.getPlayedTime()
|
|
remaining = floor(self.service.player.getRemainingTime())
|
|
totalTime = int(self.service.player.getPlayerTime() * (self.service.player.maxProgress / 100))
|
|
threshold = abs((totalTime - (totalTime * .75)) - (ONNEXT_TIMER*3))
|
|
intTime = roundupDIV(threshold,3)
|
|
if self.isIdle and played > self.service.player.minDuration and (remaining <= threshold and remaining >= intTime) and self.service.player.background is None:
|
|
self.log('__chkOnNext, isIdle = %s, played = %s, remaining = %s'%(self.isIdle, played, remaining))
|
|
self.service.player.toggleOnNext(bool(self.service.player.OnNextMode))
|
|
|
|
Thread(target=__chkIdle).start()
|
|
if self.service.player.isPlaying() and self.service.player.isPseudoTV:
|
|
Thread(target=__chkBackground).start()
|
|
__chkResumeTime()
|
|
__chkSleepTimer()
|
|
__chkPlayback()
|
|
__chkOverlay()
|
|
__chkOnNext()
|
|
|
|
|
|
def sleepTimer(self):
|
|
self.log('sleepTimer')
|
|
sec = 0
|
|
cnx = False
|
|
inc = int(100/FIFTEEN)
|
|
xbmc.playSFX(NOTE_WAV)
|
|
dia = DIALOG.progressDialog(message=LANGUAGE(30078))
|
|
while not self.abortRequested() and (sec < FIFTEEN):
|
|
sec += 1
|
|
msg = '%s\n%s'%(LANGUAGE(32039),LANGUAGE(32040)%(FIFTEEN-sec))
|
|
dia = DIALOG.progressDialog((inc*sec),dia, msg)
|
|
if self.waitForAbort(1.0) or dia is None:
|
|
cnx = True
|
|
break
|
|
DIALOG.progressDialog(100,dia)
|
|
return not bool(cnx)
|
|
|
|
|
|
def onNotification(self, sender, method, data):
|
|
self.log("onNotification, sender %s - method: %s - data: %s" % (sender, method, data))
|
|
|
|
|
|
def onSettingsChanged(self):
|
|
self.log('onSettingsChanged')
|
|
if self.service: timerit(self.onSettingsChangedTimer)(FIFTEEN)
|
|
|
|
|
|
def onSettingsChangedTimer(self):
|
|
self.log('onSettingsChangedTimer')
|
|
self.service.tasks._que(self._onSettingsChanged,1)
|
|
|
|
|
|
def _onSettingsChanged(self):
|
|
with PROPERTIES.interruptActivity():
|
|
self.log('_onSettingsChanged')
|
|
self.service.currentSettings = self.service.tasks.chkSettingsChange(self.service.currentSettings) #check for settings change, take action if needed
|
|
|
|
|
|
class Service():
|
|
lock = Lock()
|
|
currentSettings = []
|
|
pendingSuspend = PROPERTIES.setPendingSuspend(False)
|
|
pendingInterrupt = PROPERTIES.setPendingInterrupt(False)
|
|
pendingShutdown = PROPERTIES.setPendingShutdown(False)
|
|
pendingRestart = PROPERTIES.setPendingRestart(False)
|
|
|
|
def __init__(self):
|
|
self.log('__init__')
|
|
self.jsonRPC = JSONRPC(service=self)
|
|
self.player = Player(service=self)
|
|
self.monitor = Monitor(service=self)
|
|
self.tasks = Tasks(service=self)
|
|
|
|
self.tasks.service = self
|
|
self.monitor.service = self
|
|
self.player.service = self
|
|
self.jsonRPC.service = self
|
|
|
|
|
|
def log(self, msg, level=xbmc.LOGDEBUG):
|
|
return log('%s: %s'%(self.__class__.__name__,msg),level)
|
|
|
|
|
|
def __playing(self) -> bool:
|
|
if self.player.isPlaying() and not self.player.runWhilePlaying: return True
|
|
return False
|
|
|
|
|
|
def __shutdown(self, wait=1.0) -> bool:
|
|
pendingShutdown = (self.monitor.waitForAbort(wait) | PROPERTIES.isPendingShutdown())
|
|
if self.pendingShutdown != pendingShutdown:
|
|
self.pendingShutdown = pendingShutdown
|
|
self.log('__shutdown, pendingShutdown = %s, wait = %s'%(self.pendingShutdown,wait))
|
|
return self.pendingShutdown
|
|
|
|
|
|
def __restart(self) -> bool:
|
|
pendingRestart = (self.pendingRestart | PROPERTIES.isPendingRestart())
|
|
if self.pendingRestart != pendingRestart:
|
|
self.pendingRestart = pendingRestart
|
|
self.log('__restart, pendingRestart = %s'%(self.pendingRestart))
|
|
return self.pendingRestart
|
|
|
|
|
|
def _interrupt(self) -> bool: #break
|
|
pendingInterrupt = (self.pendingShutdown | self.pendingRestart | self.__playing() | PROPERTIES.isInterruptActivity() | BUILTIN.isScanning())
|
|
if pendingInterrupt != self.pendingInterrupt:
|
|
self.pendingInterrupt = PROPERTIES.setPendingInterrupt(pendingInterrupt)
|
|
self.log('_interrupt, pendingInterrupt = %s'%(self.pendingInterrupt))
|
|
return self.pendingInterrupt
|
|
|
|
|
|
def _suspend(self) -> bool: #continue
|
|
pendingSuspend = (PROPERTIES.isSuspendActivity() | BUILTIN.isSettingsOpened())
|
|
if pendingSuspend != self.pendingSuspend:
|
|
self.pendingSuspend = PROPERTIES.setPendingSuspend(pendingSuspend)
|
|
self.log('_suspend, pendingSuspend = %s'%(self.pendingSuspend))
|
|
return self.pendingSuspend
|
|
|
|
|
|
def __tasks(self):
|
|
# if SETTINGS.hasWizardRun():
|
|
self.tasks._chkEpochTimer('chkQueTimer',self.tasks._chkQueTimer,FIFTEEN)
|
|
|
|
|
|
def _start(self):
|
|
self.log('_start')
|
|
if DIALOG.notificationWait('%s...'%(LANGUAGE(32054)),wait=OSD_TIMER):
|
|
self.tasks._initialize()
|
|
if self.player.isPlaying(): self.player.onAVStarted()
|
|
while not self.monitor.abortRequested():
|
|
self.monitor.chkIdle()
|
|
if self.__shutdown(): break
|
|
elif self.__restart(): break
|
|
else: self.__tasks()
|
|
return self._stop(self.pendingRestart)
|
|
|
|
|
|
def _stop(self, pendingRestart: bool=False):
|
|
if self.player.isPlaying(): self.player.onPlayBackStopped()
|
|
with PROPERTIES.interruptActivity():
|
|
for thread in thread_enumerate():
|
|
if thread.name != "MainThread" and thread.is_alive():
|
|
if hasattr(thread, 'cancel'): thread.cancel()
|
|
try: thread.join(1.0)
|
|
except: pass
|
|
self.log('_stop, closing %s...'%(thread.name))
|
|
return pendingRestart |