diff --git a/default.py b/default.py index 96983316..9f30f61c 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_command'): + while window('plex_play_new_item'): sleep(50) - window('plex_command', - value='play_%s' % argv[2]) + window('plex_play_new_item', + value='%s%s' % ('play', 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 282c5104..fbd28e7c 100644 --- a/resources/lib/PlexAPI.py +++ b/resources/lib/PlexAPI.py @@ -53,7 +53,6 @@ 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 ############################################################################### @@ -880,8 +879,6 @@ 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' @@ -2566,8 +2563,7 @@ class API(): if forceCheck is False: # Validate the path is correct with user intervention if self.askToValidate(path): - import state - state.STOP_SYNC = True + window('plex_shouldStop', value="true") path = None window('plex_pathverified', value='true') else: diff --git a/resources/lib/PlexCompanion.py b/resources/lib/PlexCompanion.py index 48f6e831..9b961c5f 100644 --- a/resources/lib/PlexCompanion.py +++ b/resources/lib/PlexCompanion.py @@ -7,14 +7,13 @@ from urllib import urlencode from xbmc import sleep, executebuiltin -from utils import settings, ThreadMethods +from utils import settings, ThreadMethodsAdditionalSuspend, 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 ############################################################################### @@ -23,7 +22,8 @@ log = logging.getLogger("PLEX."+__name__) ############################################################################### -@ThreadMethods(add_suspends=[state.PMS_STATUS]) +@ThreadMethodsAdditionalSuspend('plex_serverStatus') +@ThreadMethods class PlexCompanion(Thread): """ """ @@ -164,8 +164,8 @@ class PlexCompanion(Thread): httpd = self.httpd # Cache for quicker while loops client = self.client - thread_stopped = self.thread_stopped - thread_suspended = self.thread_suspended + threadStopped = self.threadStopped + threadSuspended = self.threadSuspended # Start up instances requestMgr = httppersist.RequestMgr() @@ -213,12 +213,12 @@ class PlexCompanion(Thread): if httpd: t = Thread(target=httpd.handle_request) - while not thread_stopped(): + while not threadStopped(): # If we are not authorized, sleep # Otherwise, we trigger a download which leads to a # re-authorizations - while thread_suspended(): - if thread_stopped(): + while threadSuspended(): + if threadStopped(): break sleep(1000) try: diff --git a/resources/lib/artwork.py b/resources/lib/artwork.py index 4a6e352d..5d3ba04f 100644 --- a/resources/lib/artwork.py +++ b/resources/lib/artwork.py @@ -13,8 +13,7 @@ from xbmc import executeJSONRPC, sleep, translatePath from xbmcvfs import exists from utils import window, settings, language as lang, kodiSQL, tryEncode, \ - ThreadMethods, dialog, exists_dir -import state + ThreadMethods, ThreadMethodsAdditionalStop, dialog, exists_dir # Disable annoying requests warnings import requests.packages.urllib3 @@ -127,8 +126,8 @@ def double_urldecode(text): return unquote(unquote(text)) -@ThreadMethods(add_stops=[state.STOP_SYNC], - add_suspends=[state.SUSPEND_LIBRARY_THREAD, state.DB_SCAN]) +@ThreadMethodsAdditionalStop('plex_shouldStop') +@ThreadMethods class Image_Cache_Thread(Thread): xbmc_host = 'localhost' xbmc_port, xbmc_username, xbmc_password = setKodiWebServerDetails() @@ -141,16 +140,22 @@ 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): - thread_stopped = self.thread_stopped - thread_suspended = self.thread_suspended + threadStopped = self.threadStopped + threadSuspended = self.threadSuspended queue = self.queue sleep_between = self.sleep_between - while not thread_stopped(): + while not threadStopped(): # In the event the server goes offline - while thread_suspended(): + while threadSuspended(): # Set in service.py - if thread_stopped(): + if threadStopped(): # Abort was requested while waiting. We should exit log.info("---===### Stopped Image_Cache_Thread ###===---") return @@ -173,7 +178,7 @@ class Image_Cache_Thread(Thread): # download. All is well break except requests.ConnectionError: - if thread_stopped(): + if threadStopped(): # 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 deleted file mode 100644 index 9be330a6..00000000 --- a/resources/lib/command_pipeline.py +++ /dev/null @@ -1,68 +0,0 @@ -# -*- 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 d1c07d9f..8d78d8ba 100644 --- a/resources/lib/downloadutils.py +++ b/resources/lib/downloadutils.py @@ -9,8 +9,6 @@ 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 @@ -276,11 +274,10 @@ class DownloadUtils(): self.unauthorizedAttempts): log.warn('We seem to be truly unauthorized for PMS' ' %s ' % url) - if state.PMS_STATUS not in ('401', 'Auth'): + if window('plex_serverStatus') 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 3bdfac4d..8ab911cd 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, plex_command + CatchExceptions, JSONRPC, exists_dir 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') - plex_command('SUSPEND_USER_CLIENT', 'False') - plex_command('SUSPEND_LIBRARY_THREAD', 'False') + window('suspend_Userclient', clear=True) + window('suspend_LibraryThread', clear=True) return log.info("User chose server %s" % server['name']) @@ -81,7 +81,6 @@ 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') @@ -101,7 +100,7 @@ def resetAuth(): resp = dialog('yesno', heading="{plex}", line1=lang(39206)) if resp == 1: log.info("Reset login attempts.") - plex_command('PMS_STATUS', 'Auth') + window('plex_serverStatus', value="Auth") else: executebuiltin('Addon.OpenSettings(plugin.video.plexkodiconnect)') @@ -965,19 +964,22 @@ def enterPMS(): def __LogIn(): """ - Resets (clears) window properties to enable (re-)login + Resets (clears) window properties to enable (re-)login: + suspend_Userclient + plex_runLibScan: set to 'full' to trigger lib sync - SUSPEND_LIBRARY_THREAD is set to False in service.py if user was signed - out! + suspend_LibraryThread is cleared in service.py if user was signed out! """ window('plex_runLibScan', value='full') # Restart user client - plex_command('SUSPEND_USER_CLIENT', 'False') + window('suspend_Userclient', clear=True) def __LogOut(): """ - Finishes lib scans, logs out user. + Finishes lib scans, logs out user. The following window attributes are set: + suspend_LibraryThread: 'true' + suspend_Userclient: 'true' Returns True if successfully signed out, False otherwise """ @@ -989,7 +991,7 @@ def __LogOut(): time=3000, sound=False) # Pause library sync thread - plex_command('SUSPEND_LIBRARY_THREAD', 'True') + window('suspend_LibraryThread', value='true') # Wait max for 10 seconds for all lib scans to shutdown counter = 0 while window('plex_dbScan') == 'true': @@ -997,18 +999,17 @@ 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 - plex_command('SUSPEND_LIBRARY_THREAD', 'False') + window('suspend_LibraryThread', clear=True) log.error("Could not stop library sync, aborting") return False counter += 1 sleep(50) log.debug("Successfully stopped library sync") - counter = 0 # Log out currently signed in user: - window('plex_serverStatus', value='401') - plex_command('PMS_STATUS', '401') + window('plex_serverStatus', value="401") # Above method needs to have run its course! Hence wait + counter = 0 while window('plex_serverStatus') == "401": if counter > 100: # 'Failed to reset PKC. Try to restart Kodi.' @@ -1018,5 +1019,5 @@ def __LogOut(): counter += 1 sleep(50) # Suspend the user client during procedure - plex_command('SUSPEND_USER_CLIENT', 'True') + window('suspend_Userclient', value='true') return True diff --git a/resources/lib/initialsetup.py b/resources/lib/initialsetup.py index 9c41f924..54bd9940 100644 --- a/resources/lib/initialsetup.py +++ b/resources/lib/initialsetup.py @@ -13,7 +13,6 @@ from userclient import UserClient from PlexAPI import PlexAPI from PlexFunctions import GetMachineIdentifier, get_PMS_settings -import state ############################################################################### @@ -497,7 +496,7 @@ class InitialSetup(): # Open Settings page now? You will need to restart! goToSettings = dialog.yesno(heading=lang(29999), line1=lang(39017)) if goToSettings: - state.PMS_STATUS = 'Stop' + window('plex_serverStatus', value="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 483da89a..7f9fc074 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 ThreadMethods, window +from utils import ThreadMethodsAdditionalStop, ThreadMethods, window, \ + ThreadMethodsAdditionalSuspend import plexdb_functions as plexdb import itemtypes import variables as v -import state ############################################################################### @@ -18,8 +18,9 @@ log = getLogger("PLEX."+__name__) ############################################################################### -@ThreadMethods(add_suspends=[state.SUSPEND_LIBRARY_THREAD, state.DB_SCAN], - add_stops=[state.STOP_SYNC]) +@ThreadMethodsAdditionalSuspend('suspend_LibraryThread') +@ThreadMethodsAdditionalStop('plex_shouldStop') +@ThreadMethods class Process_Fanart_Thread(Thread): """ Threaded download of additional fanart in the background @@ -54,14 +55,14 @@ class Process_Fanart_Thread(Thread): Do the work """ log.debug("---===### Starting FanartSync ###===---") - thread_stopped = self.thread_stopped - thread_suspended = self.thread_suspended + threadStopped = self.threadStopped + threadSuspended = self.threadSuspended queue = self.queue - while not thread_stopped(): + while not threadStopped(): # In the event the server goes offline - while thread_suspended(): + while threadSuspended() or window('plex_dbScan'): # Set in service.py - if thread_stopped(): + if threadStopped(): # 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 4aa44266..5fd25859 100644 --- a/resources/lib/library_sync/get_metadata.py +++ b/resources/lib/library_sync/get_metadata.py @@ -5,10 +5,9 @@ from Queue import Empty from xbmc import sleep -from utils import ThreadMethods, window +from utils import ThreadMethodsAdditionalStop, ThreadMethods, window from PlexFunctions import GetPlexMetadata, GetAllPlexChildren import sync_info -import state ############################################################################### @@ -17,7 +16,8 @@ log = getLogger("PLEX."+__name__) ############################################################################### -@ThreadMethods(add_stops=[state.SUSPEND_LIBRARY_THREAD]) +@ThreadMethodsAdditionalStop('suspend_LibraryThread') +@ThreadMethods 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.thread_stopped(): + if self.threadStopped(): # 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 - thread_stopped = self.thread_stopped - while thread_stopped() is False: + threadStopped = self.threadStopped + while threadStopped() 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 7b44ed33..e6765b41 100644 --- a/resources/lib/library_sync/process_metadata.py +++ b/resources/lib/library_sync/process_metadata.py @@ -5,18 +5,19 @@ from Queue import Empty from xbmc import sleep -from utils import ThreadMethods +from utils import ThreadMethodsAdditionalStop, ThreadMethods import itemtypes import sync_info -import state ############################################################################### + log = getLogger("PLEX."+__name__) ############################################################################### -@ThreadMethods(add_stops=[state.SUSPEND_LIBRARY_THREAD]) +@ThreadMethodsAdditionalStop('suspend_LibraryThread') +@ThreadMethods class Threaded_Process_Metadata(Thread): """ Not yet implemented for more than 1 thread - if ever. Only to be called by @@ -69,9 +70,9 @@ class Threaded_Process_Metadata(Thread): item_fct = getattr(itemtypes, self.item_type) # cache local variables because it's faster queue = self.queue - thread_stopped = self.thread_stopped + threadStopped = self.threadStopped with item_fct() as item_class: - while thread_stopped() is False: + while threadStopped() 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 13caec52..3be8f70b 100644 --- a/resources/lib/library_sync/sync_info.py +++ b/resources/lib/library_sync/sync_info.py @@ -4,8 +4,7 @@ from threading import Thread, Lock from xbmc import sleep -from utils import ThreadMethods, language as lang -import state +from utils import ThreadMethodsAdditionalStop, ThreadMethods, language as lang ############################################################################### @@ -19,7 +18,8 @@ LOCK = Lock() ############################################################################### -@ThreadMethods(add_stops=[state.SUSPEND_LIBRARY_THREAD]) +@ThreadMethodsAdditionalStop('suspend_LibraryThread') +@ThreadMethods 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 - thread_stopped = self.thread_stopped + threadStopped = self.threadStopped dialog.create("%s %s: %s %s" % (lang(39714), self.item_type, str(total), lang(39715))) total = 2 * total totalProgress = 0 - while thread_stopped() is False: + while threadStopped() 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 c1362531..60e1a0e9 100644 --- a/resources/lib/librarysync.py +++ b/resources/lib/librarysync.py @@ -10,9 +10,10 @@ import xbmcgui from xbmcvfs import exists from utils import window, settings, getUnixTimestamp, sourcesXML,\ - ThreadMethods, create_actor_db_index, dialog, LogTime, getScreensaver,\ + ThreadMethods, ThreadMethodsAdditionalStop, LogTime, getScreensaver,\ setScreensaver, playlistXSP, language as lang, DateToKodi, reset,\ - advancedsettings_tweaks, tryDecode, deletePlaylists, deleteNodes + advancedsettings_tweaks, tryDecode, deletePlaylists, deleteNodes, \ + ThreadMethodsAdditionalSuspend, create_actor_db_index, dialog import downloadutils import itemtypes import plexdb_functions as plexdb @@ -29,7 +30,6 @@ 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,8 +38,9 @@ log = logging.getLogger("PLEX."+__name__) ############################################################################### -@ThreadMethods(add_stops=[state.STOP_SYNC], - add_suspends=[state.SUSPEND_LIBRARY_THREAD]) +@ThreadMethodsAdditionalSuspend('suspend_LibraryThread') +@ThreadMethodsAdditionalStop('plex_shouldStop') +@ThreadMethods class LibrarySync(Thread): """ """ @@ -299,7 +300,7 @@ class LibrarySync(Thread): # Do the processing for itemtype in process: - if self.thread_stopped(): + if self.threadStopped(): xbmc.executebuiltin('InhibitIdleShutdown(false)') setScreensaver(value=screensaver) return False @@ -322,7 +323,7 @@ class LibrarySync(Thread): window('plex_scancrashed', clear=True) elif window('plex_scancrashed') == '401': window('plex_scancrashed', clear=True) - if state.PMS_STATUS not in ('401', 'Auth'): + if window('plex_serverStatus') not in ('401', 'Auth'): # Plex server had too much and returned ERROR self.dialog.ok(lang(29999), lang(39409)) @@ -758,8 +759,8 @@ class LibrarySync(Thread): for thread in threads: # Threads might already have quit by themselves (e.g. Kodi exit) try: - thread.stop_thread() - except AttributeError: + thread.stopThread() + except: pass log.debug("Stop sent to all threads") # Wait till threads are indeed dead @@ -804,7 +805,7 @@ class LibrarySync(Thread): # PROCESS MOVIES ##### self.updatelist = [] for view in views: - if self.thread_stopped(): + if self.threadStopped(): return False # Get items per view viewId = view['id'] @@ -825,7 +826,7 @@ class LibrarySync(Thread): log.info("Processed view") # Update viewstate for EVERY item for view in views: - if self.thread_stopped(): + if self.threadStopped(): return False self.PlexUpdateWatched(view['id'], itemType) @@ -897,7 +898,7 @@ class LibrarySync(Thread): # PROCESS TV Shows ##### self.updatelist = [] for view in views: - if self.thread_stopped(): + if self.threadStopped(): return False # Get items per view viewId = view['id'] @@ -926,7 +927,7 @@ class LibrarySync(Thread): # PROCESS TV Seasons ##### # Cycle through tv shows for tvShowId in allPlexTvShowsId: - if self.thread_stopped(): + if self.threadStopped(): return False # Grab all seasons to tvshow from PMS seasons = GetAllPlexChildren(tvShowId) @@ -951,7 +952,7 @@ class LibrarySync(Thread): # PROCESS TV Episodes ##### # Cycle through tv shows for view in views: - if self.thread_stopped(): + if self.threadStopped(): return False # Grab all episodes to tvshow from PMS episodes = GetAllPlexLeaves(view['id']) @@ -986,7 +987,7 @@ class LibrarySync(Thread): # Update viewstate: for view in views: - if self.thread_stopped(): + if self.threadStopped(): return False self.PlexUpdateWatched(view['id'], itemType) @@ -1023,7 +1024,7 @@ class LibrarySync(Thread): for kind in (v.PLEX_TYPE_ARTIST, v.PLEX_TYPE_ALBUM, v.PLEX_TYPE_SONG): - if self.thread_stopped(): + if self.threadStopped(): return False log.debug("Start processing music %s" % kind) self.allKodiElementsId = {} @@ -1040,7 +1041,7 @@ class LibrarySync(Thread): # Update viewstate for EVERY item for view in views: - if self.thread_stopped(): + if self.threadStopped(): return False self.PlexUpdateWatched(view['id'], itemType) @@ -1065,7 +1066,7 @@ class LibrarySync(Thread): except ValueError: pass for view in views: - if self.thread_stopped(): + if self.threadStopped(): return False # Get items per view itemsXML = GetPlexSectionResults(view['id'], args=urlArgs) @@ -1171,7 +1172,7 @@ class LibrarySync(Thread): now = getUnixTimestamp() deleteListe = [] for i, item in enumerate(self.itemsToProcess): - if self.thread_stopped(): + if self.threadStopped(): # Chances are that Kodi gets shut down break if item['state'] == 9: @@ -1276,8 +1277,8 @@ class LibrarySync(Thread): # movie or episode) continue typus = int(item.get('type', 0)) - status = int(item.get('state', 0)) - if status == 9 or (typus in (1, 4, 10) and status == 5): + state = int(item.get('state', 0)) + if state == 9 or (typus in (1, 4, 10) and state == 5): # Only process deleted items OR movies, episodes, tracks/songs plex_id = str(item.get('itemID', '0')) if plex_id == '0': @@ -1285,7 +1286,7 @@ class LibrarySync(Thread): continue try: if (now - self.just_processed[plex_id] < - self.ignore_just_processed and status != 9): + self.ignore_just_processed and state != 9): log.debug('We just processed %s: ignoring' % plex_id) continue except KeyError: @@ -1298,7 +1299,7 @@ class LibrarySync(Thread): else: # Haven't added this element to the queue yet self.itemsToProcess.append({ - 'state': status, + 'state': state, 'type': typus, 'ratingKey': plex_id, 'timestamp': getUnixTimestamp(), @@ -1314,8 +1315,8 @@ class LibrarySync(Thread): with plexdb.Get_Plex_DB() as plex_db: for item in data: # Drop buffering messages immediately - status = item.get('state') - if status == 'buffering': + state = item.get('state') + if state == 'buffering': continue ratingKey = item.get('ratingKey') kodiInfo = plex_db.getItem_byId(ratingKey) @@ -1334,7 +1335,8 @@ class LibrarySync(Thread): } else: # PMS is ours - get all current sessions - self.sessionKeys = GetPMSStatus(state.PLEX_TOKEN) + self.sessionKeys = GetPMSStatus( + window('plex_token')) log.debug('Updated current sessions. They are: %s' % self.sessionKeys) if sessionKey not in self.sessionKeys: @@ -1347,7 +1349,8 @@ 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 (not state.PLEX_TOKEN and currSess['userId'] == '1'): + if (window('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 @@ -1391,7 +1394,7 @@ class LibrarySync(Thread): 'file_id': kodiInfo[1], 'kodi_type': kodiInfo[4], 'viewOffset': resume, - 'state': status, + 'state': state, 'duration': currSess['duration'], 'viewCount': currSess['viewCount'], 'lastViewedAt': DateToKodi(getUnixTimestamp()) @@ -1430,7 +1433,6 @@ 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 @@ -1441,8 +1443,8 @@ class LibrarySync(Thread): def run_internal(self): # Re-assign handles to have faster calls - thread_stopped = self.thread_stopped - thread_suspended = self.thread_suspended + threadStopped = self.threadStopped + threadSuspended = self.threadSuspended installSyncDone = self.installSyncDone enableBackgroundSync = self.enableBackgroundSync fullSync = self.fullSync @@ -1474,12 +1476,12 @@ class LibrarySync(Thread): if settings('FanartTV') == 'true': self.fanartthread.start() - while not thread_stopped(): + while not threadStopped(): # In the event the server goes offline - while thread_suspended(): + while threadSuspended(): # Set in service.py - if thread_stopped(): + if threadStopped(): # Abort was requested while waiting. We should exit log.info("###===--- LibrarySync Stopped ---===###") return @@ -1521,7 +1523,6 @@ 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() @@ -1546,7 +1547,6 @@ 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,26 +1565,23 @@ class LibrarySync(Thread): break # Currently no db scan, so we can start a new scan - elif state.DB_SCAN is False: + elif window('plex_dbScan') != "true": # 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 @@ -1605,7 +1602,6 @@ 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) @@ -1617,37 +1613,31 @@ 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 thread_stopped(): + if fullSync() is False and not threadStopped(): 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 new file mode 100644 index 00000000..c9827fac --- /dev/null +++ b/resources/lib/monitor_kodi_play.py @@ -0,0 +1,41 @@ +# -*- 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 836f587d..b6b14c18 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.command_pipeline.playback_queue + queue = self.mgr.monitor_kodi_play.playback_queue log.info("----===## Starting Playback_Starter ##===----") while True: item = queue.get() if item is None: - # Need to shutdown - initiated by command_pipeline + # Need to shutdown - initiated by monitor_kodi_play break else: self.triage(item) diff --git a/resources/lib/playqueue.py b/resources/lib/playqueue.py index 8e52e071..a496574b 100644 --- a/resources/lib/playqueue.py +++ b/resources/lib/playqueue.py @@ -5,13 +5,12 @@ from threading import RLock, Thread from xbmc import sleep, Player, PlayList, PLAYLIST_MUSIC, PLAYLIST_VIDEO -from utils import window, ThreadMethods +from utils import window, ThreadMethods, ThreadMethodsAdditionalSuspend 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__) @@ -22,7 +21,8 @@ PLUGIN = 'plugin://%s' % v.ADDON_ID ############################################################################### -@ThreadMethods(add_suspends=[state.PMS_STATUS]) +@ThreadMethodsAdditionalSuspend('plex_serverStatus') +@ThreadMethods 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.thread_stopped(): + if self.threadStopped(): # 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): - thread_stopped = self.thread_stopped - thread_suspended = self.thread_suspended + threadStopped = self.threadStopped + threadSuspended = self.threadSuspended 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 thread_stopped(): - while thread_suspended(): - if thread_stopped(): + while not threadStopped(): + while threadSuspended(): + if threadStopped(): break sleep(1000) with lock: diff --git a/resources/lib/state.py b/resources/lib/state.py deleted file mode 100644 index 9fffcaea..00000000 --- a/resources/lib/state.py +++ /dev/null @@ -1,22 +0,0 @@ -# -*- 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 584a1d94..93f5e019 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 +from utils import window, settings, language as lang, ThreadMethods, \ + ThreadMethodsAdditionalSuspend import downloadutils import PlexAPI from PlexFunctions import GetMachineIdentifier -import state ############################################################################### @@ -24,7 +24,8 @@ log = logging.getLogger("PLEX."+__name__) ############################################################################### -@ThreadMethods(add_suspends=[state.SUSPEND_USER_CLIENT]) +@ThreadMethodsAdditionalSuspend('suspend_Userclient') +@ThreadMethods class UserClient(threading.Thread): # Borg - multiple instances, shared state @@ -117,6 +118,25 @@ 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') @@ -151,10 +171,7 @@ 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) @@ -185,7 +202,7 @@ class UserClient(threading.Thread): # Give attempts at entering password / selecting user if self.retry >= 2: log.error("Too many retries to login.") - state.PMS_STATUS = 'Stop' + window('plex_serverStatus', value="Stop") dialog.ok(lang(33001), lang(39023)) xbmc.executebuiltin( @@ -267,7 +284,6 @@ 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) @@ -275,7 +291,6 @@ 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='') @@ -295,32 +310,32 @@ class UserClient(threading.Thread): def run(self): log.info("----===## Starting UserClient ##===----") - thread_stopped = self.thread_stopped - thread_suspended = self.thread_suspended - while not thread_stopped(): - while thread_suspended(): - if thread_stopped(): + while not self.threadStopped(): + while self.threadSuspended(): + if self.threadStopped(): break xbmc.sleep(1000) - if state.PMS_STATUS == "Stop": + status = window('plex_serverStatus') + + if status == "Stop": xbmc.sleep(500) continue # Verify the connection status to server - elif state.PMS_STATUS == "restricted": + elif status == "restricted": # Parental control is restricting access self.HasAccess = False - elif state.PMS_STATUS == "401": + elif status == "401": # Unauthorized access, revoke token - state.PMS_STATUS = 'Auth' + window('plex_serverStatus', value="Auth") self.resetClient() xbmc.sleep(3000) if self.auth and (self.currUser is None): # Try to authenticate user - if not state.PMS_STATUS or state.PMS_STATUS == "Auth": + if not status or status == "Auth": # Set auth flag because we no longer need # to authenticate the user self.auth = False @@ -330,9 +345,8 @@ class UserClient(threading.Thread): log.info("Current user: %s" % self.currUser) log.info("Current userId: %s" % self.currUserId) self.retry = 0 - state.SUSPEND_LIBRARY_THREAD = False + window('suspend_LibraryThread', clear=True) window('plex_serverStatus', clear=True) - state.PMS_STATUS = False if not self.auth and (self.currUser is None): # Loop if no server found @@ -340,7 +354,7 @@ class UserClient(threading.Thread): # The status Stop is for when user cancelled password dialog. # Or retried too many times - if server and state.PMS_STATUS != "Stop": + if server and status != "Stop": # Only if there's information found to login log.debug("Server found: %s" % server) self.auth = True @@ -348,4 +362,5 @@ 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 f362a957..32233f75 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, partial +from functools import wraps from calendar import timegm from os.path import join from os import remove, walk, makedirs @@ -25,7 +25,6 @@ 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 ############################################################################### @@ -77,19 +76,6 @@ 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 @@ -333,7 +319,7 @@ def reset(): return # first stop any db sync - plex_command('STOP_SYNC', 'True') + window('plex_shouldStop', value="true") count = 10 while window('plex_dbScan') == "true": log.debug("Sync is running, will retry: %s..." % count) @@ -920,61 +906,78 @@ def LogTime(func): return wrapper -def ThreadMethods(cls=None, add_stops=None, add_suspends=None): +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): """ Decorator to add the following methods to a threading class: - suspend_thread(): pauses the thread - resume_thread(): resumes the thread - stop_thread(): stopps/kills the thread + suspendThread(): pauses the thread + resumeThread(): resumes the thread + stopThread(): stopps/kills the thread - 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 + threadSuspended(): returns True if thread is suspend_thread + threadStopped(): returns True if thread is stopped (or should stop ;-)) + ALSO stops if Kodi is exited Also adds the following class attributes: - _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(): + _threadStopped + _threadSuspended """ - 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._thread_stopped = False - cls._thread_suspended = False + cls._threadStopped = False + cls._threadSuspended = False # Define new class methods and attach them to class - def stop_thread(self): - self._thread_stopped = True - cls.stop_thread = stop_thread + def stopThread(self): + self._threadStopped = True + cls.stopThread = stopThread - def suspend_thread(self): - self._thread_suspended = True - cls.suspend_thread = suspend_thread + def suspendThread(self): + self._threadSuspended = True + cls.suspendThread = suspendThread - def resume_thread(self): - self._thread_suspended = False - cls.resume_thread = resume_thread + def resumeThread(self): + self._threadSuspended = False + cls.resumeThread = resumeThread - def thread_suspended(self): - return self._thread_suspended or any(add_suspends) - cls.thread_suspended = thread_suspended + def threadSuspended(self): + return self._threadSuspended + cls.threadSuspended = threadSuspended - def thread_stopped(self): - return self._thread_stopped or state.STOP_PKC or any(add_stops) - cls.thread_stopped = thread_stopped + def threadStopped(self): + return self._threadStopped or (window('plex_terminateNow') == 'true') + cls.threadStopped = threadStopped # Return class to render this a decorator return cls diff --git a/resources/lib/websocket_client.py b/resources/lib/websocket_client.py index e04a7f6e..7ded4456 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, ThreadMethods +from utils import window, settings, ThreadMethodsAdditionalSuspend, \ + ThreadMethods from companion import process_command -import state ############################################################################### @@ -22,7 +22,8 @@ log = logging.getLogger("PLEX."+__name__) ############################################################################### -@ThreadMethods(add_suspends=[state.SUSPEND_LIBRARY_THREAD]) +@ThreadMethodsAdditionalSuspend('suspend_LibraryThread') +@ThreadMethods class WebSocket(Thread): opcode_data = (websocket.ABNF.OPCODE_TEXT, websocket.ABNF.OPCODE_BINARY) @@ -61,11 +62,11 @@ class WebSocket(Thread): counter = 0 handshake_counter = 0 - thread_stopped = self.thread_stopped - thread_suspended = self.thread_suspended - while not thread_stopped(): + threadStopped = self.threadStopped + threadSuspended = self.threadSuspended + while not threadStopped(): # In the event the server goes offline - while thread_suspended(): + while threadSuspended(): # Set in service.py if self.ws is not None: try: @@ -73,7 +74,7 @@ class WebSocket(Thread): except: pass self.ws = None - if thread_stopped(): + if threadStopped(): # Abort was requested while waiting. We should exit log.info("##===---- %s Stopped ----===##" % self.__class__.__name__) @@ -159,15 +160,16 @@ 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 - # Need to use plex.tv token, if any. NOT user token - if state.PLEX_TOKEN: - uri += '?X-Plex-Token=%s' % state.PLEX_TOKEN + if token: + uri += '?X-Plex-Token=%s' % token sslopt = {} if settings('sslverify') == "false": sslopt["cert_reqs"] = CERT_NONE @@ -216,7 +218,9 @@ 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, state.PLEX_TOKEN)) + % (window('currUserId'), + self.plex_client_Id, + window('plex_token'))) sslopt = {} log.debug("Uri: %s, sslopt: %s" % (uri, sslopt)) return uri, sslopt @@ -248,10 +252,11 @@ class Alexa_Websocket(WebSocket): def IOError_response(self): pass - def thread_suspended(self): + def threadSuspended(self): """ Overwrite to ignore library sync stuff and allow to check for - RESTRICTED_USER and PLEX_TOKEN + plex_restricteduser """ - return self._thread_suspended or state.RESTRICTED_USER \ - or not state.PLEX_TOKEN + return (self._threadSuspended or + window('plex_restricteduser') == 'true' or + not window('plex_token')) diff --git a/service.py b/service.py index ef174ce2..176607dd 100644 --- a/service.py +++ b/service.py @@ -42,11 +42,10 @@ from playqueue import Playqueue import PlexAPI from PlexCompanion import PlexCompanion -from command_pipeline import Monitor_Window +from monitor_kodi_play import Monitor_Kodi_Play from playback_starter import Playback_Starter from artwork import Image_Cache_Thread import variables as v -import state ############################################################################### @@ -106,6 +105,7 @@ 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,9 +113,10 @@ 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_command", "plex_result", "plex_force_transcode_pix" + "plex_play_new_item", "plex_result", "plex_force_transcode_pix" ] for prop in properties: window(prop, clear=True) @@ -140,8 +141,8 @@ class Service(): kodiProfile = v.KODI_PROFILE # Detect playback start early on - self.command_pipeline = Monitor_Window(self) - self.command_pipeline.start() + self.monitor_kodi_play = Monitor_Kodi_Play(self) + self.monitor_kodi_play.start() # Server auto-detect initialsetup.InitialSetup().setup() @@ -260,7 +261,7 @@ class Service(): self.server_online = False window('plex_online', value="false") # Suspend threads - state.SUSPEND_LIBRARY_THREAD = True + window('suspend_LibraryThread', value='true') log.error("Plex Media Server went offline") if settings('show_pms_offline') == 'true': dialog('notification', @@ -300,7 +301,7 @@ class Service(): if window('plex_authenticated') == 'true': # Server got offline when we were authenticated. # Hence resume threads - state.SUSPEND_LIBRARY_THREAD = False + window('suspend_LibraryThread', clear=True) # Start the userclient thread if not self.user_running: @@ -320,7 +321,27 @@ class Service(): # Terminating PlexKodiConnect # Tell all threads to terminate (e.g. several lib sync threads) - state.STOP_PKC = True + 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') try: downloadutils.DownloadUtils().stopSession() except: @@ -328,7 +349,6 @@ 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