Code refactoring: avoid window vars in loops

This commit is contained in:
tomkat83 2017-05-12 12:52:12 +02:00
parent 85e59e3207
commit 6629fb41e3
21 changed files with 327 additions and 311 deletions

View file

@ -170,10 +170,10 @@ class Main():
Start up playback_starter in main Python thread Start up playback_starter in main Python thread
""" """
# Put the request into the 'queue' # Put the request into the 'queue'
while window('plex_play_new_item'): while window('plex_command'):
sleep(50) sleep(50)
window('plex_play_new_item', window('plex_command',
value='%s%s' % ('play', argv[2])) value='play_%s' % argv[2])
# Wait for the result # Wait for the result
while not pickl_window('plex_result'): while not pickl_window('plex_result'):
sleep(50) sleep(50)

View file

@ -53,6 +53,7 @@ from utils import window, settings, language as lang, tryDecode, tryEncode, \
from PlexFunctions import PMSHttpsEnabled from PlexFunctions import PMSHttpsEnabled
import plexdb_functions as plexdb import plexdb_functions as plexdb
import variables as v import variables as v
import state
############################################################################### ###############################################################################
@ -879,6 +880,8 @@ class PlexAPI():
settings('plex_restricteduser', settings('plex_restricteduser',
'true' if answer.attrib.get('restricted', '0') == '1' 'true' if answer.attrib.get('restricted', '0') == '1'
else 'false') else 'false')
state.RESTRICTED_USER = True if \
answer.attrib.get('restricted', '0') == '1' else False
# Get final token to the PMS we've chosen # Get final token to the PMS we've chosen
url = 'https://plex.tv/api/resources?includeHttps=1' url = 'https://plex.tv/api/resources?includeHttps=1'
@ -2563,7 +2566,8 @@ class API():
if forceCheck is False: if forceCheck is False:
# Validate the path is correct with user intervention # Validate the path is correct with user intervention
if self.askToValidate(path): if self.askToValidate(path):
window('plex_shouldStop', value="true") import state
state.STOP_SYNC = True
path = None path = None
window('plex_pathverified', value='true') window('plex_pathverified', value='true')
else: else:

View file

@ -7,13 +7,14 @@ from urllib import urlencode
from xbmc import sleep, executebuiltin from xbmc import sleep, executebuiltin
from utils import settings, ThreadMethodsAdditionalSuspend, ThreadMethods from utils import settings, ThreadMethods
from plexbmchelper import listener, plexgdm, subscribers, functions, \ from plexbmchelper import listener, plexgdm, subscribers, functions, \
httppersist, plexsettings httppersist, plexsettings
from PlexFunctions import ParseContainerKey, GetPlexMetadata from PlexFunctions import ParseContainerKey, GetPlexMetadata
from PlexAPI import API from PlexAPI import API
import player import player
import variables as v import variables as v
import state
############################################################################### ###############################################################################
@ -22,8 +23,7 @@ log = logging.getLogger("PLEX."+__name__)
############################################################################### ###############################################################################
@ThreadMethodsAdditionalSuspend('plex_serverStatus') @ThreadMethods(add_suspends=[state.PMS_STATUS])
@ThreadMethods
class PlexCompanion(Thread): class PlexCompanion(Thread):
""" """
""" """
@ -164,8 +164,8 @@ class PlexCompanion(Thread):
httpd = self.httpd httpd = self.httpd
# Cache for quicker while loops # Cache for quicker while loops
client = self.client client = self.client
threadStopped = self.threadStopped thread_stopped = self.thread_stopped
threadSuspended = self.threadSuspended thread_suspended = self.thread_suspended
# Start up instances # Start up instances
requestMgr = httppersist.RequestMgr() requestMgr = httppersist.RequestMgr()
@ -213,12 +213,12 @@ class PlexCompanion(Thread):
if httpd: if httpd:
t = Thread(target=httpd.handle_request) t = Thread(target=httpd.handle_request)
while not threadStopped(): while not thread_stopped():
# If we are not authorized, sleep # If we are not authorized, sleep
# Otherwise, we trigger a download which leads to a # Otherwise, we trigger a download which leads to a
# re-authorizations # re-authorizations
while threadSuspended(): while thread_suspended():
if threadStopped(): if thread_stopped():
break break
sleep(1000) sleep(1000)
try: try:

View file

@ -13,7 +13,8 @@ from xbmc import executeJSONRPC, sleep, translatePath
from xbmcvfs import exists from xbmcvfs import exists
from utils import window, settings, language as lang, kodiSQL, tryEncode, \ 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 # Disable annoying requests warnings
import requests.packages.urllib3 import requests.packages.urllib3
@ -126,8 +127,8 @@ def double_urldecode(text):
return unquote(unquote(text)) return unquote(unquote(text))
@ThreadMethodsAdditionalStop('plex_shouldStop') @ThreadMethods(add_stops=[state.STOP_SYNC],
@ThreadMethods add_suspends=[state.SUSPEND_LIBRARY_THREAD, state.DB_SCAN])
class Image_Cache_Thread(Thread): class Image_Cache_Thread(Thread):
xbmc_host = 'localhost' xbmc_host = 'localhost'
xbmc_port, xbmc_username, xbmc_password = setKodiWebServerDetails() xbmc_port, xbmc_username, xbmc_password = setKodiWebServerDetails()
@ -140,22 +141,16 @@ class Image_Cache_Thread(Thread):
self.queue = ARTWORK_QUEUE self.queue = ARTWORK_QUEUE
Thread.__init__(self) 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): def run(self):
threadStopped = self.threadStopped thread_stopped = self.thread_stopped
threadSuspended = self.threadSuspended thread_suspended = self.thread_suspended
queue = self.queue queue = self.queue
sleep_between = self.sleep_between sleep_between = self.sleep_between
while not threadStopped(): while not thread_stopped():
# In the event the server goes offline # In the event the server goes offline
while threadSuspended(): while thread_suspended():
# Set in service.py # Set in service.py
if threadStopped(): if thread_stopped():
# Abort was requested while waiting. We should exit # Abort was requested while waiting. We should exit
log.info("---===### Stopped Image_Cache_Thread ###===---") log.info("---===### Stopped Image_Cache_Thread ###===---")
return return
@ -178,7 +173,7 @@ class Image_Cache_Thread(Thread):
# download. All is well # download. All is well
break break
except requests.ConnectionError: except requests.ConnectionError:
if threadStopped(): if thread_stopped():
# Kodi terminated # Kodi terminated
break break
# Server thinks its a DOS attack, ('error 10053') # Server thinks its a DOS attack, ('error 10053')

View file

@ -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 ##===----")

View file

@ -9,6 +9,8 @@ import xml.etree.ElementTree as etree
from utils import settings, window, language as lang, dialog from utils import settings, window, language as lang, dialog
import clientinfo as client import clientinfo as client
import state
############################################################################### ###############################################################################
# Disable annoying requests warnings # Disable annoying requests warnings
@ -274,10 +276,11 @@ class DownloadUtils():
self.unauthorizedAttempts): self.unauthorizedAttempts):
log.warn('We seem to be truly unauthorized for PMS' log.warn('We seem to be truly unauthorized for PMS'
' %s ' % url) ' %s ' % url)
if window('plex_serverStatus') not in ('401', 'Auth'): if state.PMS_STATUS not in ('401', 'Auth'):
# Tell userclient token has been revoked. # Tell userclient token has been revoked.
log.debug('Setting PMS server status to ' log.debug('Setting PMS server status to '
'unauthorized') 'unauthorized')
state.PMS_STATUS = '401'
window('plex_serverStatus', value="401") window('plex_serverStatus', value="401")
dialog('notification', dialog('notification',
lang(29999), lang(29999),

View file

@ -12,7 +12,7 @@ from xbmc import sleep, executebuiltin, translatePath
from xbmcgui import ListItem from xbmcgui import ListItem
from utils import window, settings, language as lang, dialog, tryEncode, \ from utils import window, settings, language as lang, dialog, tryEncode, \
CatchExceptions, JSONRPC, exists_dir CatchExceptions, JSONRPC, exists_dir, plex_command
import downloadutils import downloadutils
from PlexFunctions import GetPlexMetadata, GetPlexSectionResults, \ from PlexFunctions import GetPlexMetadata, GetPlexSectionResults, \
@ -42,8 +42,8 @@ def chooseServer():
server = setup.PickPMS(showDialog=True) server = setup.PickPMS(showDialog=True)
if server is None: if server is None:
log.error('We did not connect to a new PMS, aborting') log.error('We did not connect to a new PMS, aborting')
window('suspend_Userclient', clear=True) plex_command('SUSPEND_USER_CLIENT', 'False')
window('suspend_LibraryThread', clear=True) plex_command('SUSPEND_LIBRARY_THREAD', 'False')
return return
log.info("User chose server %s" % server['name']) log.info("User chose server %s" % server['name'])
@ -81,6 +81,7 @@ def togglePlexTV():
settings('plex_status', value="Not logged in to plex.tv") settings('plex_status', value="Not logged in to plex.tv")
window('plex_token', clear=True) window('plex_token', clear=True)
plex_command('PLEX_TOKEN', '')
window('plex_username', clear=True) window('plex_username', clear=True)
else: else:
log.info('Login to plex.tv') log.info('Login to plex.tv')
@ -100,7 +101,7 @@ def resetAuth():
resp = dialog('yesno', heading="{plex}", line1=lang(39206)) resp = dialog('yesno', heading="{plex}", line1=lang(39206))
if resp == 1: if resp == 1:
log.info("Reset login attempts.") log.info("Reset login attempts.")
window('plex_serverStatus', value="Auth") plex_command('PMS_STATUS', 'Auth')
else: else:
executebuiltin('Addon.OpenSettings(plugin.video.plexkodiconnect)') executebuiltin('Addon.OpenSettings(plugin.video.plexkodiconnect)')
@ -964,22 +965,19 @@ def enterPMS():
def __LogIn(): 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_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') window('plex_runLibScan', value='full')
# Restart user client # Restart user client
window('suspend_Userclient', clear=True) plex_command('SUSPEND_USER_CLIENT', 'False')
def __LogOut(): def __LogOut():
""" """
Finishes lib scans, logs out user. The following window attributes are set: Finishes lib scans, logs out user.
suspend_LibraryThread: 'true'
suspend_Userclient: 'true'
Returns True if successfully signed out, False otherwise Returns True if successfully signed out, False otherwise
""" """
@ -991,7 +989,7 @@ def __LogOut():
time=3000, time=3000,
sound=False) sound=False)
# Pause library sync thread # 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 # Wait max for 10 seconds for all lib scans to shutdown
counter = 0 counter = 0
while window('plex_dbScan') == 'true': while window('plex_dbScan') == 'true':
@ -999,17 +997,18 @@ def __LogOut():
# Failed to reset PMS and plex.tv connects. Try to restart Kodi. # Failed to reset PMS and plex.tv connects. Try to restart Kodi.
dialog('ok', lang(29999), lang(39208)) dialog('ok', lang(29999), lang(39208))
# Resuming threads, just in case # Resuming threads, just in case
window('suspend_LibraryThread', clear=True) plex_command('SUSPEND_LIBRARY_THREAD', 'False')
log.error("Could not stop library sync, aborting") log.error("Could not stop library sync, aborting")
return False return False
counter += 1 counter += 1
sleep(50) sleep(50)
log.debug("Successfully stopped library sync") 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 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": while window('plex_serverStatus') == "401":
if counter > 100: if counter > 100:
# 'Failed to reset PKC. Try to restart Kodi.' # 'Failed to reset PKC. Try to restart Kodi.'
@ -1019,5 +1018,5 @@ def __LogOut():
counter += 1 counter += 1
sleep(50) sleep(50)
# Suspend the user client during procedure # Suspend the user client during procedure
window('suspend_Userclient', value='true') plex_command('SUSPEND_USER_CLIENT', 'True')
return True return True

View file

@ -13,6 +13,7 @@ from userclient import UserClient
from PlexAPI import PlexAPI from PlexAPI import PlexAPI
from PlexFunctions import GetMachineIdentifier, get_PMS_settings from PlexFunctions import GetMachineIdentifier, get_PMS_settings
import state
############################################################################### ###############################################################################
@ -496,7 +497,7 @@ class InitialSetup():
# Open Settings page now? You will need to restart! # Open Settings page now? You will need to restart!
goToSettings = dialog.yesno(heading=lang(29999), line1=lang(39017)) goToSettings = dialog.yesno(heading=lang(29999), line1=lang(39017))
if goToSettings: if goToSettings:
window('plex_serverStatus', value="Stop") state.PMS_STATUS = 'Stop'
xbmc.executebuiltin( xbmc.executebuiltin(
'Addon.OpenSettings(plugin.video.plexkodiconnect)') 'Addon.OpenSettings(plugin.video.plexkodiconnect)')
else: else:

View file

@ -5,11 +5,11 @@ from Queue import Empty
from xbmc import sleep from xbmc import sleep
from utils import ThreadMethodsAdditionalStop, ThreadMethods, window, \ from utils import ThreadMethods, window
ThreadMethodsAdditionalSuspend
import plexdb_functions as plexdb import plexdb_functions as plexdb
import itemtypes import itemtypes
import variables as v import variables as v
import state
############################################################################### ###############################################################################
@ -18,9 +18,8 @@ log = getLogger("PLEX."+__name__)
############################################################################### ###############################################################################
@ThreadMethodsAdditionalSuspend('suspend_LibraryThread') @ThreadMethods(add_suspends=[state.SUSPEND_LIBRARY_THREAD, state.DB_SCAN],
@ThreadMethodsAdditionalStop('plex_shouldStop') add_stops=[state.STOP_SYNC])
@ThreadMethods
class Process_Fanart_Thread(Thread): class Process_Fanart_Thread(Thread):
""" """
Threaded download of additional fanart in the background Threaded download of additional fanart in the background
@ -55,14 +54,14 @@ class Process_Fanart_Thread(Thread):
Do the work Do the work
""" """
log.debug("---===### Starting FanartSync ###===---") log.debug("---===### Starting FanartSync ###===---")
threadStopped = self.threadStopped thread_stopped = self.thread_stopped
threadSuspended = self.threadSuspended thread_suspended = self.thread_suspended
queue = self.queue queue = self.queue
while not threadStopped(): while not thread_stopped():
# In the event the server goes offline # In the event the server goes offline
while threadSuspended() or window('plex_dbScan'): while thread_suspended():
# Set in service.py # Set in service.py
if threadStopped(): if thread_stopped():
# Abort was requested while waiting. We should exit # Abort was requested while waiting. We should exit
log.info("---===### Stopped FanartSync ###===---") log.info("---===### Stopped FanartSync ###===---")
return return

View file

@ -5,9 +5,10 @@ from Queue import Empty
from xbmc import sleep from xbmc import sleep
from utils import ThreadMethodsAdditionalStop, ThreadMethods, window from utils import ThreadMethods, window
from PlexFunctions import GetPlexMetadata, GetAllPlexChildren from PlexFunctions import GetPlexMetadata, GetAllPlexChildren
import sync_info import sync_info
import state
############################################################################### ###############################################################################
@ -16,8 +17,7 @@ log = getLogger("PLEX."+__name__)
############################################################################### ###############################################################################
@ThreadMethodsAdditionalStop('suspend_LibraryThread') @ThreadMethods(add_stops=[state.SUSPEND_LIBRARY_THREAD])
@ThreadMethods
class Threaded_Get_Metadata(Thread): class Threaded_Get_Metadata(Thread):
""" """
Threaded download of Plex XML metadata for a certain library item. Threaded download of Plex XML metadata for a certain library item.
@ -48,7 +48,7 @@ class Threaded_Get_Metadata(Thread):
continue continue
else: else:
self.queue.task_done() self.queue.task_done()
if self.threadStopped(): if self.thread_stopped():
# Shutdown from outside requested; purge out_queue as well # Shutdown from outside requested; purge out_queue as well
while not self.out_queue.empty(): while not self.out_queue.empty():
# Still try because remaining item might have been taken # 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 # cache local variables because it's faster
queue = self.queue queue = self.queue
out_queue = self.out_queue out_queue = self.out_queue
threadStopped = self.threadStopped thread_stopped = self.thread_stopped
while threadStopped() is False: while thread_stopped() is False:
# grabs Plex item from queue # grabs Plex item from queue
try: try:
item = queue.get(block=False) item = queue.get(block=False)

View file

@ -5,19 +5,18 @@ from Queue import Empty
from xbmc import sleep from xbmc import sleep
from utils import ThreadMethodsAdditionalStop, ThreadMethods from utils import ThreadMethods
import itemtypes import itemtypes
import sync_info import sync_info
import state
############################################################################### ###############################################################################
log = getLogger("PLEX."+__name__) log = getLogger("PLEX."+__name__)
############################################################################### ###############################################################################
@ThreadMethodsAdditionalStop('suspend_LibraryThread') @ThreadMethods(add_stops=[state.SUSPEND_LIBRARY_THREAD])
@ThreadMethods
class Threaded_Process_Metadata(Thread): class Threaded_Process_Metadata(Thread):
""" """
Not yet implemented for more than 1 thread - if ever. Only to be called by 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) item_fct = getattr(itemtypes, self.item_type)
# cache local variables because it's faster # cache local variables because it's faster
queue = self.queue queue = self.queue
threadStopped = self.threadStopped thread_stopped = self.thread_stopped
with item_fct() as item_class: with item_fct() as item_class:
while threadStopped() is False: while thread_stopped() is False:
# grabs item from queue # grabs item from queue
try: try:
item = queue.get(block=False) item = queue.get(block=False)

View file

@ -4,7 +4,8 @@ from threading import Thread, Lock
from xbmc import sleep 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(add_stops=[state.SUSPEND_LIBRARY_THREAD])
@ThreadMethods
class Threaded_Show_Sync_Info(Thread): class Threaded_Show_Sync_Info(Thread):
""" """
Threaded class to show the Kodi statusbar of the metadata download. 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 # cache local variables because it's faster
total = self.total total = self.total
dialog = self.dialog dialog = self.dialog
threadStopped = self.threadStopped thread_stopped = self.thread_stopped
dialog.create("%s %s: %s %s" dialog.create("%s %s: %s %s"
% (lang(39714), self.item_type, str(total), lang(39715))) % (lang(39714), self.item_type, str(total), lang(39715)))
total = 2 * total total = 2 * total
totalProgress = 0 totalProgress = 0
while threadStopped() is False: while thread_stopped() is False:
with LOCK: with LOCK:
get_progress = GET_METADATA_COUNT get_progress = GET_METADATA_COUNT
process_progress = PROCESS_METADATA_COUNT process_progress = PROCESS_METADATA_COUNT

View file

