From 6dc436da9153b06b0e2c043b8ddede14551bd158 Mon Sep 17 00:00:00 2001 From: croneter Date: Sun, 25 Nov 2018 17:03:19 +0100 Subject: [PATCH] Rewire PKC startup and authentication mechanism --- default.py | 17 +- .../resource.language.en_gb/strings.po | 2 +- resources/lib/app/account.py | 17 +- resources/lib/app/application.py | 13 +- resources/lib/app/connection.py | 7 +- resources/lib/app/libsync.py | 3 - resources/lib/backgroundthread.py | 11 +- resources/lib/downloadutils.py | 57 ++-- resources/lib/initialsetup.py | 13 +- resources/lib/kodimonitor.py | 7 +- resources/lib/library_sync/common.py | 2 +- resources/lib/library_sync/fanart.py | 2 +- resources/lib/library_sync/websocket.py | 2 +- resources/lib/playqueue.py | 2 +- resources/lib/plex_companion.py | 2 +- resources/lib/service_entry.py | 314 +++++++++--------- resources/lib/sync.py | 2 +- resources/lib/websocket_client.py | 2 +- 18 files changed, 243 insertions(+), 232 deletions(-) diff --git a/default.py b/default.py index 4b8ab071..ac6ae083 100644 --- a/default.py +++ b/default.py @@ -101,17 +101,12 @@ class Main(): entrypoint.switch_plex_user() elif mode in ('manualsync', 'repair'): - if pickler.pickl_window('plex_online') != 'true': - # Server is not online, do not run the sync - utils.messageDialog(utils.lang(29999), utils.lang(39205)) - log.error('Not connected to a PMS.') - else: - if mode == 'repair': - log.info('Requesting repair lib sync') - utils.plex_command('repair-scan') - elif mode == 'manualsync': - log.info('Requesting full library scan') - utils.plex_command('full-scan') + if mode == 'repair': + log.info('Requesting repair lib sync') + utils.plex_command('repair-scan') + elif mode == 'manualsync': + log.info('Requesting full library scan') + utils.plex_command('full-scan') elif mode == 'texturecache': log.info('Requesting texture caching of all textures') diff --git a/resources/language/resource.language.en_gb/strings.po b/resources/language/resource.language.en_gb/strings.po index ab68e551..19eee613 100644 --- a/resources/language/resource.language.en_gb/strings.po +++ b/resources/language/resource.language.en_gb/strings.po @@ -619,7 +619,7 @@ msgid "Invalid username or password" msgstr "" msgctxt "#33010" -msgid "Your Plex token is no longer valid. Logging-out of plex.tv" +msgid "User is unauthorized for server {0}" msgstr "" msgctxt "#33011" diff --git a/resources/lib/app/account.py b/resources/lib/app/account.py index 5e824779..00b19418 100644 --- a/resources/lib/app/account.py +++ b/resources/lib/app/account.py @@ -30,6 +30,9 @@ class Account(object): def load(self): LOG.debug('Loading account settings') + # User name we used to sign in to plex.tv + self.plex_login = utils.settings('plexLogin') or None + self.plex_login_id = utils.settings('plexid') or None # plex.tv username self.plex_username = utils.settings('username') or None # Plex ID of that user (e.g. for plex.tv) as a STRING @@ -62,13 +65,17 @@ class Account(object): LOG.debug('User is restricted Home user: %s', self.restricted_user) def log_out(self): - LOG.debug('Logging-out user') + LOG.debug('Logging-out user %s', self.plex_username) self.plex_username = None self.plex_user_id = None self.pms_token = None self.avatar = None self.restricted_user = None self.authenticated = False + try: + self._session.close() + except AttributeError: + pass self._session = None utils.settings('username', value='') @@ -91,6 +98,12 @@ class Account(object): self.avatar = None self.restricted_user = None self.authenticated = False + self.plex_login = None + self.plex_login_id = None + try: + self._session.close() + except AttributeError: + pass self._session = None utils.settings('username', value='') @@ -99,6 +112,8 @@ class Account(object): utils.settings('plexToken', value='') utils.settings('accessToken', value='') utils.settings('plexAvatar', value='') + utils.settings('plexLogin', value='') + utils.settings('plexid', value='') utils.window('plex_restricteduser', clear=True) utils.window('plex_token', clear=True) diff --git a/resources/lib/app/application.py b/resources/lib/app/application.py index 365b059f..10e9dad6 100644 --- a/resources/lib/app/application.py +++ b/resources/lib/app/application.py @@ -17,7 +17,10 @@ class App(object): return # Quit PKC? self.stop_pkc = False - + # Shall we completely suspend PKC and our threads? + self.suspend = False + # Shall we only suspend threads? + self._suspend_threads = False # Need to lock all methods and functions messing with Plex Companion subscribers self.lock_subscriber = RLock() # Need to lock everything messing with Kodi/PKC playqueues @@ -43,3 +46,11 @@ class App(object): # Hack to force Kodi widget for "in progress" to show up if it was empty # before self.force_reload_skin = utils.settings('forceReloadSkinOnPlaybackStop') == 'true' + + @property + def suspend_threads(self): + return self._suspend_threads or self.suspend + + @suspend_threads.setter + def suspend_threads(self, value): + self._suspend_threads = value diff --git a/resources/lib/app/connection.py b/resources/lib/app/connection.py index 5acbc9f3..6ce16d2c 100644 --- a/resources/lib/app/connection.py +++ b/resources/lib/app/connection.py @@ -16,12 +16,6 @@ class Connection(object): return # TODO: Delete self.pms_server = None - # Plex Media Server Status - along with window('plex_serverStatus') - # Values: - # 'Stop': set if e.g. - # '401': Token has been revoked - PKC yet to delete tokens - # 'Auth': - self.pms_status = False # Token passed along, e.g. if playback initiated by Plex Companion. Might be # another user playing something! Token identifies user self.plex_transient_token = None @@ -61,6 +55,7 @@ class Connection(object): else: self.server = 'http://%s:%s' % (self.host, self.port) utils.window('pms_server', value=self.server) + self.online = False LOG.debug('Set server %s (%s) to %s', self.server_name, self.machine_identifier, self.server) diff --git a/resources/lib/app/libsync.py b/resources/lib/app/libsync.py index 88c1237f..10e1a0d9 100644 --- a/resources/lib/app/libsync.py +++ b/resources/lib/app/libsync.py @@ -12,9 +12,6 @@ class Sync(object): return # Do we need to run a special library scan? self.run_lib_scan = None - # Usually triggered by another Python instance - will have to be set (by - # polling window) through e.g. librarysync thread - self.suspend_library_thread = False # Set if user decided to cancel sync self.stop_sync = False # Set during media playback if PKC should not do any syncs. Will NOT diff --git a/resources/lib/backgroundthread.py b/resources/lib/backgroundthread.py index 055f66ef..e27815e1 100644 --- a/resources/lib/backgroundthread.py +++ b/resources/lib/backgroundthread.py @@ -160,15 +160,18 @@ class Task(object): return not self.finished and not self._canceled -def FunctionAsTask(Task): - def __init__(self, function, *args, **kwargs): - self.function = function +class FunctionAsTask(Task): + def __init__(self, function, callback, *args, **kwargs): + self._function = function + self._callback = callback self._args = args self._kwargs = kwargs super(FunctionAsTask, self).__init__() def run(self): - self.function(*self._args, **self._kwargs) + result = self._function(*self._args, **self._kwargs) + if self._callback: + self._callback(result) class MutablePriorityQueue(Queue.PriorityQueue): diff --git a/resources/lib/downloadutils.py b/resources/lib/downloadutils.py index c1ed884b..e3ba333a 100644 --- a/resources/lib/downloadutils.py +++ b/resources/lib/downloadutils.py @@ -28,9 +28,11 @@ class DownloadUtils(): _shared_state = {} # How many failed attempts before declaring PMS dead? - connectionAttempts = 1 + connection_attempts = 1 + count_error = 0 # How many 401 returns before declaring unauthorized? - unauthorizedAttempts = 2 + unauthorized_attempts = 2 + count_unauthorized = 0 # How long should we wait for an answer from the timeout = 30.0 @@ -69,11 +71,9 @@ class DownloadUtils(): self.setSSL() # Counters to declare PMS dead or unauthorized - # Use window variables because start of movies will be called with a - # new plugin instance - it's impossible to share data otherwise if reset is True: - utils.window('countUnauthorized', value='0') - utils.window('countError', value='0') + self.count_error = 0 + self.count_unauthorized = 0 # Retry connections to the server self.s.mount("http://", requests.adapters.HTTPAdapter(max_retries=1)) @@ -210,9 +210,9 @@ class DownloadUtils(): else: # We COULD contact the PMS, hence it ain't dead if authenticate is True: - utils.window('countError', value='0') + self.count_error = 0 if r.status_code != 401: - utils.window('countUnauthorized', value='0') + self.count_unauthorized = 0 if r.status_code == 204: # No body in the response @@ -230,22 +230,16 @@ class DownloadUtils(): LOG.info(r.text) if '401 Unauthorized' in r.text: # Truly unauthorized - utils.window( - 'countUnauthorized', - value=str(int(utils.window('countUnauthorized')) + 1)) - if (int(utils.window('countUnauthorized')) >= - self.unauthorizedAttempts): + self.count_unauthorized += 1 + if self.count_unauthorized >= self.unauthorized_attempts: LOG.warn('We seem to be truly unauthorized for PMS' ' %s ', url) - if app.CONN.pms_status not in ('401', 'Auth'): - # Tell others token has been revoked. - LOG.debug('Setting PMS server status to ' - 'unauthorized') - app.CONN.pms_status = '401' - utils.dialog('notification', - utils.lang(29999), - utils.lang(30017), - icon='{error}') + # Unauthorized access, user no longer has access + app.ACCOUNT.log_out() + utils.dialog('notification', + utils.lang(29999), + utils.lang(30017), + icon='{error}') else: # there might be other 401 where e.g. PMS under strain LOG.info('PMS might only be under strain') @@ -284,7 +278,7 @@ class DownloadUtils(): elif r.status_code == 403: # E.g. deleting a PMS item LOG.warn('PMS sent 403: Forbidden error for url %s', url) - return None + return else: r.encoding = 'utf-8' LOG.warn('Unknown answer from PMS %s with status code %s. ', @@ -294,14 +288,9 @@ class DownloadUtils(): # And now deal with the consequences of the exceptions if authenticate is True: # Make the addon aware of status - try: - utils.window('countError', - value=str(int(utils.window('countError')) + 1)) - if int(utils.window('countError')) >= self.connectionAttempts: - LOG.warn('Failed to connect to %s too many times. ' - 'Declare PMS dead', url) - utils.window('plex_online', value="false") - except ValueError: - # 'countError' not yet set - pass - return None + self.count_error += 1 + if self.count_error >= self.connection_attempts: + LOG.warn('Failed to connect to %s too many times. ' + 'Declare PMS dead', url) + app.CONN.online = False + return diff --git a/resources/lib/initialsetup.py b/resources/lib/initialsetup.py index 470a6828..24924185 100644 --- a/resources/lib/initialsetup.py +++ b/resources/lib/initialsetup.py @@ -54,8 +54,8 @@ class InitialSetup(object): # Get Plex credentials from settings file, if they exist plexdict = PF.GetPlexLoginFromSettings() self.plex_login = plexdict['plexLogin'] + self.plex_login_id = plexdict['plexid'] self.plex_token = plexdict['plexToken'] - self.plexid = plexdict['plexid'] # Token for the PMS, not plex.tv self.pms_token = utils.settings('accessToken') if self.plex_token: @@ -66,7 +66,7 @@ class InitialSetup(object): Writes Plex username, token to plex.tv and Plex id to PKC settings """ utils.settings('username', value=self.plex_login or '') - utils.settings('userid', value=self.plexid or '') + utils.settings('userid', value=self.plex_login_id or '') utils.settings('plexToken', value=self.plex_token or '') def enter_new_pms_address(self): @@ -117,14 +117,11 @@ class InitialSetup(object): Returns True if successful, or False if not """ - try: - user = plex_tv.sign_in_with_pin() - except: - utils.ERROR() + user = plex_tv.sign_in_with_pin() if user: self.plex_login = user.username self.plex_token = user.authToken - self.plexid = user.id + self.plex_login_id = user.id return True return False @@ -600,7 +597,7 @@ class InitialSetup(object): goto_settings = utils.yesno_dialog(utils.lang(29999), utils.lang(39017)) if goto_settings: - state.PMS_STATUS = 'Stop' + app.APP.suspend = True executebuiltin( 'Addon.OpenSettings(plugin.video.plexkodiconnect)') elif reboot is True: diff --git a/resources/lib/kodimonitor.py b/resources/lib/kodimonitor.py index 5e24f390..dde7c1e1 100644 --- a/resources/lib/kodimonitor.py +++ b/resources/lib/kodimonitor.py @@ -54,6 +54,8 @@ class KodiMonitor(xbmc.Monitor): Monitor the PKC settings for changes made by the user """ LOG.debug('PKC settings change detected') + # Assume that the user changed something so we can try to reconnect + app.APP.suspend = False def onNotification(self, sender, method, data): """ @@ -129,11 +131,10 @@ class KodiMonitor(xbmc.Monitor): elif method == "System.OnSleep": # Connection is going to sleep LOG.info("Marking the server as offline. SystemOnSleep activated.") - utils.window('plex_online', value="sleep") elif method == "System.OnWake": # Allow network to wake up self.waitForAbort(10) - utils.window('plex_online', value="false") + app.CONN.online = False elif method == "GUI.OnScreensaverDeactivated": if utils.settings('dbSyncScreensaver') == "true": self.waitForAbort(5) @@ -511,7 +512,7 @@ def _record_playstate(status, ended): xbmc.getCondVisibility('Window.IsVisible(Home.xml)')): LOG.debug('Refreshing skin to update widgets') xbmc.executebuiltin('ReloadSkin()') - task = backgroundthread.FunctionAsTask(_clean_file_table) + task = backgroundthread.FunctionAsTask(function=_clean_file_table) backgroundthread.BGThreader.addTasksToFront(task) diff --git a/resources/lib/library_sync/common.py b/resources/lib/library_sync/common.py index 867b7392..8e7477b5 100644 --- a/resources/lib/library_sync/common.py +++ b/resources/lib/library_sync/common.py @@ -9,7 +9,7 @@ from .. import app class libsync_mixin(object): def isCanceled(self): return (self._canceled or app.APP.stop_pkc or app.SYNC.stop_sync or - app.SYNC.suspend_library_thread or app.SYNC.suspend_sync) + app.APP.suspend_threads or app.SYNC.suspend_sync) def update_kodi_library(video=True, music=True): diff --git a/resources/lib/library_sync/fanart.py b/resources/lib/library_sync/fanart.py index 3fcb916e..7c0c6416 100644 --- a/resources/lib/library_sync/fanart.py +++ b/resources/lib/library_sync/fanart.py @@ -18,7 +18,7 @@ PREFER_KODI_COLLECTION_ART = utils.settings('PreferKodiCollectionArt') == 'false def suspends(): - return (app.SYNC.suspend_library_thread or + return (app.APP.suspend_threads or app.SYNC.stop_sync or app.SYNC.db_scan or app.SYNC.suspend_sync) diff --git a/resources/lib/library_sync/websocket.py b/resources/lib/library_sync/websocket.py index 4f14a5b2..53253995 100644 --- a/resources/lib/library_sync/websocket.py +++ b/resources/lib/library_sync/websocket.py @@ -22,7 +22,7 @@ PLAYSTATE_SESSIONS = {} def interrupt_processing(): - return app.APP.stop_pkc or app.SYNC.suspend_library_thread or app.SYNC.stop_sync + return app.APP.stop_pkc or app.APP.suspend_threads or app.SYNC.stop_sync def multi_delete(input_list, delete_list): diff --git a/resources/lib/playqueue.py b/resources/lib/playqueue.py index 42d84b33..26daec86 100644 --- a/resources/lib/playqueue.py +++ b/resources/lib/playqueue.py @@ -101,7 +101,7 @@ class PlayqueueMonitor(backgroundthread.KillableThread): """ Returns True if the thread is suspended """ - return self._suspended or app.CONN.pms_status + return self._suspended or app.APP.suspend_threads def _compare_playqueues(self, playqueue, new): """ diff --git a/resources/lib/plex_companion.py b/resources/lib/plex_companion.py index e7cc7a83..57f287f8 100644 --- a/resources/lib/plex_companion.py +++ b/resources/lib/plex_companion.py @@ -84,7 +84,7 @@ class PlexCompanion(backgroundthread.KillableThread): """ Returns True if the thread is suspended """ - return self._suspended or app.CONN.pms_status + return self._suspended or app.APP.suspend def _process_alexa(self, data): xml = PF.GetPlexMetadata(data['key']) diff --git a/resources/lib/service_entry.py b/resources/lib/service_entry.py index 29b7b008..d2fef4b3 100644 --- a/resources/lib/service_entry.py +++ b/resources/lib/service_entry.py @@ -17,6 +17,7 @@ from . import playqueue from . import variables as v from . import app from . import loghandler +from . import backgroundthread from .windows import userselect ############################################################################### @@ -25,11 +26,11 @@ LOG = logging.getLogger("PLEX.service") ############################################################################### WINDOW_PROPERTIES = ( - "plex_online", "plex_command_processed", "plex_shouldStop", "plex_dbScan", + "plex_command_processed", "plex_shouldStop", "plex_dbScan", "plex_customplayqueue", "plex_playbackProps", "pms_token", "plex_token", "pms_server", "plex_machineIdentifier", "plex_servername", "plex_authenticated", "PlexUserImage", "useDirectPaths", - "countError", "countUnauthorized", "plex_restricteduser", + "plex_restricteduser", "plex_allows_mediaDeletion", "plex_command", "plex_result", "plex_force_transcode_pix" ) @@ -40,13 +41,6 @@ class Service(): sync = None plexcompanion = None - ws_running = False - alexa_running = False - sync_running = False - plexcompanion_running = False - kodimonitor_running = False - playback_starter_running = False - def __init__(self): # Initial logging LOG.info("======== START %s ========", v.ADDON_NAME) @@ -87,15 +81,71 @@ class Service(): # Init time-offset between Kodi and Plex timing.KODI_PLEX_TIME_OFFSET = float(utils.settings('kodiplextimeoffset') or 0.0) - def isCanceled(self): - return xbmc.abortRequested + self.startup_completed = False + self.server_has_been_online = True + self.welcome_msg = True + self.connection_check_counter = 0 + # Flags for other threads + self.connection_check_running = False + self.auth_running = False + + def on_connection_check(self, result): + """ + Call this method after PF.check_connection() + """ + try: + if result is False: + # Server is offline or cannot be reached + # Alert the user and suppress future warning + if app.CONN.online: + # PMS was online before + app.CONN.online = False + app.APP.suspend_threads = True + LOG.warn("Plex Media Server went offline") + if utils.settings('show_pms_offline') == 'true': + utils.dialog('notification', + utils.lang(33001), + "%s %s" % (utils.lang(29999), + utils.lang(33002)), + icon='{plex}', + sound=False) + self.connection_check_counter += 1 + # Periodically check if the IP changed every 15 seconds + if self.connection_check_counter > 150: + self.connection_check_counter = 0 + server = self.setup.pick_pms() + if server: + self.setup.write_pms_to_settings(server) + app.CONN.load() + else: + # Server is online + self.connection_check_counter = 0 + if not app.CONN.online: + # Server was offline before + if (self.welcome_msg is False and + utils.settings('show_pms_offline') == 'true'): + # Alert the user that server is online + utils.dialog('notification', + utils.lang(29999), + utils.lang(33003), + icon='{plex}', + time=5000, + sound=False) + LOG.info("Server is online and ready") + if app.ACCOUNT.authenticated: + # Server got offline when we were authenticated. + # Hence resume threads + app.APP.suspend_threads = False + app.CONN.online = True + finally: + self.connection_check_running = False def log_out(self): """ Ensures that lib sync threads are suspended; signs out user """ LOG.info('Log-out requested') - app.SYNC.suspend_library_thread = True + app.APP.suspend_threads = True i = 0 while app.SYNC.db_scan: i += 1 @@ -105,7 +155,7 @@ class Service(): # Failed to reset PMS and plex.tv connects. Try to restart Kodi utils.messageDialog(utils.lang(29999), utils.lang(39208)) # Resuming threads, just in case - app.SYNC.suspend_library_thread = False + app.APP.suspend_threads = False return False LOG.info('Successfully stopped library sync') app.ACCOUNT.log_out() @@ -132,6 +182,9 @@ class Service(): # Wipe Kodi and Plex database as well as playlists and video nodes utils.wipe_database() app.CONN.load() + app.ACCOUNT.set_unauthenticated() + self.server_has_been_online = False + self.welcome_msg = False LOG.info("Choosing new PMS complete") return True @@ -142,16 +195,19 @@ class Service(): utils.delete_playlists() # Remove video nodes utils.delete_nodes() + app.ACCOUNT.set_unauthenticated() return True def toggle_plex_tv(self): - if utils.settings('plexToken'): - LOG.info('Reseting plex.tv credentials in settings') + if app.ACCOUNT.plex_token: + LOG.info('Resetting plex.tv credentials in settings') + self.log_out() app.ACCOUNT.clear() - return True else: LOG.info('Login to plex.tv') - return self.setup.plex_tv_sign_in() + if self.setup.plex_tv_sign_in(): + self.setup.write_credentials_to_settings() + app.ACCOUNT.load() def authenticate(self): """ @@ -160,9 +216,30 @@ class Service(): Returns True if successful, False if not. 'aborted' if user chose to abort """ + if self._do_auth(): + if self.welcome_msg is True: + # Reset authentication warnings + self.welcome_msg = False + utils.dialog('notification', + utils.lang(29999), + "%s %s" % (utils.lang(33000), + app.ACCOUNT.plex_username), + icon='{plex}', + time=2000, + sound=False) + app.APP.suspend_threads = False + self.auth_running = False + + def enter_new_pms_address(self): + if self.setup.enter_new_pms_address(): + app.CONN.load() + app.ACCOUNT.set_unauthenticated() + self.server_has_been_online = False + self.welcome_msg = False + + def _do_auth(self): LOG.info('Authenticating user') - if app.ACCOUNT.plex_username and not app.ACCOUNT.force_login: - # Found a user in the settings, try to authenticate + if app.ACCOUNT.plex_username and not app.ACCOUNT.force_login: # Found a user in the settings, try to authenticate LOG.info('Trying to authenticate with old settings') res = PF.check_connection(app.CONN.server, token=app.ACCOUNT.pms_token, @@ -171,8 +248,9 @@ class Service(): LOG.error('Something went wrong while checking connection') return False elif res == 401: - LOG.error('User token no longer valid. Sign user out') - app.ACCOUNT.clear() + LOG.error('User %s no longer has access - signing user out', + app.ACCOUNT.plex_username) + self.log_out() return False elif res >= 400: LOG.error('Answer from PMS is not as expected') @@ -188,7 +266,7 @@ class Service(): user, _ = userselect.start() if not user: LOG.info('No user received') - app.CONN.pms_status = 'Stop' + app.APP.suspend = True return False username = user.title user_id = user.id @@ -206,10 +284,12 @@ class Service(): return False elif res == 401: if app.ACCOUNT.plex_token: - LOG.error('Token no longer valid') - # "Your Plex token is no longer valid. Logging-out of plex.tv" - utils.messageDialog(utils.lang(29999), utils.lang(33010)) - app.ACCOUNT.clear() + LOG.error('User %s does not have access to PMS %s on %s', + username, app.CONN.server_name, app.CONN.server) + # "User is unauthorized for server {0}" + utils.messageDialog(utils.lang(29999), + utils.lang(33010).format(app.CONN.server_name)) + self.log_out() return False else: # "Failed to authenticate. Did you login to plex.tv?" @@ -219,6 +299,7 @@ class Service(): app.ACCOUNT.load() continue else: + app.APP.suspend = True return False elif res >= 400: LOG.error('Answer from PMS is not as expected') @@ -235,12 +316,12 @@ class Service(): def ServiceEntryPoint(self): # Important: Threads depending on abortRequest will not trigger # if profile switch happens more than once. - app.init() # Some plumbing + app.init() app.APP.monitor = kodimonitor.KodiMonitor() app.APP.player = xbmc.Player() artwork.IMAGE_CACHING_SUSPENDS = [ - app.SYNC.suspend_library_thread, + app.APP.suspend_threads, app.SYNC.stop_sync, app.SYNC.db_scan ] @@ -262,11 +343,9 @@ class Service(): self.playback_starter = playback_starter.PlaybackStarter() self.playqueue = playqueue.PlayqueueMonitor() - server_online = True - welcome_msg = True - counter = 0 - while not self.isCanceled(): - + # Main PKC program loop + while not xbmc.abortRequested: + # Check for Kodi profile change if utils.window('plex_kodiProfile') != v.KODI_PROFILE: # Profile change happened, terminate this thread and others LOG.info("Kodi profile was: %s and changed to: %s. " @@ -274,6 +353,7 @@ class Service(): v.KODI_PROFILE, utils.window('plex_kodiProfile')) break + # Check for PKC commands from other Python instances plex_command = utils.window('plex_command') if plex_command: # Commands/user interaction received from other PKC Python @@ -291,24 +371,21 @@ class Service(): 'dummy?mode=context_menu&%s' % plex_command.replace('CONTEXT_menu?', '')) elif plex_command == 'choose_pms_server': - if self.choose_pms_server(): - utils.window('plex_online', clear=True) - app.ACCOUNT.set_unauthenticated() - server_online = False - welcome_msg = False + task = backgroundthread.FunctionAsTask( + self.choose_pms_server, None) + backgroundthread.BGThreader.addTasksToFront([task]) elif plex_command == 'switch_plex_user': - if self.switch_plex_user(): - app.ACCOUNT.set_unauthenticated() + task = backgroundthread.FunctionAsTask( + self.switch_plex_user, None) + backgroundthread.BGThreader.addTasksToFront([task]) elif plex_command == 'enter_new_pms_address': - if self.setup.enter_new_pms_address(): - if self.log_out(): - utils.window('plex_online', clear=True) - app.ACCOUNT.set_unauthenticated() - server_online = False - welcome_msg = False + task = backgroundthread.FunctionAsTask( + self.enter_new_pms_address, None) + backgroundthread.BGThreader.addTasksToFront([task]) elif plex_command == 'toggle_plex_tv_sign_in': - if self.toggle_plex_tv(): - app.ACCOUNT.set_unauthenticated() + task = backgroundthread.FunctionAsTask( + self.toggle_plex_tv, None) + backgroundthread.BGThreader.addTasksToFront([task]) elif plex_command == 'repair-scan': app.SYNC.run_lib_scan = 'repair' elif plex_command == 'full-scan': @@ -319,120 +396,51 @@ class Service(): app.SYNC.run_lib_scan = 'textures' continue + if app.APP.suspend: + app.APP.monitor.waitForAbort(0.1) + continue + # Before proceeding, need to make sure: # 1. Server is online # 2. User is set # 3. User has access to the server - if utils.window('plex_online') == "true": - # Plex server is online - if app.CONN.pms_status == 'Stop': - app.APP.monitor.waitForAbort(0.05) - continue - elif app.CONN.pms_status == '401': - # Unauthorized access, revoke token - LOG.info('401 received - revoking token') - app.ACCOUNT.clear() - app.CONN.pms_status = 'Auth' - utils.window('plex_serverStatus', value='Auth') - continue - if not app.ACCOUNT.authenticated: - LOG.info('Not yet authenticated') - # Do authentication - if not self.authenticate(): - continue - # Start up events - if welcome_msg is True: - # Reset authentication warnings - welcome_msg = False - utils.dialog('notification', - utils.lang(29999), - "%s %s" % (utils.lang(33000), - app.ACCOUNT.plex_username), - icon='{plex}', - time=2000, - sound=False) - # Start monitoring kodi events - if not self.kodimonitor_running: - self.kodimonitor_running = True - self.specialmonitor.start() - # Start the Websocket Client - if not self.ws_running: - self.ws_running = True - self.ws.start() - # Start the Alexa thread - if (not self.alexa_running and - utils.settings('enable_alexa') == 'true'): - self.alexa_running = True - self.alexa.start() - # Start the syncing thread - if not self.sync_running: - self.sync_running = True - self.sync.start() - # Start the Plex Companion thread - if not self.plexcompanion_running: - self.plexcompanion_running = True - self.plexcompanion.start() - if not self.playback_starter_running: - self.playback_starter_running = True - self.playback_starter.start() - self.playqueue.start() - else: - # Wait until Plex server is online - # or Kodi is shut down. + if not app.CONN.online: + # Not online server = app.CONN.server if not server: # No server info set in add-on settings pass - elif PF.check_connection(server, verifySSL=True) is False: - # Server is offline or cannot be reached - # Alert the user and suppress future warning - if server_online: - server_online = False - utils.window('plex_online', value="false") - # Suspend threads - app.SYNC.suspend_library_thread = True - LOG.warn("Plex Media Server went offline") - if utils.settings('show_pms_offline') == 'true': - utils.dialog('notification', - utils.lang(33001), - "%s %s" % (utils.lang(29999), - utils.lang(33002)), - icon='{plex}', - sound=False) - counter += 1 - # Periodically check if the IP changed, e.g. per minute - if counter > 20: - counter = 0 - setup = initialsetup.InitialSetup() - tmp = setup.pick_pms() - if tmp: - setup.write_pms_to_settings(tmp) - app.CONN.load() - else: - # Server is online - counter = 0 - if not server_online: - # Server was offline when Kodi started. - server_online = True - # Alert the user that server is online. - if (welcome_msg is False and - utils.settings('show_pms_offline') == 'true'): - utils.dialog('notification', - utils.lang(29999), - utils.lang(33003), - icon='{plex}', - time=5000, - sound=False) - LOG.info("Server %s is online and ready.", server) - utils.window('plex_online', value="true") - if app.ACCOUNT.authenticated: - # Server got offline when we were authenticated. - # Hence resume threads - app.SYNC.suspend_library_thread = False + elif not self.connection_check_running: + self.connection_check_running = True + task = backgroundthread.FunctionAsTask( + PF.check_connection, + self.on_connection_check, + server, + verifySSL=True) + backgroundthread.BGThreader.addTasksToFront([task]) + continue + elif not app.ACCOUNT.authenticated: + # Plex server is online, but we're not yet authenticated + if not self.auth_running: + self.auth_running = True + task = backgroundthread.FunctionAsTask( + self.authenticate, None) + backgroundthread.BGThreader.addTasksToFront([task]) + continue + elif not self.startup_completed: + self.startup_completed = True + self.specialmonitor.start() + self.ws.start() + self.sync.start() + self.plexcompanion.start() + self.playback_starter.start() + self.playqueue.start() + if utils.settings('enable_alexa') == 'true': + self.alexa.start() - if app.APP.monitor.waitForAbort(0.05): - # Abort was requested while waiting. We should exit - break + app.APP.monitor.waitForAbort(0.1) + + # EXITING PKC # Tell all threads to terminate (e.g. several lib sync threads) app.APP.stop_pkc = True utils.window('plex_service_started', clear=True) diff --git a/resources/lib/sync.py b/resources/lib/sync.py index b2378576..1538eb56 100644 --- a/resources/lib/sync.py +++ b/resources/lib/sync.py @@ -41,7 +41,7 @@ class Sync(backgroundthread.KillableThread): super(Sync, self).__init__() def isSuspended(self): - return self._suspended or app.SYNC.suspend_library_thread + return self._suspended or app.APP.suspend_threads def show_kodi_note(self, message, icon="plex", force=False): """ diff --git a/resources/lib/websocket_client.py b/resources/lib/websocket_client.py index a1d777bd..09f919c1 100644 --- a/resources/lib/websocket_client.py +++ b/resources/lib/websocket_client.py @@ -137,7 +137,7 @@ class PMS_Websocket(WebSocket): Returns True if the thread is suspended """ return (self._suspended or - app.SYNC.suspend_library_thread or + app.APP.suspend_threads or app.SYNC.background_sync_disabled) def getUri(self):