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
"""
# Put the request into the 'queue'
while window('plex_play_new_item'):
while window('plex_command'):
sleep(50)
window('plex_play_new_item',
value='%s%s' % ('play', argv[2]))
window('plex_command',
value='play_%s' % argv[2])
# Wait for the result
while not pickl_window('plex_result'):
sleep(50)

View file

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

View file

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

View file

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

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
import clientinfo as client
import state
###############################################################################
# Disable annoying requests warnings
@ -274,10 +276,11 @@ class DownloadUtils():
self.unauthorizedAttempts):
log.warn('We seem to be truly unauthorized for PMS'
' %s ' % url)
if window('plex_serverStatus') not in ('401', 'Auth'):
if state.PMS_STATUS not in ('401', 'Auth'):
# Tell userclient token has been revoked.
log.debug('Setting PMS server status to '
'unauthorized')
state.PMS_STATUS = '401'
window('plex_serverStatus', value="401")
dialog('notification',
lang(29999),

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

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)
def run(self):
queue = self.mgr.monitor_kodi_play.playback_queue
queue = self.mgr.command_pipeline.playback_queue
log.info("----===## Starting Playback_Starter ##===----")
while True:
item = queue.get()
if item is None:
# Need to shutdown - initiated by monitor_kodi_play
# Need to shutdown - initiated by command_pipeline
break
else:
self.triage(item)

View file

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

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

View file

@ -11,7 +11,7 @@ from StringIO import StringIO
from time import localtime, strftime, strptime
from unicodedata import normalize
import xml.etree.ElementTree as etree
from functools import wraps
from functools import wraps, partial
from calendar import timegm
from os.path import join
from os import remove, walk, makedirs
@ -25,6 +25,7 @@ from xbmcvfs import exists, delete
from variables import DB_VIDEO_PATH, DB_MUSIC_PATH, DB_TEXTURE_PATH, \
DB_PLEX_PATH, KODI_PROFILE, KODIVERSION
import state
###############################################################################
@ -76,6 +77,19 @@ def pickl_window(property, value=None, clear=False, windowid=10000):
return win.getProperty(property)
def plex_command(key, value):
"""
Used to funnel states between different Python instances. NOT really thread
safe - let's hope the Kodi user can't click fast enough
key: state.py variable
value: either 'True' or 'False'
"""
while window('plex_command'):
xbmc.sleep(1)
window('plex_command', value='%s-%s' % (key, value))
def settings(setting, value=None):
"""
Get or add addon setting. Returns unicode
@ -319,7 +333,7 @@ def reset():
return
# first stop any db sync
window('plex_shouldStop', value="true")
plex_command('STOP_SYNC', 'True')
count = 10
while window('plex_dbScan') == "true":
log.debug("Sync is running, will retry: %s..." % count)
@ -906,78 +920,61 @@ def LogTime(func):
return wrapper
def ThreadMethodsAdditionalStop(windowAttribute):
"""
Decorator to replace stopThread method to include the Kodi windowAttribute
Use with any sync threads. @ThreadMethods still required FIRST
"""
def wrapper(cls):
def threadStopped(self):
return (self._threadStopped or
(window('plex_terminateNow') == "true") or
window(windowAttribute) == "true")
cls.threadStopped = threadStopped
return cls
return wrapper
def ThreadMethodsAdditionalSuspend(windowAttribute):
"""
Decorator to replace threadSuspended(): thread now also suspends if a
Kodi windowAttribute is set to 'true', e.g. 'suspend_LibraryThread'
Use with any library sync threads. @ThreadMethods still required FIRST
"""
def wrapper(cls):
def threadSuspended(self):
return (self._threadSuspended or
window(windowAttribute) == 'true')
cls.threadSuspended = threadSuspended
return cls
return wrapper
def ThreadMethods(cls):
def ThreadMethods(cls=None, add_stops=None, add_suspends=None):
"""
Decorator to add the following methods to a threading class:
suspendThread(): pauses the thread
resumeThread(): resumes the thread
stopThread(): stopps/kills the thread
suspend_thread(): pauses the thread
resume_thread(): resumes the thread
stop_thread(): stopps/kills the thread
threadSuspended(): returns True if thread is suspend_thread
threadStopped(): returns True if thread is stopped (or should stop ;-))
ALSO stops if Kodi is exited
thread_suspended(): returns True if thread is suspend_thread
thread_stopped(): returns True if thread is stopped (or should stop ;-))
ALSO stops if PKC should exit
Also adds the following class attributes:
_threadStopped
_threadSuspended
_thread_stopped
_thread_suspended
invoke with either
@NewThreadMethods
class MyClass():
or
@NewThreadMethods(add_stops=[state.SUSPEND_LIBRARY_TRHEAD],
add_suspends=[state.WHATEVER, state.WHATEVER2])
class MyClass():
"""
if cls is None:
return partial(ThreadMethods,
add_stops=add_stops,
add_suspends=add_suspends)
# Make sure we have an iterable
add_stops = add_stops or []
add_suspends = add_suspends or []
# Attach new attributes to class
cls._threadStopped = False
cls._threadSuspended = False
cls._thread_stopped = False
cls._thread_suspended = False
# Define new class methods and attach them to class
def stopThread(self):
self._threadStopped = True
cls.stopThread = stopThread
def stop_thread(self):
self._thread_stopped = True
cls.stop_thread = stop_thread
def suspendThread(self):
self._threadSuspended = True
cls.suspendThread = suspendThread
def suspend_thread(self):
self._thread_suspended = True
cls.suspend_thread = suspend_thread
def resumeThread(self):
self._threadSuspended = False
cls.resumeThread = resumeThread
def resume_thread(self):
self._thread_suspended = False
cls.resume_thread = resume_thread
def threadSuspended(self):
return self._threadSuspended
cls.threadSuspended = threadSuspended
def thread_suspended(self):
return self._thread_suspended or any(add_suspends)
cls.thread_suspended = thread_suspended
def threadStopped(self):
return self._threadStopped or (window('plex_terminateNow') == 'true')
cls.threadStopped = threadStopped
def thread_stopped(self):
return self._thread_stopped or state.STOP_PKC or any(add_stops)
cls.thread_stopped = thread_stopped
# Return class to render this a decorator
return cls

View file

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

View file

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