@ -10,10 +10,9 @@ import xbmcgui
from xbmcvfs import exists from xbmcvfs import exists
from utils import window, settings, getUnixTimestamp, sourcesXML,\ 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,\ 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 downloadutils
import itemtypes import itemtypes
import plexdb_functions as plexdb 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 import library_sync.sync_info as sync_info
from library_sync.fanart import Process_Fanart_Thread from library_sync.fanart import Process_Fanart_Thread
import music import music
import state
############################################################################### ###############################################################################
@ -38,9 +38,8 @@ log = logging.getLogger("PLEX."+__name__)
############################################################################### ###############################################################################
@ThreadMethodsAdditionalSuspend('suspend_LibraryThread') @ThreadMethods(add_stops=[state.STOP_SYNC],
@ThreadMethodsAdditionalStop('plex_shouldStop') add_suspends=[state.SUSPEND_LIBRARY_THREAD])
@ThreadMethods
class LibrarySync(Thread): class LibrarySync(Thread):
""" """
""" """
@ -300,7 +299,7 @@ class LibrarySync(Thread):
# Do the processing # Do the processing
for itemtype in process: for itemtype in process:
if self.threadStopped(): if self.thread_stopped():
xbmc.executebuiltin('InhibitIdleShutdown(false)') xbmc.executebuiltin('InhibitIdleShutdown(false)')
setScreensaver(value=screensaver) setScreensaver(value=screensaver)
return False return False
@ -323,7 +322,7 @@ class LibrarySync(Thread):
window('plex_scancrashed', clear=True) window('plex_scancrashed', clear=True)
elif window('plex_scancrashed') == '401': elif window('plex_scancrashed') == '401':
window('plex_scancrashed', clear=True) 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 # Plex server had too much and returned ERROR
self.dialog.ok(lang(29999), lang(39409)) self.dialog.ok(lang(29999), lang(39409))
@ -759,8 +758,8 @@ class LibrarySync(Thread):
for thread in threads: for thread in threads:
# Threads might already have quit by themselves (e.g. Kodi exit) # Threads might already have quit by themselves (e.g. Kodi exit)
try: try:
thread.stopThread() thread.stop_thread()
except: except AttributeError:
pass pass
log.debug("Stop sent to all threads") log.debug("Stop sent to all threads")
# Wait till threads are indeed dead # Wait till threads are indeed dead
@ -805,7 +804,7 @@ class LibrarySync(Thread):
# PROCESS MOVIES ##### # PROCESS MOVIES #####
self.updatelist = [] self.updatelist = []
for view in views: for view in views:
if self.threadStopped(): if self.thread_stopped():
return False return False
# Get items per view # Get items per view
viewId = view['id'] viewId = view['id']
@ -826,7 +825,7 @@ class LibrarySync(Thread):
log.info("Processed view") log.info("Processed view")
# Update viewstate for EVERY item # Update viewstate for EVERY item
for view in views: for view in views:
if self.threadStopped(): if self.thread_stopped():
return False return False
self.PlexUpdateWatched(view['id'], itemType) self.PlexUpdateWatched(view['id'], itemType)
@ -898,7 +897,7 @@ class LibrarySync(Thread):
# PROCESS TV Shows ##### # PROCESS TV Shows #####
self.updatelist = [] self.updatelist = []
for view in views: for view in views:
if self.threadStopped(): if self.thread_stopped():
return False return False
# Get items per view # Get items per view
viewId = view['id'] viewId = view['id']
@ -927,7 +926,7 @@ class LibrarySync(Thread):
# PROCESS TV Seasons ##### # PROCESS TV Seasons #####
# Cycle through tv shows # Cycle through tv shows
for tvShowId in allPlexTvShowsId: for tvShowId in allPlexTvShowsId:
if self.threadStopped(): if self.thread_stopped():
return False return False
# Grab all seasons to tvshow from PMS # Grab all seasons to tvshow from PMS
seasons = GetAllPlexChildren(tvShowId) seasons = GetAllPlexChildren(tvShowId)
@ -952,7 +951,7 @@ class LibrarySync(Thread):
# PROCESS TV Episodes ##### # PROCESS TV Episodes #####
# Cycle through tv shows # Cycle through tv shows
for view in views: for view in views:
if self.threadStopped(): if self.thread_stopped():
return False return False
# Grab all episodes to tvshow from PMS # Grab all episodes to tvshow from PMS
episodes = GetAllPlexLeaves(view['id']) episodes = GetAllPlexLeaves(view['id'])
@ -987,7 +986,7 @@ class LibrarySync(Thread):
# Update viewstate: # Update viewstate:
for view in views: for view in views:
if self.threadStopped(): if self.thread_stopped():
return False return False
self.PlexUpdateWatched(view['id'], itemType) self.PlexUpdateWatched(view['id'], itemType)
@ -1024,7 +1023,7 @@ class LibrarySync(Thread):
for kind in (v.PLEX_TYPE_ARTIST, for kind in (v.PLEX_TYPE_ARTIST,
v.PLEX_TYPE_ALBUM, v.PLEX_TYPE_ALBUM,
v.PLEX_TYPE_SONG): v.PLEX_TYPE_SONG):
if self.threadStopped(): if self.thread_stopped():
return False return False
log.debug("Start processing music %s" % kind) log.debug("Start processing music %s" % kind)
self.allKodiElementsId = {} self.allKodiElementsId = {}
@ -1041,7 +1040,7 @@ class LibrarySync(Thread):
# Update viewstate for EVERY item # Update viewstate for EVERY item
for view in views: for view in views:
if self.threadStopped(): if self.thread_stopped():
return False return False
self.PlexUpdateWatched(view['id'], itemType) self.PlexUpdateWatched(view['id'], itemType)
@ -1066,7 +1065,7 @@ class LibrarySync(Thread):
except ValueError: except ValueError:
pass pass
for view in views: for view in views:
if self.threadStopped(): if self.thread_stopped():
return False return False
# Get items per view # Get items per view
itemsXML = GetPlexSectionResults(view['id'], args=urlArgs) itemsXML = GetPlexSectionResults(view['id'], args=urlArgs)
@ -1172,7 +1171,7 @@ class LibrarySync(Thread):
now = getUnixTimestamp() now = getUnixTimestamp()
deleteListe = [] deleteListe = []
for i, item in enumerate(self.itemsToProcess): for i, item in enumerate(self.itemsToProcess):
if self.threadStopped(): if self.thread_stopped():
# Chances are that Kodi gets shut down # Chances are that Kodi gets shut down
break break
if item['state'] == 9: if item['state'] == 9:
@ -1277,8 +1276,8 @@ class LibrarySync(Thread):
# movie or episode) # movie or episode)
continue continue
typus = int(item.get('type', 0)) typus = int(item.get('type', 0))
state = int(item.get('state', 0)) status = int(item.get('state', 0))
if state == 9 or (typus in (1, 4, 10) and state == 5): if status == 9 or (typus in (1, 4, 10) and status == 5):
# Only process deleted items OR movies, episodes, tracks/songs # Only process deleted items OR movies, episodes, tracks/songs
plex_id = str(item.get('itemID', '0')) plex_id = str(item.get('itemID', '0'))
if plex_id == '0': if plex_id == '0':
@ -1286,7 +1285,7 @@ class LibrarySync(Thread):
continue continue
try: try:
if (now - self.just_processed[plex_id] < 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) log.debug('We just processed %s: ignoring' % plex_id)
continue continue
except KeyError: except KeyError:
@ -1299,7 +1298,7 @@ class LibrarySync(Thread):
else: else:
# Haven't added this element to the queue yet # Haven't added this element to the queue yet
self.itemsToProcess.append({ self.itemsToProcess.append({
'state': state, 'state': status,
'type': typus, 'type': typus,
'ratingKey': plex_id, 'ratingKey': plex_id,
'timestamp': getUnixTimestamp(), 'timestamp': getUnixTimestamp(),
@ -1315,8 +1314,8 @@ class LibrarySync(Thread):
with plexdb.Get_Plex_DB() as plex_db: with plexdb.Get_Plex_DB() as plex_db:
for item in data: for item in data:
# Drop buffering messages immediately # Drop buffering messages immediately
state = item.get('state') status = item.get('state')
if state == 'buffering': if status == 'buffering':
continue continue
ratingKey = item.get('ratingKey') ratingKey = item.get('ratingKey')
kodiInfo = plex_db.getItem_byId(ratingKey) kodiInfo = plex_db.getItem_byId(ratingKey)
@ -1335,8 +1334,7 @@ class LibrarySync(Thread):
} }
else: else:
# PMS is ours - get all current sessions # PMS is ours - get all current sessions
self.sessionKeys = GetPMSStatus( self.sessionKeys = GetPMSStatus(state.PLEX_TOKEN)
window('plex_token'))
log.debug('Updated current sessions. They are: %s' log.debug('Updated current sessions. They are: %s'
% self.sessionKeys) % self.sessionKeys)
if sessionKey not in 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 # Identify the user - same one as signed on with PKC? Skip
# update if neither session's username nor userid match # update if neither session's username nor userid match
# (Owner sometime's returns id '1', not always) # (Owner sometime's returns id '1', not always)
if (window('plex_token') == '' and if (not state.PLEX_TOKEN and currSess['userId'] == '1'):
currSess['userId'] == '1'):
# PKC not signed in to plex.tv. Plus owner of PMS is # PKC not signed in to plex.tv. Plus owner of PMS is
# playing (the '1'). # playing (the '1').
# Hence must be us (since several users require plex.tv # Hence must be us (since several users require plex.tv
@ -1394,7 +1391,7 @@ class LibrarySync(Thread):
'file_id': kodiInfo[1], 'file_id': kodiInfo[1],
'kodi_type': kodiInfo[4], 'kodi_type': kodiInfo[4],
'viewOffset': resume, 'viewOffset': resume,
'state': state, 'state': status,
'duration': currSess['duration'], 'duration': currSess['duration'],
'viewCount': currSess['viewCount'], 'viewCount': currSess['viewCount'],
'lastViewedAt': DateToKodi(getUnixTimestamp()) 'lastViewedAt': DateToKodi(getUnixTimestamp())
@ -1433,6 +1430,7 @@ class LibrarySync(Thread):
try: try:
self.run_internal() self.run_internal()
except Exception as e: except Exception as e:
state.DB_SCAN = False
window('plex_dbScan', clear=True) window('plex_dbScan', clear=True)
log.error('LibrarySync thread crashed. Error message: %s' % e) log.error('LibrarySync thread crashed. Error message: %s' % e)
import traceback import traceback
@ -1443,8 +1441,8 @@ class LibrarySync(Thread):
def run_internal(self): def run_internal(self):
# Re-assign handles to have faster calls # Re-assign handles to have faster calls
threadStopped = self.threadStopped thread_stopped = self.thread_stopped
threadSuspended = self.threadSuspended thread_suspended = self.thread_suspended
installSyncDone = self.installSyncDone installSyncDone = self.installSyncDone
enableBackgroundSync = self.enableBackgroundSync enableBackgroundSync = self.enableBackgroundSync
fullSync = self.fullSync fullSync = self.fullSync
@ -1476,12 +1474,12 @@ class LibrarySync(Thread):
if settings('FanartTV') == 'true': if settings('FanartTV') == 'true':
self.fanartthread.start() self.fanartthread.start()
while not threadStopped(): while not thread_stopped():
# In the event the server goes offline # In the event the server goes offline
while threadSuspended(): while thread_suspended():
# Set in service.py # Set in service.py
if threadStopped(): if thread_stopped():
# Abort was requested while waiting. We should exit # Abort was requested while waiting. We should exit
log.info("###===--- LibrarySync Stopped ---===###") log.info("###===--- LibrarySync Stopped ---===###")
return return
@ -1523,6 +1521,7 @@ class LibrarySync(Thread):
self.dialog.ok(heading=lang(29999), line1=lang(39403)) self.dialog.ok(heading=lang(29999), line1=lang(39403))
break break
# Run start up sync # Run start up sync
state.DB_SCAN = True
window('plex_dbScan', value="true") window('plex_dbScan', value="true")
log.info("Db version: %s" % settings('dbCreatedWithVersion')) log.info("Db version: %s" % settings('dbCreatedWithVersion'))
lastTimeSync = getUnixTimestamp() lastTimeSync = getUnixTimestamp()
@ -1547,6 +1546,7 @@ class LibrarySync(Thread):
log.info("Initial start-up full sync starting") log.info("Initial start-up full sync starting")
librarySync = fullSync() librarySync = fullSync()
window('plex_dbScan', clear=True) window('plex_dbScan', clear=True)
state.DB_SCAN = False
if librarySync: if librarySync:
log.info("Initial start-up full sync successful") log.info("Initial start-up full sync successful")
startupComplete = True startupComplete = True
@ -1565,23 +1565,26 @@ class LibrarySync(Thread):
break break
# Currently no db scan, so we can start a new scan # 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 # Full scan was requested from somewhere else, e.g. userclient
if window('plex_runLibScan') in ("full", "repair"): if window('plex_runLibScan') in ("full", "repair"):
log.info('Full library scan requested, starting') log.info('Full library scan requested, starting')
window('plex_dbScan', value="true") window('plex_dbScan', value="true")
state.DB_SCAN = True
if window('plex_runLibScan') == "full": if window('plex_runLibScan') == "full":
fullSync() fullSync()
elif window('plex_runLibScan') == "repair": elif window('plex_runLibScan') == "repair":
fullSync(repair=True) fullSync(repair=True)
window('plex_runLibScan', clear=True) window('plex_runLibScan', clear=True)
window('plex_dbScan', clear=True) window('plex_dbScan', clear=True)
state.DB_SCAN = False
# Full library sync finished # Full library sync finished
self.showKodiNote(lang(39407), forced=False) self.showKodiNote(lang(39407), forced=False)
# Reset views was requested from somewhere else # Reset views was requested from somewhere else
elif window('plex_runLibScan') == "views": elif window('plex_runLibScan') == "views":
log.info('Refresh playlist and nodes requested, starting') log.info('Refresh playlist and nodes requested, starting')
window('plex_dbScan', value="true") window('plex_dbScan', value="true")
state.DB_SCAN = True
window('plex_runLibScan', clear=True) window('plex_runLibScan', clear=True)
# First remove playlists # First remove playlists
@ -1602,6 +1605,7 @@ class LibrarySync(Thread):
forced=True, forced=True,
icon="error") icon="error")
window('plex_dbScan', clear=True) window('plex_dbScan', clear=True)
state.DB_SCAN = False
elif window('plex_runLibScan') == 'fanart': elif window('plex_runLibScan') == 'fanart':
window('plex_runLibScan', clear=True) window('plex_runLibScan', clear=True)
# Only look for missing fanart (No) # Only look for missing fanart (No)
@ -1613,31 +1617,37 @@ class LibrarySync(Thread):
yeslabel=lang(39225))) yeslabel=lang(39225)))
elif window('plex_runLibScan') == 'del_textures': elif window('plex_runLibScan') == 'del_textures':
window('plex_runLibScan', clear=True) window('plex_runLibScan', clear=True)
state.DB_SCAN = True
window('plex_dbScan', value="true") window('plex_dbScan', value="true")
import artwork import artwork
artwork.Artwork().fullTextureCacheSync() artwork.Artwork().fullTextureCacheSync()
window('plex_dbScan', clear=True) window('plex_dbScan', clear=True)
state.DB_SCAN = False
else: else:
now = getUnixTimestamp() now = getUnixTimestamp()
if (now - lastSync > fullSyncInterval and if (now - lastSync > fullSyncInterval and
not xbmcplayer.isPlaying()): not xbmcplayer.isPlaying()):
lastSync = now lastSync = now
log.info('Doing scheduled full library scan') log.info('Doing scheduled full library scan')
state.DB_SCAN = True
window('plex_dbScan', value="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') log.error('Could not finish scheduled full sync')
self.showKodiNote(lang(39410), self.showKodiNote(lang(39410),
forced=True, forced=True,
icon='error') icon='error')
window('plex_dbScan', clear=True) window('plex_dbScan', clear=True)
state.DB_SCAN = False
# Full library sync finished # Full library sync finished
self.showKodiNote(lang(39407), forced=False) self.showKodiNote(lang(39407), forced=False)
elif now - lastTimeSync > oneDay: elif now - lastTimeSync > oneDay:
lastTimeSync = now lastTimeSync = now
log.info('Starting daily time sync') log.info('Starting daily time sync')
state.DB_SCAN = True
window('plex_dbScan', value="true") window('plex_dbScan', value="true")
self.syncPMStime() self.syncPMStime()
window('plex_dbScan', clear=True) window('plex_dbScan', clear=True)
state.DB_SCAN = False
elif enableBackgroundSync: elif enableBackgroundSync:
# Check back whether we should process something # Check back whether we should process something
# Only do this once every while (otherwise, potentially # Only do this once every while (otherwise, potentially

View file

@ -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 ##===----")

View file

@ -152,12 +152,12 @@ class Playback_Starter(Thread):
pickle_me(result) pickle_me(result)
def run(self): def run(self):
queue = self.mgr.monitor_kodi_play.playback_queue queue = self.mgr.command_pipeline.playback_queue
log.info("----===## Starting Playback_Starter ##===----") log.info("----===## Starting Playback_Starter ##===----")
while True: while True:
item = queue.get() item = queue.get()
if item is None: if item is None:
# Need to shutdown - initiated by monitor_kodi_play # Need to shutdown - initiated by command_pipeline
break break
else: else:
self.triage(item) self.triage(item)

View file

@ -5,12 +5,13 @@ from threading import RLock, Thread
from xbmc import sleep, Player, PlayList, PLAYLIST_MUSIC, PLAYLIST_VIDEO 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 import playlist_func as PL
from PlexFunctions import ConvertPlexToKodiTime, GetAllPlexChildren from PlexFunctions import ConvertPlexToKodiTime, GetAllPlexChildren
from PlexAPI import API from PlexAPI import API
from playbackutils import PlaybackUtils from playbackutils import PlaybackUtils
import variables as v import variables as v
import state
############################################################################### ###############################################################################
log = logging.getLogger("PLEX."+__name__) log = logging.getLogger("PLEX."+__name__)
@ -21,8 +22,7 @@ PLUGIN = 'plugin://%s' % v.ADDON_ID
############################################################################### ###############################################################################
@ThreadMethodsAdditionalSuspend('plex_serverStatus') @ThreadMethods(add_suspends=[state.PMS_STATUS])
@ThreadMethods
class Playqueue(Thread): class Playqueue(Thread):
""" """
Monitors Kodi's playqueues for changes on the Kodi side Monitors Kodi's playqueues for changes on the Kodi side
@ -153,7 +153,7 @@ class Playqueue(Thread):
# Ignore new media added by other addons # Ignore new media added by other addons
continue continue
for j, old_item in enumerate(old): 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 # Chances are that we got an empty Kodi playlist due to
# Kodi exit # Kodi exit
return return
@ -193,8 +193,8 @@ class Playqueue(Thread):
log.debug('Done comparing playqueues') log.debug('Done comparing playqueues')
def run(self): def run(self):
threadStopped = self.threadStopped thread_stopped = self.thread_stopped
threadSuspended = self.threadSuspended thread_suspended = self.thread_suspended
log.info("----===## Starting PlayQueue client ##===----") log.info("----===## Starting PlayQueue client ##===----")
# Initialize the playqueues, if Kodi already got items in them # Initialize the playqueues, if Kodi already got items in them
for playqueue in self.playqueues: for playqueue in self.playqueues:
@ -203,9 +203,9 @@ class Playqueue(Thread):
PL.init_Plex_playlist(playqueue, kodi_item=item) PL.init_Plex_playlist(playqueue, kodi_item=item)
else: else:
PL.add_item_to_PMS_playlist(playqueue, i, kodi_item=item) PL.add_item_to_PMS_playlist(playqueue, i, kodi_item=item)
while not threadStopped(): while not thread_stopped():
while threadSuspended(): while thread_suspended():
if threadStopped(): if thread_stopped():
break break
sleep(1000) sleep(1000)
with lock: with lock:

22
resources/lib/state.py Normal file
View file

@ -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

View file

@ -10,12 +10,12 @@ import xbmcaddon
from xbmcvfs import exists 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 downloadutils
import PlexAPI import PlexAPI
from PlexFunctions import GetMachineIdentifier from PlexFunctions import GetMachineIdentifier
import state
############################################################################### ###############################################################################
@ -24,8 +24,7 @@ log = logging.getLogger("PLEX."+__name__)
############################################################################### ###############################################################################
@ThreadMethodsAdditionalSuspend('suspend_Userclient') @ThreadMethods(add_suspends=[state.SUSPEND_USER_CLIENT])
@ThreadMethods
class UserClient(threading.Thread): class UserClient(threading.Thread):
# Borg - multiple instances, shared state # Borg - multiple instances, shared state
@ -118,25 +117,6 @@ class UserClient(threading.Thread):
def hasAccess(self): def hasAccess(self):
# Plex: always return True for now # Plex: always return True for now
return True 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): def loadCurrUser(self, username, userId, usertoken, authenticated=False):
log.debug('Loading current user') log.debug('Loading current user')
@ -171,7 +151,10 @@ class UserClient(threading.Thread):
# This is the token for plex.tv for the current user # This is the token for plex.tv for the current user
# Is only '' if user is not signed in to plex.tv # Is only '' if user is not signed in to plex.tv
window('plex_token', value=settings('plexToken')) window('plex_token', value=settings('plexToken'))
state.PLEX_TOKEN = settings('plexToken') or None
window('plex_restricteduser', value=settings('plex_restricteduser')) 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('pms_server', value=self.currServer)
window('plex_machineIdentifier', value=self.machineIdentifier) window('plex_machineIdentifier', value=self.machineIdentifier)
window('plex_servername', value=self.servername) window('plex_servername', value=self.servername)
@ -202,7 +185,7 @@ class UserClient(threading.Thread):
# Give attempts at entering password / selecting user # Give attempts at entering password / selecting user
if self.retry >= 2: if self.retry >= 2:
log.error("Too many retries to login.") log.error("Too many retries to login.")
window('plex_serverStatus', value="Stop") state.PMS_STATUS = 'Stop'
dialog.ok(lang(33001), dialog.ok(lang(33001),
lang(39023)) lang(39023))
xbmc.executebuiltin( xbmc.executebuiltin(
@ -284,6 +267,7 @@ class UserClient(threading.Thread):
window('plex_authenticated', clear=True) window('plex_authenticated', clear=True)
window('pms_token', clear=True) window('pms_token', clear=True)
state.PLEX_TOKEN = None
window('plex_token', clear=True) window('plex_token', clear=True)
window('pms_server', clear=True) window('pms_server', clear=True)
window('plex_machineIdentifier', clear=True) window('plex_machineIdentifier', clear=True)
@ -291,6 +275,7 @@ class UserClient(threading.Thread):
window('currUserId', clear=True) window('currUserId', clear=True)
window('plex_username', clear=True) window('plex_username', clear=True)
window('plex_restricteduser', clear=True) window('plex_restricteduser', clear=True)
state.RESTRICTED_USER = False
settings('username', value='') settings('username', value='')
settings('userid', value='') settings('userid', value='')
@ -310,32 +295,32 @@ class UserClient(threading.Thread):
def run(self): def run(self):
log.info("----===## Starting UserClient ##===----") log.info("----===## Starting UserClient ##===----")
while not self.threadStopped(): thread_stopped = self.thread_stopped
while self.threadSuspended(): thread_suspended = self.thread_suspended
if self.threadStopped(): while not thread_stopped():
while thread_suspended():
if thread_stopped():
break break
xbmc.sleep(1000) xbmc.sleep(1000)
status = window('plex_serverStatus') if state.PMS_STATUS == "Stop":
if status == "Stop":
xbmc.sleep(500) xbmc.sleep(500)
continue continue
# Verify the connection status to server # Verify the connection status to server
elif status == "restricted": elif state.PMS_STATUS == "restricted":
# Parental control is restricting access # Parental control is restricting access
self.HasAccess = False self.HasAccess = False
elif status == "401": elif state.PMS_STATUS == "401":
# Unauthorized access, revoke token # Unauthorized access, revoke token
window('plex_serverStatus', value="Auth") state.PMS_STATUS = 'Auth'
self.resetClient() self.resetClient()
xbmc.sleep(2000) xbmc.sleep(2000)
if self.auth and (self.currUser is None): if self.auth and (self.currUser is None):
# Try to authenticate user # 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 # Set auth flag because we no longer need
# to authenticate the user # to authenticate the user
self.auth = False self.auth = False
@ -345,8 +330,9 @@ class UserClient(threading.Thread):
log.info("Current user: %s" % self.currUser) log.info("Current user: %s" % self.currUser)
log.info("Current userId: %s" % self.currUserId) log.info("Current userId: %s" % self.currUserId)
self.retry = 0 self.retry = 0
window('suspend_LibraryThread', clear=True) state.SUSPEND_LIBRARY_THREAD = False
window('plex_serverStatus', clear=True) window('plex_serverStatus', clear=True)
state.PMS_STATUS = False
if not self.auth and (self.currUser is None): if not self.auth and (self.currUser is None):
# Loop if no server found # Loop if no server found
@ -354,7 +340,7 @@ class UserClient(threading.Thread):
# The status Stop is for when user cancelled password dialog. # The status Stop is for when user cancelled password dialog.
# Or retried too many times # 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 # Only if there's information found to login
log.debug("Server found: %s" % server) log.debug("Server found: %s" % server)
self.auth = True self.auth = True
@ -362,5 +348,4 @@ class UserClient(threading.Thread):
# Minimize CPU load # Minimize CPU load
xbmc.sleep(100) xbmc.sleep(100)
self.doUtils.stopSession()
log.info("##===---- UserClient Stopped ----===##") log.info("##===---- UserClient Stopped ----===##")

View file

@ -11,7 +11,7 @@ from StringIO import StringIO
from time import localtime, strftime, strptime from time import localtime, strftime, strptime
from unicodedata import normalize from unicodedata import normalize
import xml.etree.ElementTree as etree import xml.etree.ElementTree as etree
from functools import wraps from functools import wraps, partial
from calendar import timegm from calendar import timegm
from os.path import join from os.path import join
from os import remove, walk, makedirs 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, \ from variables import DB_VIDEO_PATH, DB_MUSIC_PATH, DB_TEXTURE_PATH, \
DB_PLEX_PATH, KODI_PROFILE, KODIVERSION 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) 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): def settings(setting, value=None):
""" """
Get or add addon setting. Returns unicode Get or add addon setting. Returns unicode
@ -319,7 +333,7 @@ def reset():
return return
# first stop any db sync # first stop any db sync
window('plex_shouldStop', value="true") plex_command('STOP_SYNC', 'True')
count = 10 count = 10
while window('plex_dbScan') == "true": while window('plex_dbScan') == "true":
log.debug("Sync is running, will retry: %s..." % count) log.debug("Sync is running, will retry: %s..." % count)
@ -906,78 +920,61 @@ def LogTime(func):
return wrapper return wrapper
def ThreadMethodsAdditionalStop(windowAttribute): def ThreadMethods(cls=None, add_stops=None, add_suspends=None):
"""
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: Decorator to add the following methods to a threading class:
suspendThread(): pauses the thread suspend_thread(): pauses the thread
resumeThread(): resumes the thread resume_thread(): resumes the thread
stopThread(): stopps/kills the thread stop_thread(): stopps/kills the thread
threadSuspended(): returns True if thread is suspend_thread thread_suspended(): returns True if thread is suspend_thread
threadStopped(): returns True if thread is stopped (or should stop ;-)) thread_stopped(): returns True if thread is stopped (or should stop ;-))
ALSO stops if Kodi is exited ALSO stops if PKC should exit
Also adds the following class attributes: Also adds the following class attributes:
_threadStopped _thread_stopped
_threadSuspended _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 # Attach new attributes to class
cls._threadStopped = False cls._thread_stopped = False
cls._threadSuspended = False cls._thread_suspended = False
# Define new class methods and attach them to class # Define new class methods and attach them to class
def stopThread(self): def stop_thread(self):
self._threadStopped = True self._thread_stopped = True
cls.stopThread = stopThread cls.stop_thread = stop_thread
def suspendThread(self): def suspend_thread(self):
self._threadSuspended = True self._thread_suspended = True
cls.suspendThread = suspendThread cls.suspend_thread = suspend_thread
def resumeThread(self): def resume_thread(self):
self._threadSuspended = False self._thread_suspended = False
cls.resumeThread = resumeThread cls.resume_thread = resume_thread
def threadSuspended(self): def thread_suspended(self):
return self._threadSuspended return self._thread_suspended or any(add_suspends)
cls.threadSuspended = threadSuspended cls.thread_suspended = thread_suspended
def threadStopped(self): def thread_stopped(self):
return self._threadStopped or (window('plex_terminateNow') == 'true') return self._thread_stopped or state.STOP_PKC or any(add_stops)
cls.threadStopped = threadStopped cls.thread_stopped = thread_stopped
# Return class to render this a decorator # Return class to render this a decorator
return cls return cls

View file

@ -11,9 +11,9 @@ from ssl import CERT_NONE
from xbmc import sleep from xbmc import sleep
from utils import window, settings, ThreadMethodsAdditionalSuspend, \ from utils import window, settings, ThreadMethods
ThreadMethods
from companion import process_command from companion import process_command
import state
############################################################################### ###############################################################################
@ -22,8 +22,7 @@ log = logging.getLogger("PLEX."+__name__)
############################################################################### ###############################################################################
@ThreadMethodsAdditionalSuspend('suspend_LibraryThread') @ThreadMethods(add_suspends=[state.SUSPEND_LIBRARY_THREAD])
@ThreadMethods
class WebSocket(Thread): class WebSocket(Thread):
opcode_data = (websocket.ABNF.OPCODE_TEXT, websocket.ABNF.OPCODE_BINARY) opcode_data = (websocket.ABNF.OPCODE_TEXT, websocket.ABNF.OPCODE_BINARY)
@ -62,11 +61,11 @@ class WebSocket(Thread):
counter = 0 counter = 0
handshake_counter = 0 handshake_counter = 0
threadStopped = self.threadStopped thread_stopped = self.thread_stopped
threadSuspended = self.threadSuspended thread_suspended = self.thread_suspended
while not threadStopped(): while not thread_stopped():
# In the event the server goes offline # In the event the server goes offline
while threadSuspended(): while thread_suspended():
# Set in service.py # Set in service.py
if self.ws is not None: if self.ws is not None:
try: try:
@ -74,7 +73,7 @@ class WebSocket(Thread):
except: except:
pass pass
self.ws = None self.ws = None
if threadStopped(): if thread_stopped():
# Abort was requested while waiting. We should exit # Abort was requested while waiting. We should exit
log.info("##===---- %s Stopped ----===##" log.info("##===---- %s Stopped ----===##"
% self.__class__.__name__) % self.__class__.__name__)
@ -160,16 +159,15 @@ class PMS_Websocket(WebSocket):
def getUri(self): def getUri(self):
server = window('pms_server') 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 # Get the appropriate prefix for the websocket
if server.startswith('https'): if server.startswith('https'):
server = "wss%s" % server[5:] server = "wss%s" % server[5:]
else: else:
server = "ws%s" % server[4:] server = "ws%s" % server[4:]
uri = "%s/:/websockets/notifications" % server uri = "%s/:/websockets/notifications" % server
if token: # Need to use plex.tv token, if any. NOT user token
uri += '?X-Plex-Token=%s' % token if state.PLEX_TOKEN:
uri += '?X-Plex-Token=%s' % state.PLEX_TOKEN
sslopt = {} sslopt = {}
if settings('sslverify') == "false": if settings('sslverify') == "false":
sslopt["cert_reqs"] = CERT_NONE sslopt["cert_reqs"] = CERT_NONE
@ -218,9 +216,7 @@ class Alexa_Websocket(WebSocket):
def getUri(self): def getUri(self):
self.plex_client_Id = window('plex_client_Id') self.plex_client_Id = window('plex_client_Id')
uri = ('wss://pubsub.plex.tv/sub/websockets/%s/%s?X-Plex-Token=%s' uri = ('wss://pubsub.plex.tv/sub/websockets/%s/%s?X-Plex-Token=%s'
% (window('currUserId'), % (window('currUserId'), self.plex_client_Id, state.PLEX_TOKEN))
self.plex_client_Id,
window('plex_token')))
sslopt = {} sslopt = {}
log.debug("Uri: %s, sslopt: %s" % (uri, sslopt)) log.debug("Uri: %s, sslopt: %s" % (uri, sslopt))
return uri, sslopt return uri, sslopt
@ -252,11 +248,10 @@ class Alexa_Websocket(WebSocket):
def IOError_response(self): def IOError_response(self):
pass pass
def threadSuspended(self): def thread_suspended(self):
""" """
Overwrite to ignore library sync stuff and allow to check for Overwrite to ignore library sync stuff and allow to check for
plex_restricteduser RESTRICTED_USER and PLEX_TOKEN
""" """
return (self._threadSuspended or return self._thread_suspended or state.RESTRICTED_USER \
window('plex_restricteduser') == 'true' or or not state.PLEX_TOKEN
not window('plex_token'))

View file

@ -42,10 +42,11 @@ from playqueue import Playqueue
import PlexAPI import PlexAPI
from PlexCompanion import PlexCompanion 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 playback_starter import Playback_Starter
from artwork import Image_Cache_Thread from artwork import Image_Cache_Thread
import variables as v import variables as v
import state
############################################################################### ###############################################################################
@ -105,7 +106,6 @@ class Service():
# Reset window props for profile switch # Reset window props for profile switch
properties = [ properties = [
"plex_online", "plex_serverStatus", "plex_onWake", "plex_online", "plex_serverStatus", "plex_onWake",
"plex_dbCheck", "plex_kodiScan", "plex_dbCheck", "plex_kodiScan",
"plex_shouldStop", "currUserId", "plex_dbScan", "plex_shouldStop", "currUserId", "plex_dbScan",
@ -113,10 +113,9 @@ class Service():
"plex_runLibScan", "plex_username", "pms_token", "plex_token", "plex_runLibScan", "plex_username", "pms_token", "plex_token",
"pms_server", "plex_machineIdentifier", "plex_servername", "pms_server", "plex_machineIdentifier", "plex_servername",
"plex_authenticated", "PlexUserImage", "useDirectPaths", "plex_authenticated", "PlexUserImage", "useDirectPaths",
"suspend_LibraryThread", "plex_terminateNow",
"kodiplextimeoffset", "countError", "countUnauthorized", "kodiplextimeoffset", "countError", "countUnauthorized",
"plex_restricteduser", "plex_allows_mediaDeletion", "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: for prop in properties:
window(prop, clear=True) window(prop, clear=True)
@ -141,8 +140,8 @@ class Service():
kodiProfile = v.KODI_PROFILE kodiProfile = v.KODI_PROFILE
# Detect playback start early on # Detect playback start early on
self.monitor_kodi_play = Monitor_Kodi_Play(self) self.command_pipeline = Monitor_Window(self)
self.monitor_kodi_play.start() self.command_pipeline.start()
# Server auto-detect # Server auto-detect
initialsetup.InitialSetup().setup() initialsetup.InitialSetup().setup()
@ -261,7 +260,7 @@ class Service():
self.server_online = False self.server_online = False
window('plex_online', value="false") window('plex_online', value="false")
# Suspend threads # Suspend threads
window('suspend_LibraryThread', value='true') state.SUSPEND_LIBRARY_THREAD = True
log.error("Plex Media Server went offline") log.error("Plex Media Server went offline")
if settings('show_pms_offline') == 'true': if settings('show_pms_offline') == 'true':
dialog('notification', dialog('notification',
@ -301,7 +300,7 @@ class Service():
if window('plex_authenticated') == 'true': if window('plex_authenticated') == 'true':
# Server got offline when we were authenticated. # Server got offline when we were authenticated.
# Hence resume threads # Hence resume threads
window('suspend_LibraryThread', clear=True) state.SUSPEND_LIBRARY_THREAD = False
# Start the userclient thread # Start the userclient thread
if not self.user_running: if not self.user_running:
@ -321,27 +320,7 @@ class Service():
# Terminating PlexKodiConnect # Terminating PlexKodiConnect
# Tell all threads to terminate (e.g. several lib sync threads) # Tell all threads to terminate (e.g. several lib sync threads)
window('plex_terminateNow', value='true') state.STOP_PKC = 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: try:
downloadutils.DownloadUtils().stopSession() downloadutils.DownloadUtils().stopSession()
except: except:
@ -349,6 +328,7 @@ class Service():
window('plex_service_started', clear=True) window('plex_service_started', clear=True)
log.warn("======== STOP %s ========" % v.ADDON_NAME) log.warn("======== STOP %s ========" % v.ADDON_NAME)
# Safety net - Kody starts PKC twice upon first installation! # Safety net - Kody starts PKC twice upon first installation!
if window('plex_service_started') == 'true': if window('plex_service_started') == 'true':
exit = True exit = True