diff --git a/default.py b/default.py index 9f30f61c..96983316 100644 --- a/default.py +++ b/default.py @@ -170,10 +170,10 @@ class Main(): Start up playback_starter in main Python thread """ # Put the request into the 'queue' - while window('plex_play_new_item'): + while window('plex_command'): sleep(50) - window('plex_play_new_item', - value='%s%s' % ('play', argv[2])) + window('plex_command', + value='play_%s' % argv[2]) # Wait for the result while not pickl_window('plex_result'): sleep(50) diff --git a/resources/lib/PlexAPI.py b/resources/lib/PlexAPI.py index fbd28e7c..282c5104 100644 --- a/resources/lib/PlexAPI.py +++ b/resources/lib/PlexAPI.py @@ -53,6 +53,7 @@ from utils import window, settings, language as lang, tryDecode, tryEncode, \ from PlexFunctions import PMSHttpsEnabled import plexdb_functions as plexdb import variables as v +import state ############################################################################### @@ -879,6 +880,8 @@ class PlexAPI(): settings('plex_restricteduser', 'true' if answer.attrib.get('restricted', '0') == '1' else 'false') + state.RESTRICTED_USER = True if \ + answer.attrib.get('restricted', '0') == '1' else False # Get final token to the PMS we've chosen url = 'https://plex.tv/api/resources?includeHttps=1' @@ -2563,7 +2566,8 @@ class API(): if forceCheck is False: # Validate the path is correct with user intervention if self.askToValidate(path): - window('plex_shouldStop', value="true") + import state + state.STOP_SYNC = True path = None window('plex_pathverified', value='true') else: diff --git a/resources/lib/PlexCompanion.py b/resources/lib/PlexCompanion.py index 9b961c5f..48f6e831 100644 --- a/resources/lib/PlexCompanion.py +++ b/resources/lib/PlexCompanion.py @@ -7,13 +7,14 @@ from urllib import urlencode from xbmc import sleep, executebuiltin -from utils import settings, ThreadMethodsAdditionalSuspend, ThreadMethods +from utils import settings, ThreadMethods from plexbmchelper import listener, plexgdm, subscribers, functions, \ httppersist, plexsettings from PlexFunctions import ParseContainerKey, GetPlexMetadata from PlexAPI import API import player import variables as v +import state ############################################################################### @@ -22,8 +23,7 @@ log = logging.getLogger("PLEX."+__name__) ############################################################################### -@ThreadMethodsAdditionalSuspend('plex_serverStatus') -@ThreadMethods +@ThreadMethods(add_suspends=[state.PMS_STATUS]) class PlexCompanion(Thread): """ """ @@ -164,8 +164,8 @@ class PlexCompanion(Thread): httpd = self.httpd # Cache for quicker while loops client = self.client - threadStopped = self.threadStopped - threadSuspended = self.threadSuspended + thread_stopped = self.thread_stopped + thread_suspended = self.thread_suspended # Start up instances requestMgr = httppersist.RequestMgr() @@ -213,12 +213,12 @@ class PlexCompanion(Thread): if httpd: t = Thread(target=httpd.handle_request) - while not threadStopped(): + while not thread_stopped(): # If we are not authorized, sleep # Otherwise, we trigger a download which leads to a # re-authorizations - while threadSuspended(): - if threadStopped(): + while thread_suspended(): + if thread_stopped(): break sleep(1000) try: diff --git a/resources/lib/artwork.py b/resources/lib/artwork.py index 5d3ba04f..4a6e352d 100644 --- a/resources/lib/artwork.py +++ b/resources/lib/artwork.py @@ -13,7 +13,8 @@ from xbmc import executeJSONRPC, sleep, translatePath from xbmcvfs import exists from utils import window, settings, language as lang, kodiSQL, tryEncode, \ - ThreadMethods, ThreadMethodsAdditionalStop, dialog, exists_dir + ThreadMethods, dialog, exists_dir +import state # Disable annoying requests warnings import requests.packages.urllib3 @@ -126,8 +127,8 @@ def double_urldecode(text): return unquote(unquote(text)) -@ThreadMethodsAdditionalStop('plex_shouldStop') -@ThreadMethods +@ThreadMethods(add_stops=[state.STOP_SYNC], + add_suspends=[state.SUSPEND_LIBRARY_THREAD, state.DB_SCAN]) class Image_Cache_Thread(Thread): xbmc_host = 'localhost' xbmc_port, xbmc_username, xbmc_password = setKodiWebServerDetails() @@ -140,22 +141,16 @@ class Image_Cache_Thread(Thread): self.queue = ARTWORK_QUEUE Thread.__init__(self) - def threadSuspended(self): - # Overwrite method to add TWO additional suspends - return (self._threadSuspended or - window('suspend_LibraryThread') or - window('plex_dbScan')) - def run(self): - threadStopped = self.threadStopped - threadSuspended = self.threadSuspended + thread_stopped = self.thread_stopped + thread_suspended = self.thread_suspended queue = self.queue sleep_between = self.sleep_between - while not threadStopped(): + while not thread_stopped(): # In the event the server goes offline - while threadSuspended(): + while thread_suspended(): # Set in service.py - if threadStopped(): + if thread_stopped(): # Abort was requested while waiting. We should exit log.info("---===### Stopped Image_Cache_Thread ###===---") return @@ -178,7 +173,7 @@ class Image_Cache_Thread(Thread): # download. All is well break except requests.ConnectionError: - if threadStopped(): + if thread_stopped(): # Kodi terminated break # Server thinks its a DOS attack, ('error 10053') diff --git a/resources/lib/command_pipeline.py b/resources/lib/command_pipeline.py new file mode 100644 index 00000000..9be330a6 --- /dev/null +++ b/resources/lib/command_pipeline.py @@ -0,0 +1,68 @@ +# -*- coding: utf-8 -*- +############################################################################### +import logging +from threading import Thread +from Queue import Queue + +from xbmc import sleep + +from utils import window, ThreadMethods +import state + +############################################################################### +log = logging.getLogger("PLEX."+__name__) + +############################################################################### + + +@ThreadMethods +class Monitor_Window(Thread): + """ + Monitors window('plex_command') for new entries that we need to take care + of, e.g. for new plays initiated on the Kodi side with addon paths. + + Possible values of window('plex_command'): + 'play_....': to start playback using playback_starter + + Adjusts state.py accordingly + """ + # Borg - multiple instances, shared state + def __init__(self, callback=None): + self.mgr = callback + self.playback_queue = Queue() + Thread.__init__(self) + + def run(self): + thread_stopped = self.thread_stopped + queue = self.playback_queue + log.info("----===## Starting Kodi_Play_Client ##===----") + while not thread_stopped(): + if window('plex_command'): + value = window('plex_command') + window('plex_command', clear=True) + if value.startswith('play_'): + queue.put(value) + + elif value == 'SUSPEND_LIBRARY_THREAD-True': + state.SUSPEND_LIBRARY_THREAD = True + elif value == 'SUSPEND_LIBRARY_THREAD-False': + state.SUSPEND_LIBRARY_THREAD = False + elif value == 'STOP_SYNC-True': + state.STOP_SYNC = True + elif value == 'STOP_SYNC-False': + state.STOP_SYNC = False + elif value == 'PMS_STATUS-Auth': + state.PMS_STATUS = 'Auth' + elif value == 'PMS_STATUS-401': + state.PMS_STATUS = '401' + elif value == 'SUSPEND_USER_CLIENT-True': + state.SUSPEND_USER_CLIENT = True + elif value == 'SUSPEND_USER_CLIENT-False': + state.SUSPEND_USER_CLIENT = False + elif value.startswith('PLEX_TOKEN-'): + state.PLEX_TOKEN = value.replace('PLEX_TOKEN-', '') or None + else: + sleep(50) + # Put one last item into the queue to let playback_starter end + queue.put(None) + log.info("----===## Kodi_Play_Client stopped ##===----") diff --git a/resources/lib/downloadutils.py b/resources/lib/downloadutils.py index 8d78d8ba..d1c07d9f 100644 --- a/resources/lib/downloadutils.py +++ b/resources/lib/downloadutils.py @@ -9,6 +9,8 @@ import xml.etree.ElementTree as etree from utils import settings, window, language as lang, dialog import clientinfo as client +import state + ############################################################################### # Disable annoying requests warnings @@ -274,10 +276,11 @@ class DownloadUtils(): self.unauthorizedAttempts): log.warn('We seem to be truly unauthorized for PMS' ' %s ' % url) - if window('plex_serverStatus') not in ('401', 'Auth'): + if state.PMS_STATUS not in ('401', 'Auth'): # Tell userclient token has been revoked. log.debug('Setting PMS server status to ' 'unauthorized') + state.PMS_STATUS = '401' window('plex_serverStatus', value="401") dialog('notification', lang(29999), diff --git a/resources/lib/entrypoint.py b/resources/lib/entrypoint.py index 8ab911cd..3bdfac4d 100644 --- a/resources/lib/entrypoint.py +++ b/resources/lib/entrypoint.py @@ -12,7 +12,7 @@ from xbmc import sleep, executebuiltin, translatePath from xbmcgui import ListItem from utils import window, settings, language as lang, dialog, tryEncode, \ - CatchExceptions, JSONRPC, exists_dir + CatchExceptions, JSONRPC, exists_dir, plex_command import downloadutils from PlexFunctions import GetPlexMetadata, GetPlexSectionResults, \ @@ -42,8 +42,8 @@ def chooseServer(): server = setup.PickPMS(showDialog=True) if server is None: log.error('We did not connect to a new PMS, aborting') - window('suspend_Userclient', clear=True) - window('suspend_LibraryThread', clear=True) + plex_command('SUSPEND_USER_CLIENT', 'False') + plex_command('SUSPEND_LIBRARY_THREAD', 'False') return log.info("User chose server %s" % server['name']) @@ -81,6 +81,7 @@ def togglePlexTV(): settings('plex_status', value="Not logged in to plex.tv") window('plex_token', clear=True) + plex_command('PLEX_TOKEN', '') window('plex_username', clear=True) else: log.info('Login to plex.tv') @@ -100,7 +101,7 @@ def resetAuth(): resp = dialog('yesno', heading="{plex}", line1=lang(39206)) if resp == 1: log.info("Reset login attempts.") - window('plex_serverStatus', value="Auth") + plex_command('PMS_STATUS', 'Auth') else: executebuiltin('Addon.OpenSettings(plugin.video.plexkodiconnect)') @@ -964,22 +965,19 @@ def enterPMS(): def __LogIn(): """ - Resets (clears) window properties to enable (re-)login: - suspend_Userclient - plex_runLibScan: set to 'full' to trigger lib sync + Resets (clears) window properties to enable (re-)login - suspend_LibraryThread is cleared in service.py if user was signed out! + SUSPEND_LIBRARY_THREAD is set to False in service.py if user was signed + out! """ window('plex_runLibScan', value='full') # Restart user client - window('suspend_Userclient', clear=True) + plex_command('SUSPEND_USER_CLIENT', 'False') def __LogOut(): """ - Finishes lib scans, logs out user. The following window attributes are set: - suspend_LibraryThread: 'true' - suspend_Userclient: 'true' + Finishes lib scans, logs out user. Returns True if successfully signed out, False otherwise """ @@ -991,7 +989,7 @@ def __LogOut(): time=3000, sound=False) # Pause library sync thread - window('suspend_LibraryThread', value='true') + plex_command('SUSPEND_LIBRARY_THREAD', 'True') # Wait max for 10 seconds for all lib scans to shutdown counter = 0 while window('plex_dbScan') == 'true': @@ -999,17 +997,18 @@ def __LogOut(): # Failed to reset PMS and plex.tv connects. Try to restart Kodi. dialog('ok', lang(29999), lang(39208)) # Resuming threads, just in case - window('suspend_LibraryThread', clear=True) + plex_command('SUSPEND_LIBRARY_THREAD', 'False') log.error("Could not stop library sync, aborting") return False counter += 1 sleep(50) log.debug("Successfully stopped library sync") - # Log out currently signed in user: - window('plex_serverStatus', value="401") - # Above method needs to have run its course! Hence wait counter = 0 + # Log out currently signed in user: + window('plex_serverStatus', value='401') + plex_command('PMS_STATUS', '401') + # Above method needs to have run its course! Hence wait while window('plex_serverStatus') == "401": if counter > 100: # 'Failed to reset PKC. Try to restart Kodi.' @@ -1019,5 +1018,5 @@ def __LogOut(): counter += 1 sleep(50) # Suspend the user client during procedure - window('suspend_Userclient', value='true') + plex_command('SUSPEND_USER_CLIENT', 'True') return True diff --git a/resources/lib/initialsetup.py b/resources/lib/initialsetup.py index 54bd9940..9c41f924 100644 --- a/resources/lib/initialsetup.py +++ b/resources/lib/initialsetup.py @@ -13,6 +13,7 @@ from userclient import UserClient from PlexAPI import PlexAPI from PlexFunctions import GetMachineIdentifier, get_PMS_settings +import state ############################################################################### @@ -496,7 +497,7 @@ class InitialSetup(): # Open Settings page now? You will need to restart! goToSettings = dialog.yesno(heading=lang(29999), line1=lang(39017)) if goToSettings: - window('plex_serverStatus', value="Stop") + state.PMS_STATUS = 'Stop' xbmc.executebuiltin( 'Addon.OpenSettings(plugin.video.plexkodiconnect)') else: diff --git a/resources/lib/library_sync/fanart.py b/resources/lib/library_sync/fanart.py index 7f9fc074..483da89a 100644 --- a/resources/lib/library_sync/fanart.py +++ b/resources/lib/library_sync/fanart.py @@ -5,11 +5,11 @@ from Queue import Empty from xbmc import sleep -from utils import ThreadMethodsAdditionalStop, ThreadMethods, window, \ - ThreadMethodsAdditionalSuspend +from utils import ThreadMethods, window import plexdb_functions as plexdb import itemtypes import variables as v +import state ############################################################################### @@ -18,9 +18,8 @@ log = getLogger("PLEX."+__name__) ############################################################################### -@ThreadMethodsAdditionalSuspend('suspend_LibraryThread') -@ThreadMethodsAdditionalStop('plex_shouldStop') -@ThreadMethods +@ThreadMethods(add_suspends=[state.SUSPEND_LIBRARY_THREAD, state.DB_SCAN], + add_stops=[state.STOP_SYNC]) class Process_Fanart_Thread(Thread): """ Threaded download of additional fanart in the background @@ -55,14 +54,14 @@ class Process_Fanart_Thread(Thread): Do the work """ log.debug("---===### Starting FanartSync ###===---") - threadStopped = self.threadStopped - threadSuspended = self.threadSuspended + thread_stopped = self.thread_stopped + thread_suspended = self.thread_suspended queue = self.queue - while not threadStopped(): + while not thread_stopped(): # In the event the server goes offline - while threadSuspended() or window('plex_dbScan'): + while thread_suspended(): # Set in service.py - if threadStopped(): + if thread_stopped(): # Abort was requested while waiting. We should exit log.info("---===### Stopped FanartSync ###===---") return diff --git a/resources/lib/library_sync/get_metadata.py b/resources/lib/library_sync/get_metadata.py index 5fd25859..4aa44266 100644 --- a/resources/lib/library_sync/get_metadata.py +++ b/resources/lib/library_sync/get_metadata.py @@ -5,9 +5,10 @@ from Queue import Empty from xbmc import sleep -from utils import ThreadMethodsAdditionalStop, ThreadMethods, window +from utils import ThreadMethods, window from PlexFunctions import GetPlexMetadata, GetAllPlexChildren import sync_info +import state ############################################################################### @@ -16,8 +17,7 @@ log = getLogger("PLEX."+__name__) ############################################################################### -@ThreadMethodsAdditionalStop('suspend_LibraryThread') -@ThreadMethods +@ThreadMethods(add_stops=[state.SUSPEND_LIBRARY_THREAD]) class Threaded_Get_Metadata(Thread): """ Threaded download of Plex XML metadata for a certain library item. @@ -48,7 +48,7 @@ class Threaded_Get_Metadata(Thread): continue else: self.queue.task_done() - if self.threadStopped(): + if self.thread_stopped(): # Shutdown from outside requested; purge out_queue as well while not self.out_queue.empty(): # Still try because remaining item might have been taken @@ -79,8 +79,8 @@ class Threaded_Get_Metadata(Thread): # cache local variables because it's faster queue = self.queue out_queue = self.out_queue - threadStopped = self.threadStopped - while threadStopped() is False: + thread_stopped = self.thread_stopped + while thread_stopped() is False: # grabs Plex item from queue try: item = queue.get(block=False) diff --git a/resources/lib/library_sync/process_metadata.py b/resources/lib/library_sync/process_metadata.py index e6765b41..7b44ed33 100644 --- a/resources/lib/library_sync/process_metadata.py +++ b/resources/lib/library_sync/process_metadata.py @@ -5,19 +5,18 @@ from Queue import Empty from xbmc import sleep -from utils import ThreadMethodsAdditionalStop, ThreadMethods +from utils import ThreadMethods import itemtypes import sync_info +import state ############################################################################### - log = getLogger("PLEX."+__name__) ############################################################################### -@ThreadMethodsAdditionalStop('suspend_LibraryThread') -@ThreadMethods +@ThreadMethods(add_stops=[state.SUSPEND_LIBRARY_THREAD]) class Threaded_Process_Metadata(Thread): """ Not yet implemented for more than 1 thread - if ever. Only to be called by @@ -70,9 +69,9 @@ class Threaded_Process_Metadata(Thread): item_fct = getattr(itemtypes, self.item_type) # cache local variables because it's faster queue = self.queue - threadStopped = self.threadStopped + thread_stopped = self.thread_stopped with item_fct() as item_class: - while threadStopped() is False: + while thread_stopped() is False: # grabs item from queue try: item = queue.get(block=False) diff --git a/resources/lib/library_sync/sync_info.py b/resources/lib/library_sync/sync_info.py index 3be8f70b..13caec52 100644 --- a/resources/lib/library_sync/sync_info.py +++ b/resources/lib/library_sync/sync_info.py @@ -4,7 +4,8 @@ from threading import Thread, Lock from xbmc import sleep -from utils import ThreadMethodsAdditionalStop, ThreadMethods, language as lang +from utils import ThreadMethods, language as lang +import state ############################################################################### @@ -18,8 +19,7 @@ LOCK = Lock() ############################################################################### -@ThreadMethodsAdditionalStop('suspend_LibraryThread') -@ThreadMethods +@ThreadMethods(add_stops=[state.SUSPEND_LIBRARY_THREAD]) class Threaded_Show_Sync_Info(Thread): """ Threaded class to show the Kodi statusbar of the metadata download. @@ -53,13 +53,13 @@ class Threaded_Show_Sync_Info(Thread): # cache local variables because it's faster total = self.total dialog = self.dialog - threadStopped = self.threadStopped + thread_stopped = self.thread_stopped dialog.create("%s %s: %s %s" % (lang(39714), self.item_type, str(total), lang(39715))) total = 2 * total totalProgress = 0 - while threadStopped() is False: + while thread_stopped() is False: with LOCK: get_progress = GET_METADATA_COUNT process_progress = PROCESS_METADATA_COUNT diff --git a/resources/lib/librarysync.py b/resources/lib/librarysync.py index 60e1a0e9..c1362531 100644 --- a/resources/lib/librarysync.py +++ b/resources/lib/librarysync.py @@ -10,10 +10,9 @@ import xbmcgui from xbmcvfs import exists from utils import window, settings, getUnixTimestamp, sourcesXML,\ - ThreadMethods, ThreadMethodsAdditionalStop, LogTime, getScreensaver,\ + ThreadMethods, create_actor_db_index, dialog, LogTime, getScreensaver,\ setScreensaver, playlistXSP, language as lang, DateToKodi, reset,\ - advancedsettings_tweaks, tryDecode, deletePlaylists, deleteNodes, \ - ThreadMethodsAdditionalSuspend, create_actor_db_index, dialog + advancedsettings_tweaks, tryDecode, deletePlaylists, deleteNodes import downloadutils import itemtypes import plexdb_functions as plexdb @@ -30,6 +29,7 @@ from library_sync.process_metadata import Threaded_Process_Metadata import library_sync.sync_info as sync_info from library_sync.fanart import Process_Fanart_Thread import music +import state ############################################################################### @@ -38,9 +38,8 @@ log = logging.getLogger("PLEX."+__name__) ############################################################################### -@ThreadMethodsAdditionalSuspend('suspend_LibraryThread') -@ThreadMethodsAdditionalStop('plex_shouldStop') -@ThreadMethods +@ThreadMethods(add_stops=[state.STOP_SYNC], + add_suspends=[state.SUSPEND_LIBRARY_THREAD]) class LibrarySync(Thread): """ """ @@ -300,7 +299,7 @@ class LibrarySync(Thread): # Do the processing for itemtype in process: - if self.threadStopped(): + if self.thread_stopped(): xbmc.executebuiltin('InhibitIdleShutdown(false)') setScreensaver(value=screensaver) return False @@ -323,7 +322,7 @@ class LibrarySync(Thread): window('plex_scancrashed', clear=True) elif window('plex_scancrashed') == '401': window('plex_scancrashed', clear=True) - if window('plex_serverStatus') not in ('401', 'Auth'): + if state.PMS_STATUS not in ('401', 'Auth'): # Plex server had too much and returned ERROR self.dialog.ok(lang(29999), lang(39409)) @@ -759,8 +758,8 @@ class LibrarySync(Thread): for thread in threads: # Threads might already have quit by themselves (e.g. Kodi exit) try: - thread.stopThread() - except: + thread.stop_thread() + except AttributeError: pass log.debug("Stop sent to all threads") # Wait till threads are indeed dead @@ -805,7 +804,7 @@ class LibrarySync(Thread): # PROCESS MOVIES ##### self.updatelist = [] for view in views: - if self.threadStopped(): + if self.thread_stopped(): return False # Get items per view viewId = view['id'] @@ -826,7 +825,7 @@ class LibrarySync(Thread): log.info("Processed view") # Update viewstate for EVERY item for view in views: - if self.threadStopped(): + if self.thread_stopped(): return False self.PlexUpdateWatched(view['id'], itemType) @@ -898,7 +897,7 @@ class LibrarySync(Thread): # PROCESS TV Shows ##### self.updatelist = [] for view in views: - if self.threadStopped(): + if self.thread_stopped(): return False # Get items per view viewId = view['id'] @@ -927,7 +926,7 @@ class LibrarySync(Thread): # PROCESS TV Seasons ##### # Cycle through tv shows for tvShowId in allPlexTvShowsId: - if self.threadStopped(): + if self.thread_stopped(): return False # Grab all seasons to tvshow from PMS seasons = GetAllPlexChildren(tvShowId) @@ -952,7 +951,7 @@ class LibrarySync(Thread): # PROCESS TV Episodes ##### # Cycle through tv shows for view in views: - if self.threadStopped(): + if self.thread_stopped(): return False # Grab all episodes to tvshow from PMS episodes = GetAllPlexLeaves(view['id']) @@ -987,7 +986,7 @@ class LibrarySync(Thread): # Update viewstate: for view in views: - if self.threadStopped(): + if self.thread_stopped(): return False self.PlexUpdateWatched(view['id'], itemType) @@ -1024,7 +1023,7 @@ class LibrarySync(Thread): for kind in (v.PLEX_TYPE_ARTIST, v.PLEX_TYPE_ALBUM, v.PLEX_TYPE_SONG): - if self.threadStopped(): + if self.thread_stopped(): return False log.debug("Start processing music %s" % kind) self.allKodiElementsId = {} @@ -1041,7 +1040,7 @@ class LibrarySync(Thread): # Update viewstate for EVERY item for view in views: - if self.threadStopped(): + if self.thread_stopped(): return False self.PlexUpdateWatched(view['id'], itemType) @@ -1066,7 +1065,7 @@ class LibrarySync(Thread): except ValueError: pass for view in views: - if self.threadStopped(): + if self.thread_stopped(): return False # Get items per view itemsXML = GetPlexSectionResults(view['id'], args=urlArgs) @@ -1172,7 +1171,7 @@ class LibrarySync(Thread): now = getUnixTimestamp() deleteListe = [] for i, item in enumerate(self.itemsToProcess): - if self.threadStopped(): + if self.thread_stopped(): # Chances are that Kodi gets shut down break if item['state'] == 9: @@ -1277,8 +1276,8 @@ class LibrarySync(Thread): # movie or episode) continue typus = int(item.get('type', 0)) - state = int(item.get('state', 0)) - if state == 9 or (typus in (1, 4, 10) and state == 5): + status = int(item.get('state', 0)) + if status == 9 or (typus in (1, 4, 10) and status == 5): # Only process deleted items OR movies, episodes, tracks/songs plex_id = str(item.get('itemID', '0')) if plex_id == '0': @@ -1286,7 +1285,7 @@ class LibrarySync(Thread): continue try: if (now - self.just_processed[plex_id] < - self.ignore_just_processed and state != 9): + self.ignore_just_processed and status != 9): log.debug('We just processed %s: ignoring' % plex_id) continue except KeyError: @@ -1299,7 +1298,7 @@ class LibrarySync(Thread): else: # Haven't added this element to the queue yet self.itemsToProcess.append({ - 'state': state, + 'state': status, 'type': typus, 'ratingKey': plex_id, 'timestamp': getUnixTimestamp(), @@ -1315,8 +1314,8 @@ class LibrarySync(Thread): with plexdb.Get_Plex_DB() as plex_db: for item in data: # Drop buffering messages immediately - state = item.get('state') - if state == 'buffering': + status = item.get('state') + if status == 'buffering': continue ratingKey = item.get('ratingKey') kodiInfo = plex_db.getItem_byId(ratingKey) @@ -1335,8 +1334,7 @@ class LibrarySync(Thread): } else: # PMS is ours - get all current sessions - self.sessionKeys = GetPMSStatus( - window('plex_token')) + self.sessionKeys = GetPMSStatus(state.PLEX_TOKEN) log.debug('Updated current sessions. They are: %s' % self.sessionKeys) if sessionKey not in self.sessionKeys: @@ -1349,8 +1347,7 @@ class LibrarySync(Thread): # Identify the user - same one as signed on with PKC? Skip # update if neither session's username nor userid match # (Owner sometime's returns id '1', not always) - if (window('plex_token') == '' and - currSess['userId'] == '1'): + if (not state.PLEX_TOKEN and currSess['userId'] == '1'): # PKC not signed in to plex.tv. Plus owner of PMS is # playing (the '1'). # Hence must be us (since several users require plex.tv @@ -1394,7 +1391,7 @@ class LibrarySync(Thread): 'file_id': kodiInfo[1], 'kodi_type': kodiInfo[4], 'viewOffset': resume, - 'state': state, + 'state': status, 'duration': currSess['duration'], 'viewCount': currSess['viewCount'], 'lastViewedAt': DateToKodi(getUnixTimestamp()) @@ -1433,6 +1430,7 @@ class LibrarySync(Thread): try: self.run_internal() except Exception as e: + state.DB_SCAN = False window('plex_dbScan', clear=True) log.error('LibrarySync thread crashed. Error message: %s' % e) import traceback @@ -1443,8 +1441,8 @@ class LibrarySync(Thread): def run_internal(self): # Re-assign handles to have faster calls - threadStopped = self.threadStopped - threadSuspended = self.threadSuspended + thread_stopped = self.thread_stopped + thread_suspended = self.thread_suspended installSyncDone = self.installSyncDone enableBackgroundSync = self.enableBackgroundSync fullSync = self.fullSync @@ -1476,12 +1474,12 @@ class LibrarySync(Thread): if settings('FanartTV') == 'true': self.fanartthread.start() - while not threadStopped(): + while not thread_stopped(): # In the event the server goes offline - while threadSuspended(): + while thread_suspended(): # Set in service.py - if threadStopped(): + if thread_stopped(): # Abort was requested while waiting. We should exit log.info("###===--- LibrarySync Stopped ---===###") return @@ -1523,6 +1521,7 @@ class LibrarySync(Thread): self.dialog.ok(heading=lang(29999), line1=lang(39403)) break # Run start up sync + state.DB_SCAN = True window('plex_dbScan', value="true") log.info("Db version: %s" % settings('dbCreatedWithVersion')) lastTimeSync = getUnixTimestamp() @@ -1547,6 +1546,7 @@ class LibrarySync(Thread): log.info("Initial start-up full sync starting") librarySync = fullSync() window('plex_dbScan', clear=True) + state.DB_SCAN = False if librarySync: log.info("Initial start-up full sync successful") startupComplete = True @@ -1565,23 +1565,26 @@ class LibrarySync(Thread): break # Currently no db scan, so we can start a new scan - elif window('plex_dbScan') != "true": + elif state.DB_SCAN is False: # Full scan was requested from somewhere else, e.g. userclient if window('plex_runLibScan') in ("full", "repair"): log.info('Full library scan requested, starting') window('plex_dbScan', value="true") + state.DB_SCAN = True if window('plex_runLibScan') == "full": fullSync() elif window('plex_runLibScan') == "repair": fullSync(repair=True) window('plex_runLibScan', clear=True) window('plex_dbScan', clear=True) + state.DB_SCAN = False # Full library sync finished self.showKodiNote(lang(39407), forced=False) # Reset views was requested from somewhere else elif window('plex_runLibScan') == "views": log.info('Refresh playlist and nodes requested, starting') window('plex_dbScan', value="true") + state.DB_SCAN = True window('plex_runLibScan', clear=True) # First remove playlists @@ -1602,6 +1605,7 @@ class LibrarySync(Thread): forced=True, icon="error") window('plex_dbScan', clear=True) + state.DB_SCAN = False elif window('plex_runLibScan') == 'fanart': window('plex_runLibScan', clear=True) # Only look for missing fanart (No) @@ -1613,31 +1617,37 @@ class LibrarySync(Thread): yeslabel=lang(39225))) elif window('plex_runLibScan') == 'del_textures': window('plex_runLibScan', clear=True) + state.DB_SCAN = True window('plex_dbScan', value="true") import artwork artwork.Artwork().fullTextureCacheSync() window('plex_dbScan', clear=True) + state.DB_SCAN = False else: now = getUnixTimestamp() if (now - lastSync > fullSyncInterval and not xbmcplayer.isPlaying()): lastSync = now log.info('Doing scheduled full library scan') + state.DB_SCAN = True window('plex_dbScan', value="true") - if fullSync() is False and not threadStopped(): + if fullSync() is False and not thread_stopped(): log.error('Could not finish scheduled full sync') self.showKodiNote(lang(39410), forced=True, icon='error') window('plex_dbScan', clear=True) + state.DB_SCAN = False # Full library sync finished self.showKodiNote(lang(39407), forced=False) elif now - lastTimeSync > oneDay: lastTimeSync = now log.info('Starting daily time sync') + state.DB_SCAN = True window('plex_dbScan', value="true") self.syncPMStime() window('plex_dbScan', clear=True) + state.DB_SCAN = False elif enableBackgroundSync: # Check back whether we should process something # Only do this once every while (otherwise, potentially diff --git a/resources/lib/monitor_kodi_play.py b/resources/lib/monitor_kodi_play.py deleted file mode 100644 index c9827fac..00000000 --- a/resources/lib/monitor_kodi_play.py +++ /dev/null @@ -1,41 +0,0 @@ -# -*- coding: utf-8 -*- -############################################################################### -import logging -from threading import Thread -from Queue import Queue - -from xbmc import sleep - -from utils import window, ThreadMethods - -############################################################################### -log = logging.getLogger("PLEX."+__name__) - -############################################################################### - - -@ThreadMethods -class Monitor_Kodi_Play(Thread): - """ - Monitors for new plays initiated on the Kodi side with addon paths. - Immediately throws them into a queue to be processed by playback_starter - """ - # Borg - multiple instances, shared state - def __init__(self, callback=None): - self.mgr = callback - self.playback_queue = Queue() - Thread.__init__(self) - - def run(self): - threadStopped = self.threadStopped - queue = self.playback_queue - log.info("----===## Starting Kodi_Play_Client ##===----") - while not threadStopped(): - if window('plex_play_new_item'): - queue.put(window('plex_play_new_item')) - window('plex_play_new_item', clear=True) - else: - sleep(50) - # Put one last item into the queue to let playback_starter end - queue.put(None) - log.info("----===## Kodi_Play_Client stopped ##===----") diff --git a/resources/lib/playback_starter.py b/resources/lib/playback_starter.py index b6b14c18..836f587d 100644 --- a/resources/lib/playback_starter.py +++ b/resources/lib/playback_starter.py @@ -152,12 +152,12 @@ class Playback_Starter(Thread): pickle_me(result) def run(self): - queue = self.mgr.monitor_kodi_play.playback_queue + queue = self.mgr.command_pipeline.playback_queue log.info("----===## Starting Playback_Starter ##===----") while True: item = queue.get() if item is None: - # Need to shutdown - initiated by monitor_kodi_play + # Need to shutdown - initiated by command_pipeline break else: self.triage(item) diff --git a/resources/lib/playqueue.py b/resources/lib/playqueue.py index 60656813..7e716559 100644 --- a/resources/lib/playqueue.py +++ b/resources/lib/playqueue.py @@ -5,12 +5,13 @@ from threading import RLock, Thread from xbmc import sleep, Player, PlayList, PLAYLIST_MUSIC, PLAYLIST_VIDEO -from utils import window, ThreadMethods, ThreadMethodsAdditionalSuspend +from utils import window, ThreadMethods import playlist_func as PL from PlexFunctions import ConvertPlexToKodiTime, GetAllPlexChildren from PlexAPI import API from playbackutils import PlaybackUtils import variables as v +import state ############################################################################### log = logging.getLogger("PLEX."+__name__) @@ -21,8 +22,7 @@ PLUGIN = 'plugin://%s' % v.ADDON_ID ############################################################################### -@ThreadMethodsAdditionalSuspend('plex_serverStatus') -@ThreadMethods +@ThreadMethods(add_suspends=[state.PMS_STATUS]) class Playqueue(Thread): """ Monitors Kodi's playqueues for changes on the Kodi side @@ -153,7 +153,7 @@ class Playqueue(Thread): # Ignore new media added by other addons continue for j, old_item in enumerate(old): - if self.threadStopped(): + if self.thread_stopped(): # Chances are that we got an empty Kodi playlist due to # Kodi exit return @@ -193,8 +193,8 @@ class Playqueue(Thread): log.debug('Done comparing playqueues') def run(self): - threadStopped = self.threadStopped - threadSuspended = self.threadSuspended + thread_stopped = self.thread_stopped + thread_suspended = self.thread_suspended log.info("----===## Starting PlayQueue client ##===----") # Initialize the playqueues, if Kodi already got items in them for playqueue in self.playqueues: @@ -203,9 +203,9 @@ class Playqueue(Thread): PL.init_Plex_playlist(playqueue, kodi_item=item) else: PL.add_item_to_PMS_playlist(playqueue, i, kodi_item=item) - while not threadStopped(): - while threadSuspended(): - if threadStopped(): + while not thread_stopped(): + while thread_suspended(): + if thread_stopped(): break sleep(1000) with lock: diff --git a/resources/lib/state.py b/resources/lib/state.py new file mode 100644 index 00000000..9fffcaea --- /dev/null +++ b/resources/lib/state.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- +# THREAD SAFE + +# Quit PKC +STOP_PKC = False + +# Usually triggered by another Python instance - will have to be set (by +# polling window) through e.g. librarysync thread +SUSPEND_LIBRARY_THREAD = False +# Set if user decided to cancel sync +STOP_SYNC = False +# Set if a Plex-Kodi DB sync is being done - along with window('plex_dbScan') +# set to 'true' +DB_SCAN = False +# Plex Media Server Status - along with window('plex_serverStatus') +PMS_STATUS = False +# When the userclient needs to wait +SUSPEND_USER_CLIENT = False +# Plex home user? Then "False". Along with window('plex_restricteduser') +RESTRICTED_USER = False + +PLEX_TOKEN = None diff --git a/resources/lib/userclient.py b/resources/lib/userclient.py index 93f5e019..584a1d94 100644 --- a/resources/lib/userclient.py +++ b/resources/lib/userclient.py @@ -10,12 +10,12 @@ import xbmcaddon from xbmcvfs import exists -from utils import window, settings, language as lang, ThreadMethods, \ - ThreadMethodsAdditionalSuspend +from utils import window, settings, language as lang, ThreadMethods import downloadutils import PlexAPI from PlexFunctions import GetMachineIdentifier +import state ############################################################################### @@ -24,8 +24,7 @@ log = logging.getLogger("PLEX."+__name__) ############################################################################### -@ThreadMethodsAdditionalSuspend('suspend_Userclient') -@ThreadMethods +@ThreadMethods(add_suspends=[state.SUSPEND_USER_CLIENT]) class UserClient(threading.Thread): # Borg - multiple instances, shared state @@ -118,25 +117,6 @@ class UserClient(threading.Thread): def hasAccess(self): # Plex: always return True for now return True - # hasAccess is verified in service.py - url = "{server}/emby/Users?format=json" - result = self.doUtils.downloadUrl(url) - - if result is False: - # Access is restricted, set in downloadutils.py via exception - log.info("Access is restricted.") - self.HasAccess = False - - elif window('plex_online') != "true": - # Server connection failed - pass - - elif window('plex_serverStatus') == "restricted": - log.info("Access is granted.") - self.HasAccess = True - window('plex_serverStatus', clear=True) - xbmcgui.Dialog().notification(lang(29999), - lang(33007)) def loadCurrUser(self, username, userId, usertoken, authenticated=False): log.debug('Loading current user') @@ -171,7 +151,10 @@ class UserClient(threading.Thread): # This is the token for plex.tv for the current user # Is only '' if user is not signed in to plex.tv window('plex_token', value=settings('plexToken')) + state.PLEX_TOKEN = settings('plexToken') or None window('plex_restricteduser', value=settings('plex_restricteduser')) + state.RESTRICTED_USER = True \ + if settings('plex_restricteduser') == 'true' else False window('pms_server', value=self.currServer) window('plex_machineIdentifier', value=self.machineIdentifier) window('plex_servername', value=self.servername) @@ -202,7 +185,7 @@ class UserClient(threading.Thread): # Give attempts at entering password / selecting user if self.retry >= 2: log.error("Too many retries to login.") - window('plex_serverStatus', value="Stop") + state.PMS_STATUS = 'Stop' dialog.ok(lang(33001), lang(39023)) xbmc.executebuiltin( @@ -284,6 +267,7 @@ class UserClient(threading.Thread): window('plex_authenticated', clear=True) window('pms_token', clear=True) + state.PLEX_TOKEN = None window('plex_token', clear=True) window('pms_server', clear=True) window('plex_machineIdentifier', clear=True) @@ -291,6 +275,7 @@ class UserClient(threading.Thread): window('currUserId', clear=True) window('plex_username', clear=True) window('plex_restricteduser', clear=True) + state.RESTRICTED_USER = False settings('username', value='') settings('userid', value='') @@ -310,32 +295,32 @@ class UserClient(threading.Thread): def run(self): log.info("----===## Starting UserClient ##===----") - while not self.threadStopped(): - while self.threadSuspended(): - if self.threadStopped(): + thread_stopped = self.thread_stopped + thread_suspended = self.thread_suspended + while not thread_stopped(): + while thread_suspended(): + if thread_stopped(): break xbmc.sleep(1000) - status = window('plex_serverStatus') - - if status == "Stop": + if state.PMS_STATUS == "Stop": xbmc.sleep(500) continue # Verify the connection status to server - elif status == "restricted": + elif state.PMS_STATUS == "restricted": # Parental control is restricting access self.HasAccess = False - elif status == "401": + elif state.PMS_STATUS == "401": # Unauthorized access, revoke token - window('plex_serverStatus', value="Auth") + state.PMS_STATUS = 'Auth' self.resetClient() xbmc.sleep(3000) if self.auth and (self.currUser is None): # Try to authenticate user - if not status or status == "Auth": + if not state.PMS_STATUS or state.PMS_STATUS == "Auth": # Set auth flag because we no longer need # to authenticate the user self.auth = False @@ -345,8 +330,9 @@ class UserClient(threading.Thread): log.info("Current user: %s" % self.currUser) log.info("Current userId: %s" % self.currUserId) self.retry = 0 - window('suspend_LibraryThread', clear=True) + state.SUSPEND_LIBRARY_THREAD = False window('plex_serverStatus', clear=True) + state.PMS_STATUS = False if not self.auth and (self.currUser is None): # Loop if no server found @@ -354,7 +340,7 @@ class UserClient(threading.Thread): # The status Stop is for when user cancelled password dialog. # Or retried too many times - if server and status != "Stop": + if server and state.PMS_STATUS != "Stop": # Only if there's information found to login log.debug("Server found: %s" % server) self.auth = True @@ -362,5 +348,4 @@ class UserClient(threading.Thread): # Minimize CPU load xbmc.sleep(100) - self.doUtils.stopSession() log.info("##===---- UserClient Stopped ----===##") diff --git a/resources/lib/utils.py b/resources/lib/utils.py index 32233f75..f362a957 100644 --- a/resources/lib/utils.py +++ b/resources/lib/utils.py @@ -11,7 +11,7 @@ from StringIO import StringIO from time import localtime, strftime, strptime from unicodedata import normalize import xml.etree.ElementTree as etree -from functools import wraps +from functools import wraps, partial from calendar import timegm from os.path import join from os import remove, walk, makedirs @@ -25,6 +25,7 @@ from xbmcvfs import exists, delete from variables import DB_VIDEO_PATH, DB_MUSIC_PATH, DB_TEXTURE_PATH, \ DB_PLEX_PATH, KODI_PROFILE, KODIVERSION +import state ############################################################################### @@ -76,6 +77,19 @@ def pickl_window(property, value=None, clear=False, windowid=10000): return win.getProperty(property) +def plex_command(key, value): + """ + Used to funnel states between different Python instances. NOT really thread + safe - let's hope the Kodi user can't click fast enough + + key: state.py variable + value: either 'True' or 'False' + """ + while window('plex_command'): + xbmc.sleep(1) + window('plex_command', value='%s-%s' % (key, value)) + + def settings(setting, value=None): """ Get or add addon setting. Returns unicode @@ -319,7 +333,7 @@ def reset(): return # first stop any db sync - window('plex_shouldStop', value="true") + plex_command('STOP_SYNC', 'True') count = 10 while window('plex_dbScan') == "true": log.debug("Sync is running, will retry: %s..." % count) @@ -906,78 +920,61 @@ def LogTime(func): return wrapper -def ThreadMethodsAdditionalStop(windowAttribute): - """ - Decorator to replace stopThread method to include the Kodi windowAttribute - - Use with any sync threads. @ThreadMethods still required FIRST - """ - def wrapper(cls): - def threadStopped(self): - return (self._threadStopped or - (window('plex_terminateNow') == "true") or - window(windowAttribute) == "true") - cls.threadStopped = threadStopped - return cls - return wrapper - - -def ThreadMethodsAdditionalSuspend(windowAttribute): - """ - Decorator to replace threadSuspended(): thread now also suspends if a - Kodi windowAttribute is set to 'true', e.g. 'suspend_LibraryThread' - - Use with any library sync threads. @ThreadMethods still required FIRST - """ - def wrapper(cls): - def threadSuspended(self): - return (self._threadSuspended or - window(windowAttribute) == 'true') - cls.threadSuspended = threadSuspended - return cls - return wrapper - - -def ThreadMethods(cls): +def ThreadMethods(cls=None, add_stops=None, add_suspends=None): """ Decorator to add the following methods to a threading class: - suspendThread(): pauses the thread - resumeThread(): resumes the thread - stopThread(): stopps/kills the thread + suspend_thread(): pauses the thread + resume_thread(): resumes the thread + stop_thread(): stopps/kills the thread - threadSuspended(): returns True if thread is suspend_thread - threadStopped(): returns True if thread is stopped (or should stop ;-)) - ALSO stops if Kodi is exited + thread_suspended(): returns True if thread is suspend_thread + thread_stopped(): returns True if thread is stopped (or should stop ;-)) + ALSO stops if PKC should exit Also adds the following class attributes: - _threadStopped - _threadSuspended + _thread_stopped + _thread_suspended + + invoke with either + @NewThreadMethods + class MyClass(): + or + @NewThreadMethods(add_stops=[state.SUSPEND_LIBRARY_TRHEAD], + add_suspends=[state.WHATEVER, state.WHATEVER2]) + class MyClass(): """ + if cls is None: + return partial(ThreadMethods, + add_stops=add_stops, + add_suspends=add_suspends) + # Make sure we have an iterable + add_stops = add_stops or [] + add_suspends = add_suspends or [] # Attach new attributes to class - cls._threadStopped = False - cls._threadSuspended = False + cls._thread_stopped = False + cls._thread_suspended = False # Define new class methods and attach them to class - def stopThread(self): - self._threadStopped = True - cls.stopThread = stopThread + def stop_thread(self): + self._thread_stopped = True + cls.stop_thread = stop_thread - def suspendThread(self): - self._threadSuspended = True - cls.suspendThread = suspendThread + def suspend_thread(self): + self._thread_suspended = True + cls.suspend_thread = suspend_thread - def resumeThread(self): - self._threadSuspended = False - cls.resumeThread = resumeThread + def resume_thread(self): + self._thread_suspended = False + cls.resume_thread = resume_thread - def threadSuspended(self): - return self._threadSuspended - cls.threadSuspended = threadSuspended + def thread_suspended(self): + return self._thread_suspended or any(add_suspends) + cls.thread_suspended = thread_suspended - def threadStopped(self): - return self._threadStopped or (window('plex_terminateNow') == 'true') - cls.threadStopped = threadStopped + def thread_stopped(self): + return self._thread_stopped or state.STOP_PKC or any(add_stops) + cls.thread_stopped = thread_stopped # Return class to render this a decorator return cls diff --git a/resources/lib/websocket_client.py b/resources/lib/websocket_client.py index 7ded4456..e04a7f6e 100644 --- a/resources/lib/websocket_client.py +++ b/resources/lib/websocket_client.py @@ -11,9 +11,9 @@ from ssl import CERT_NONE from xbmc import sleep -from utils import window, settings, ThreadMethodsAdditionalSuspend, \ - ThreadMethods +from utils import window, settings, ThreadMethods from companion import process_command +import state ############################################################################### @@ -22,8 +22,7 @@ log = logging.getLogger("PLEX."+__name__) ############################################################################### -@ThreadMethodsAdditionalSuspend('suspend_LibraryThread') -@ThreadMethods +@ThreadMethods(add_suspends=[state.SUSPEND_LIBRARY_THREAD]) class WebSocket(Thread): opcode_data = (websocket.ABNF.OPCODE_TEXT, websocket.ABNF.OPCODE_BINARY) @@ -62,11 +61,11 @@ class WebSocket(Thread): counter = 0 handshake_counter = 0 - threadStopped = self.threadStopped - threadSuspended = self.threadSuspended - while not threadStopped(): + thread_stopped = self.thread_stopped + thread_suspended = self.thread_suspended + while not thread_stopped(): # In the event the server goes offline - while threadSuspended(): + while thread_suspended(): # Set in service.py if self.ws is not None: try: @@ -74,7 +73,7 @@ class WebSocket(Thread): except: pass self.ws = None - if threadStopped(): + if thread_stopped(): # Abort was requested while waiting. We should exit log.info("##===---- %s Stopped ----===##" % self.__class__.__name__) @@ -160,16 +159,15 @@ class PMS_Websocket(WebSocket): def getUri(self): server = window('pms_server') - # Need to use plex.tv token, if any. NOT user token - token = window('plex_token') # Get the appropriate prefix for the websocket if server.startswith('https'): server = "wss%s" % server[5:] else: server = "ws%s" % server[4:] uri = "%s/:/websockets/notifications" % server - if token: - uri += '?X-Plex-Token=%s' % token + # Need to use plex.tv token, if any. NOT user token + if state.PLEX_TOKEN: + uri += '?X-Plex-Token=%s' % state.PLEX_TOKEN sslopt = {} if settings('sslverify') == "false": sslopt["cert_reqs"] = CERT_NONE @@ -218,9 +216,7 @@ class Alexa_Websocket(WebSocket): def getUri(self): self.plex_client_Id = window('plex_client_Id') uri = ('wss://pubsub.plex.tv/sub/websockets/%s/%s?X-Plex-Token=%s' - % (window('currUserId'), - self.plex_client_Id, - window('plex_token'))) + % (window('currUserId'), self.plex_client_Id, state.PLEX_TOKEN)) sslopt = {} log.debug("Uri: %s, sslopt: %s" % (uri, sslopt)) return uri, sslopt @@ -252,11 +248,10 @@ class Alexa_Websocket(WebSocket): def IOError_response(self): pass - def threadSuspended(self): + def thread_suspended(self): """ Overwrite to ignore library sync stuff and allow to check for - plex_restricteduser + RESTRICTED_USER and PLEX_TOKEN """ - return (self._threadSuspended or - window('plex_restricteduser') == 'true' or - not window('plex_token')) + return self._thread_suspended or state.RESTRICTED_USER \ + or not state.PLEX_TOKEN diff --git a/service.py b/service.py index 176607dd..ef174ce2 100644 --- a/service.py +++ b/service.py @@ -42,10 +42,11 @@ from playqueue import Playqueue import PlexAPI from PlexCompanion import PlexCompanion -from monitor_kodi_play import Monitor_Kodi_Play +from command_pipeline import Monitor_Window from playback_starter import Playback_Starter from artwork import Image_Cache_Thread import variables as v +import state ############################################################################### @@ -105,7 +106,6 @@ class Service(): # Reset window props for profile switch properties = [ - "plex_online", "plex_serverStatus", "plex_onWake", "plex_dbCheck", "plex_kodiScan", "plex_shouldStop", "currUserId", "plex_dbScan", @@ -113,10 +113,9 @@ class Service(): "plex_runLibScan", "plex_username", "pms_token", "plex_token", "pms_server", "plex_machineIdentifier", "plex_servername", "plex_authenticated", "PlexUserImage", "useDirectPaths", - "suspend_LibraryThread", "plex_terminateNow", "kodiplextimeoffset", "countError", "countUnauthorized", "plex_restricteduser", "plex_allows_mediaDeletion", - "plex_play_new_item", "plex_result", "plex_force_transcode_pix" + "plex_command", "plex_result", "plex_force_transcode_pix" ] for prop in properties: window(prop, clear=True) @@ -141,8 +140,8 @@ class Service(): kodiProfile = v.KODI_PROFILE # Detect playback start early on - self.monitor_kodi_play = Monitor_Kodi_Play(self) - self.monitor_kodi_play.start() + self.command_pipeline = Monitor_Window(self) + self.command_pipeline.start() # Server auto-detect initialsetup.InitialSetup().setup() @@ -261,7 +260,7 @@ class Service(): self.server_online = False window('plex_online', value="false") # Suspend threads - window('suspend_LibraryThread', value='true') + state.SUSPEND_LIBRARY_THREAD = True log.error("Plex Media Server went offline") if settings('show_pms_offline') == 'true': dialog('notification', @@ -301,7 +300,7 @@ class Service(): if window('plex_authenticated') == 'true': # Server got offline when we were authenticated. # Hence resume threads - window('suspend_LibraryThread', clear=True) + state.SUSPEND_LIBRARY_THREAD = False # Start the userclient thread if not self.user_running: @@ -321,27 +320,7 @@ class Service(): # Terminating PlexKodiConnect # Tell all threads to terminate (e.g. several lib sync threads) - window('plex_terminateNow', value='true') - try: - self.plexCompanion.stopThread() - except: - log.warn('plexCompanion already shut down') - try: - self.library.stopThread() - except: - log.warn('Library sync already shut down') - try: - self.ws.stopThread() - except: - log.warn('Websocket client already shut down') - try: - self.alexa.stopThread() - except: - log.warn('Websocket client already shut down') - try: - self.user.stopThread() - except: - log.warn('User client already shut down') + state.STOP_PKC = True try: downloadutils.DownloadUtils().stopSession() except: @@ -349,6 +328,7 @@ class Service(): window('plex_service_started', clear=True) log.warn("======== STOP %s ========" % v.ADDON_NAME) + # Safety net - Kody starts PKC twice upon first installation! if window('plex_service_started') == 'true': exit = True