diff --git a/resources/lib/app/__init__.py b/resources/lib/app/__init__.py index 42783eb7..c224d7b9 100644 --- a/resources/lib/app/__init__.py +++ b/resources/lib/app/__init__.py @@ -5,19 +5,12 @@ Used to save PKC's application state and share between modules. Be careful if you invoke another PKC Python instance (!!) when e.g. PKC.movies is called """ from __future__ import absolute_import, division, unicode_literals -from copy import deepcopy -from logging import getLogger - -import xbmc - from .account import Account from .application import App from .connection import Connection from .libsync import Sync from .playstate import PlayState -LOG = getLogger('PLEX.app') - ACCOUNT = None APP = None CONN = None @@ -37,56 +30,3 @@ def init(entrypoint=False): SYNC = Sync(entrypoint) if not entrypoint: PLAYSTATE = PlayState() - - -def _check_thread_suspension(): - global ACCOUNT, APP, SYNC - threads_to_be_suspended = set() - if SYNC.background_sync_disabled: - threads_to_be_suspended.add(APP.pms_websocket) - if not SYNC.enable_alexa or not ACCOUNT.plex_token: - threads_to_be_suspended.add(APP.alexa_websocket) - if ACCOUNT.restricted_user: - threads_to_be_suspended.add(APP.pms_websocket) - threads_to_be_suspended.add(APP.alexa_websocket) - if None in threads_to_be_suspended: - threads_to_be_suspended.remove(None) - return threads_to_be_suspended - - -def resume_threads(): - """ - Resume all thread activity with or without blocking. Won't resume websocket - threads if they should not be resumed - Returns True only if PKC shutdown requested - """ - global APP - threads = deepcopy(APP.threads) - threads_to_be_suspended = _check_thread_suspension() - LOG.debug('Not resuming the following threads: %s', threads_to_be_suspended) - for thread in threads_to_be_suspended: - try: - threads.remove(thread) - except ValueError: - pass - LOG.debug('Thus resuming the following threads: %s', threads) - for thread in threads: - thread.resume() - return xbmc.Monitor().abortRequested() - - -def check_websocket_threads_suspend(): - threads_to_be_suspended = _check_thread_suspension() - for thread in threads_to_be_suspended: - thread.suspend() - - -def suspend_threads(block=True): - global APP - APP.suspend_threads(block=block) - - -def reload(): - global APP, SYNC - APP.reload() - SYNC.reload() diff --git a/resources/lib/app/application.py b/resources/lib/app/application.py index eb8f5a29..a676efbe 100644 --- a/resources/lib/app/application.py +++ b/resources/lib/app/application.py @@ -22,7 +22,7 @@ class App(object): if entrypoint: self.load_entrypoint() else: - self.reload() + self.load() # Quit PKC? self.stop_pkc = False # This will suspend the main thread also @@ -51,8 +51,6 @@ class App(object): self.fanart_thread = None # Instance of ImageCachingThread() self.caching_thread = None - self.pms_websocket = None - self.alexa_websocket = None @property def is_playing(self): @@ -104,48 +102,6 @@ class App(object): except AttributeError: pass - def register_pms_websocket(self, thread): - self.pms_websocket = thread - self.threads.append(thread) - - def deregister_pms_websocket(self, thread): - self.pms_websocket.unblock_callers() - self.pms_websocket = None - self.threads.remove(thread) - - def suspend_pms_websocket(self, block=True): - try: - self.pms_websocket.suspend(block=block) - except AttributeError: - pass - - def resume_pms_websocket(self): - try: - self.pms_websocket.resume() - except AttributeError: - pass - - def register_alexa_websocket(self, thread): - self.alexa_websocket = thread - self.threads.append(thread) - - def deregister_alexa_websocket(self, thread): - self.alexa_websocket.unblock_callers() - self.alexa_websocket = None - self.threads.remove(thread) - - def suspend_alexa_websocket(self, block=True): - try: - self.alexa_websocket.suspend(block=block) - except AttributeError: - pass - - def resume_alexa_websocket(self): - try: - self.alexa_websocket.resume() - except AttributeError: - pass - def register_thread(self, thread): """ Hit with thread [backgroundthread.Killablethread instance] to register @@ -180,6 +136,16 @@ class App(object): break return xbmc.Monitor().abortRequested() + def resume_threads(self): + """ + Resume all thread activity with or without blocking. + Returns True only if PKC shutdown requested + """ + LOG.debug('Resuming threads: %s', self.threads) + for thread in self.threads: + thread.resume() + return xbmc.Monitor().abortRequested() + def stop_threads(self, block=True): """ Stop all threads. Will block until all threads are stopped @@ -194,7 +160,7 @@ class App(object): if xbmc.sleep(100): return True - def reload(self): + def load(self): # Number of items to fetch and display in widgets self.fetch_pms_item_number = int(utils.settings('fetch_pms_item_number')) # Hack to force Kodi widget for "in progress" to show up if it was empty diff --git a/resources/lib/app/libsync.py b/resources/lib/app/libsync.py index ce658a21..d018752b 100644 --- a/resources/lib/app/libsync.py +++ b/resources/lib/app/libsync.py @@ -81,8 +81,6 @@ class Sync(object): # re-built if sections are set a-new self.section_ids = set() - self.enable_alexa = None - self.load() @property @@ -115,19 +113,11 @@ class Sync(object): self.show_extras_instead_of_playing_trailer = utils.settings('showExtrasInsteadOfTrailer') == 'true' self.sync_specific_plex_playlists = utils.settings('syncSpecificPlexPlaylists') == 'true' self.sync_specific_kodi_playlists = utils.settings('syncSpecificKodiPlaylists') == 'true' - self.sync_thread_number = int(utils.settings('syncThreadNumber')) - self.reload() - - def reload(self): - """ - Any settings unrelated to syncs to the Kodi database - can thus be - safely reset without a Kodi reboot - """ self.sync_dialog = utils.settings('dbSyncIndicator') == 'true' self.full_sync_intervall = int(utils.settings('fullSyncInterval')) * 60 self.background_sync_disabled = utils.settings('enableBackgroundSync') == 'false' self.backgroundsync_saftymargin = int(utils.settings('backgroundsync_saftyMargin')) + self.sync_thread_number = int(utils.settings('syncThreadNumber')) self.image_sync_notifications = utils.settings('imageSyncNotifications') == 'true' - self.enable_alexa = utils.settings('enable_alexa') == 'true' diff --git a/resources/lib/service_entry.py b/resources/lib/service_entry.py index 15fa67c1..970aa801 100644 --- a/resources/lib/service_entry.py +++ b/resources/lib/service_entry.py @@ -116,7 +116,7 @@ class Service(object): # PMS was online before LOG.warn("Plex Media Server went offline") app.CONN.online = False - app.suspend_threads() + app.APP.suspend_threads() LOG.debug('Threads suspended') if utils.settings('show_pms_offline') == 'true': utils.dialog('notification', @@ -154,7 +154,7 @@ class Service(object): if app.ACCOUNT.authenticated: # Server got offline when we were authenticated. # Hence resume threads - app.resume_threads() + app.APP.resume_threads() app.CONN.online = True finally: self.connection_check_running = False @@ -165,7 +165,7 @@ class Service(object): Ensures that lib sync threads are suspended; signs out user """ LOG.info('Log-out requested') - app.suspend_threads() + app.APP.suspend_threads() LOG.info('Successfully suspended threads') app.ACCOUNT.log_out() LOG.info('User has been logged out') @@ -248,10 +248,7 @@ class Service(object): icon='{plex}', time=2000, sound=False) - app.reload() - app.check_websocket_threads_suspend() - app.resume_threads() - + app.APP.resume_threads() self.auth_running = False def enter_new_pms_address(self): @@ -293,7 +290,7 @@ class Service(object): # "Unauthorized for PMS" utils.dialog('notification', utils.lang(29999), utils.lang(30017)) return - app.suspend_threads() + app.APP.suspend_threads() from .library_sync import sections try: # Get newest sections from the PMS @@ -303,14 +300,14 @@ class Service(object): library_sync.force_full_sync() app.SYNC.run_lib_scan = 'full' finally: - app.resume_threads() + app.APP.resume_threads() def reset_playlists_and_nodes(self): """ Resets the Kodi playlists and nodes for all the PKC libraries by deleting all of them first, then rewriting everything """ - app.suspend_threads() + app.APP.suspend_threads() from .library_sync import sections try: sections.clear_window_vars() @@ -332,7 +329,7 @@ class Service(object): icon='{plex}', sound=False) finally: - app.resume_threads() + app.APP.resume_threads() xbmc.executebuiltin('ReloadSkin()') def _do_auth(self): @@ -365,7 +362,7 @@ class Service(object): if not user: LOG.info('No user received') app.APP.suspend = True - app.suspend_threads() + app.APP.suspend_threads() LOG.debug('Threads suspended') return False username = user.title @@ -401,7 +398,7 @@ class Service(object): else: LOG.debug('Suspending threads') app.APP.suspend = True - app.suspend_threads() + app.APP.suspend_threads() LOG.debug('Threads suspended') return False elif res >= 400: @@ -538,7 +535,8 @@ class Service(object): self.sync.start() self.plexcompanion.start() self.playqueue.start() - self.alexa.start() + if utils.settings('enable_alexa') == 'true': + self.alexa.start() xbmc.sleep(100) diff --git a/resources/lib/utils.py b/resources/lib/utils.py index 53e8d2b9..72127168 100644 --- a/resources/lib/utils.py +++ b/resources/lib/utils.py @@ -603,7 +603,7 @@ def reset(ask_user=True): return from . import app # first stop any db sync - app.suspend_threads() + app.APP.suspend_threads() # Reset all PlexKodiConnect Addon settings? (this is usually NOT # recommended and unnecessary!) if ask_user and yesno_dialog(lang(29999), lang(39603)): diff --git a/resources/lib/websocket_client.py b/resources/lib/websocket_client.py index 7d5986fb..8ff767b1 100644 --- a/resources/lib/websocket_client.py +++ b/resources/lib/websocket_client.py @@ -22,11 +22,6 @@ class WebSocket(backgroundthread.KillableThread): self.sleeptime = 0.0 super(WebSocket, self).__init__() - def close_websocket(self): - if self.ws is not None: - self.ws.close() - self.ws = None - def process(self, opcode, message): raise NotImplementedError @@ -61,11 +56,26 @@ class WebSocket(backgroundthread.KillableThread): if self.sleeptime < 6: self.sleeptime += 1.0 + def run(self): + LOG.info("----===## Starting %s ##===----", self.__class__.__name__) + app.APP.register_thread(self) + try: + self._run() + finally: + # Close websocket connection on shutdown + if self.ws is not None: + self.ws.close() + app.APP.deregister_thread(self) + LOG.info("##===---- %s Stopped ----===##", self.__class__.__name__) + def _run(self): while not self.should_cancel(): # In the event the server goes offline if self.should_suspend(): - self.close_websocket() + # Set in service.py + if self.ws is not None: + self.ws.close() + self.ws = None if self.wait_while_suspended(): # Abort was requested while waiting. We should exit return @@ -122,7 +132,8 @@ class WebSocket(backgroundthread.KillableThread): import traceback LOG.error("%s: Traceback:\n%s", self.__class__.__name__, traceback.format_exc()) - self.close_websocket() + if self.ws is not None: + self.ws.close() self.ws = None @@ -130,15 +141,11 @@ class PMS_Websocket(WebSocket): """ Websocket connection with the PMS for Plex Companion """ - def run(self): - LOG.info("----===## Starting Websocket ##===----") - app.APP.register_pms_websocket(self) - try: - self._run() - finally: - self.close_websocket() - app.APP.deregister_pms_websocket(self) - LOG.info("##===---- Websocket Stopped ----===##") + def should_suspend(self): + """ + Returns True if the thread is suspended + """ + return self._suspended or app.SYNC.background_sync_disabled def getUri(self): if self.redirect_uri: @@ -199,15 +206,13 @@ class Alexa_Websocket(WebSocket): """ Websocket connection to talk to Amazon Alexa. """ - def run(self): - LOG.info("----===## Starting Alexa Websocket ##===----") - app.APP.register_alexa_websocket(self) - try: - self._run() - finally: - self.close_websocket() - app.APP.deregister_alexa_websocket(self) - LOG.info("##===---- Alexa Websocket Stopped ----===##") + def should_suspend(self): + """ + Overwrite method since we need to check for plex token + """ + return (self._suspended or + not app.ACCOUNT.plex_token or + app.ACCOUNT.restricted_user) def getUri(self): if self.redirect_uri: