Rewire PKC startup and authentication mechanism

This commit is contained in:
croneter 2018-11-25 17:03:19 +01:00
parent 6d450b2be9
commit 6dc436da91
18 changed files with 243 additions and 232 deletions

View file

@ -101,17 +101,12 @@ class Main():
entrypoint.switch_plex_user() entrypoint.switch_plex_user()
elif mode in ('manualsync', 'repair'): elif mode in ('manualsync', 'repair'):
if pickler.pickl_window('plex_online') != 'true': if mode == 'repair':
# Server is not online, do not run the sync log.info('Requesting repair lib sync')
utils.messageDialog(utils.lang(29999), utils.lang(39205)) utils.plex_command('repair-scan')
log.error('Not connected to a PMS.') elif mode == 'manualsync':
else: log.info('Requesting full library scan')
if mode == 'repair': utils.plex_command('full-scan')
log.info('Requesting repair lib sync')
utils.plex_command('repair-scan')
elif mode == 'manualsync':
log.info('Requesting full library scan')
utils.plex_command('full-scan')
elif mode == 'texturecache': elif mode == 'texturecache':
log.info('Requesting texture caching of all textures') log.info('Requesting texture caching of all textures')

View file

@ -619,7 +619,7 @@ msgid "Invalid username or password"
msgstr "" msgstr ""
msgctxt "#33010" msgctxt "#33010"
msgid "Your Plex token is no longer valid. Logging-out of plex.tv" msgid "User is unauthorized for server {0}"
msgstr "" msgstr ""
msgctxt "#33011" msgctxt "#33011"

View file

@ -30,6 +30,9 @@ class Account(object):
def load(self): def load(self):
LOG.debug('Loading account settings') LOG.debug('Loading account settings')
# User name we used to sign in to plex.tv
self.plex_login = utils.settings('plexLogin') or None
self.plex_login_id = utils.settings('plexid') or None
# plex.tv username # plex.tv username
self.plex_username = utils.settings('username') or None self.plex_username = utils.settings('username') or None
# Plex ID of that user (e.g. for plex.tv) as a STRING # Plex ID of that user (e.g. for plex.tv) as a STRING
@ -62,13 +65,17 @@ class Account(object):
LOG.debug('User is restricted Home user: %s', self.restricted_user) LOG.debug('User is restricted Home user: %s', self.restricted_user)
def log_out(self): def log_out(self):
LOG.debug('Logging-out user') LOG.debug('Logging-out user %s', self.plex_username)
self.plex_username = None self.plex_username = None
self.plex_user_id = None self.plex_user_id = None
self.pms_token = None self.pms_token = None
self.avatar = None self.avatar = None
self.restricted_user = None self.restricted_user = None
self.authenticated = False self.authenticated = False
try:
self._session.close()
except AttributeError:
pass
self._session = None self._session = None
utils.settings('username', value='') utils.settings('username', value='')
@ -91,6 +98,12 @@ class Account(object):
self.avatar = None self.avatar = None
self.restricted_user = None self.restricted_user = None
self.authenticated = False self.authenticated = False
self.plex_login = None
self.plex_login_id = None
try:
self._session.close()
except AttributeError:
pass
self._session = None self._session = None
utils.settings('username', value='') utils.settings('username', value='')
@ -99,6 +112,8 @@ class Account(object):
utils.settings('plexToken', value='') utils.settings('plexToken', value='')
utils.settings('accessToken', value='') utils.settings('accessToken', value='')
utils.settings('plexAvatar', value='') utils.settings('plexAvatar', value='')
utils.settings('plexLogin', value='')
utils.settings('plexid', value='')
utils.window('plex_restricteduser', clear=True) utils.window('plex_restricteduser', clear=True)
utils.window('plex_token', clear=True) utils.window('plex_token', clear=True)

View file

@ -17,7 +17,10 @@ class App(object):
return return
# Quit PKC? # Quit PKC?
self.stop_pkc = False self.stop_pkc = False
# Shall we completely suspend PKC and our threads?
self.suspend = False
# Shall we only suspend threads?
self._suspend_threads = False
# Need to lock all methods and functions messing with Plex Companion subscribers # Need to lock all methods and functions messing with Plex Companion subscribers
self.lock_subscriber = RLock() self.lock_subscriber = RLock()
# Need to lock everything messing with Kodi/PKC playqueues # Need to lock everything messing with Kodi/PKC playqueues
@ -43,3 +46,11 @@ class App(object):
# Hack to force Kodi widget for "in progress" to show up if it was empty # Hack to force Kodi widget for "in progress" to show up if it was empty
# before # before
self.force_reload_skin = utils.settings('forceReloadSkinOnPlaybackStop') == 'true' self.force_reload_skin = utils.settings('forceReloadSkinOnPlaybackStop') == 'true'
@property
def suspend_threads(self):
return self._suspend_threads or self.suspend
@suspend_threads.setter
def suspend_threads(self, value):
self._suspend_threads = value

View file

@ -16,12 +16,6 @@ class Connection(object):
return return
# TODO: Delete # TODO: Delete
self.pms_server = None self.pms_server = None
# Plex Media Server Status - along with window('plex_serverStatus')
# Values:
# 'Stop': set if e.g.
# '401': Token has been revoked - PKC yet to delete tokens
# 'Auth':
self.pms_status = False
# Token passed along, e.g. if playback initiated by Plex Companion. Might be # Token passed along, e.g. if playback initiated by Plex Companion. Might be
# another user playing something! Token identifies user # another user playing something! Token identifies user
self.plex_transient_token = None self.plex_transient_token = None
@ -61,6 +55,7 @@ class Connection(object):
else: else:
self.server = 'http://%s:%s' % (self.host, self.port) self.server = 'http://%s:%s' % (self.host, self.port)
utils.window('pms_server', value=self.server) utils.window('pms_server', value=self.server)
self.online = False
LOG.debug('Set server %s (%s) to %s', LOG.debug('Set server %s (%s) to %s',
self.server_name, self.machine_identifier, self.server) self.server_name, self.machine_identifier, self.server)

View file

@ -12,9 +12,6 @@ class Sync(object):
return return
# Do we need to run a special library scan? # Do we need to run a special library scan?
self.run_lib_scan = None self.run_lib_scan = None
# Usually triggered by another Python instance - will have to be set (by
# polling window) through e.g. librarysync thread
self.suspend_library_thread = False
# Set if user decided to cancel sync # Set if user decided to cancel sync
self.stop_sync = False self.stop_sync = False
# Set during media playback if PKC should not do any syncs. Will NOT # Set during media playback if PKC should not do any syncs. Will NOT

View file

@ -160,15 +160,18 @@ class Task(object):
return not self.finished and not self._canceled return not self.finished and not self._canceled
def FunctionAsTask(Task): class FunctionAsTask(Task):
def __init__(self, function, *args, **kwargs): def __init__(self, function, callback, *args, **kwargs):
self.function = function self._function = function
self._callback = callback
self._args = args self._args = args
self._kwargs = kwargs self._kwargs = kwargs
super(FunctionAsTask, self).__init__() super(FunctionAsTask, self).__init__()
def run(self): def run(self):
self.function(*self._args, **self._kwargs) result = self._function(*self._args, **self._kwargs)
if self._callback:
self._callback(result)
class MutablePriorityQueue(Queue.PriorityQueue): class MutablePriorityQueue(Queue.PriorityQueue):

View file

@ -28,9 +28,11 @@ class DownloadUtils():
_shared_state = {} _shared_state = {}
# How many failed attempts before declaring PMS dead? # How many failed attempts before declaring PMS dead?
connectionAttempts = 1 connection_attempts = 1
count_error = 0
# How many 401 returns before declaring unauthorized? # How many 401 returns before declaring unauthorized?
unauthorizedAttempts = 2 unauthorized_attempts = 2
count_unauthorized = 0
# How long should we wait for an answer from the # How long should we wait for an answer from the
timeout = 30.0 timeout = 30.0
@ -69,11 +71,9 @@ class DownloadUtils():
self.setSSL() self.setSSL()
# Counters to declare PMS dead or unauthorized # Counters to declare PMS dead or unauthorized
# Use window variables because start of movies will be called with a
# new plugin instance - it's impossible to share data otherwise
if reset is True: if reset is True:
utils.window('countUnauthorized', value='0') self.count_error = 0
utils.window('countError', value='0') self.count_unauthorized = 0
# Retry connections to the server # Retry connections to the server
self.s.mount("http://", requests.adapters.HTTPAdapter(max_retries=1)) self.s.mount("http://", requests.adapters.HTTPAdapter(max_retries=1))
@ -210,9 +210,9 @@ class DownloadUtils():
else: else:
# We COULD contact the PMS, hence it ain't dead # We COULD contact the PMS, hence it ain't dead
if authenticate is True: if authenticate is True:
utils.window('countError', value='0') self.count_error = 0
if r.status_code != 401: if r.status_code != 401:
utils.window('countUnauthorized', value='0') self.count_unauthorized = 0
if r.status_code == 204: if r.status_code == 204:
# No body in the response # No body in the response
@ -230,22 +230,16 @@ class DownloadUtils():
LOG.info(r.text) LOG.info(r.text)
if '401 Unauthorized' in r.text: if '401 Unauthorized' in r.text:
# Truly unauthorized # Truly unauthorized
utils.window( self.count_unauthorized += 1
'countUnauthorized', if self.count_unauthorized >= self.unauthorized_attempts:
value=str(int(utils.window('countUnauthorized')) + 1))
if (int(utils.window('countUnauthorized')) >=
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 app.CONN.pms_status not in ('401', 'Auth'): # Unauthorized access, user no longer has access
# Tell others token has been revoked. app.ACCOUNT.log_out()
LOG.debug('Setting PMS server status to ' utils.dialog('notification',
'unauthorized') utils.lang(29999),
app.CONN.pms_status = '401' utils.lang(30017),
utils.dialog('notification', icon='{error}')
utils.lang(29999),
utils.lang(30017),
icon='{error}')
else: else:
# there might be other 401 where e.g. PMS under strain # there might be other 401 where e.g. PMS under strain
LOG.info('PMS might only be under strain') LOG.info('PMS might only be under strain')
@ -284,7 +278,7 @@ class DownloadUtils():
elif r.status_code == 403: elif r.status_code == 403:
# E.g. deleting a PMS item # E.g. deleting a PMS item
LOG.warn('PMS sent 403: Forbidden error for url %s', url) LOG.warn('PMS sent 403: Forbidden error for url %s', url)
return None return
else: else:
r.encoding = 'utf-8' r.encoding = 'utf-8'
LOG.warn('Unknown answer from PMS %s with status code %s. ', LOG.warn('Unknown answer from PMS %s with status code %s. ',
@ -294,14 +288,9 @@ class DownloadUtils():
# And now deal with the consequences of the exceptions # And now deal with the consequences of the exceptions
if authenticate is True: if authenticate is True:
# Make the addon aware of status # Make the addon aware of status
try: self.count_error += 1
utils.window('countError', if self.count_error >= self.connection_attempts:
value=str(int(utils.window('countError')) + 1)) LOG.warn('Failed to connect to %s too many times. '
if int(utils.window('countError')) >= self.connectionAttempts: 'Declare PMS dead', url)
LOG.warn('Failed to connect to %s too many times. ' app.CONN.online = False
'Declare PMS dead', url) return
utils.window('plex_online', value="false")
except ValueError:
# 'countError' not yet set
pass
return None

View file

@ -54,8 +54,8 @@ class InitialSetup(object):
# Get Plex credentials from settings file, if they exist # Get Plex credentials from settings file, if they exist
plexdict = PF.GetPlexLoginFromSettings() plexdict = PF.GetPlexLoginFromSettings()
self.plex_login = plexdict['plexLogin'] self.plex_login = plexdict['plexLogin']
self.plex_login_id = plexdict['plexid']
self.plex_token = plexdict['plexToken'] self.plex_token = plexdict['plexToken']
self.plexid = plexdict['plexid']
# Token for the PMS, not plex.tv # Token for the PMS, not plex.tv
self.pms_token = utils.settings('accessToken') self.pms_token = utils.settings('accessToken')
if self.plex_token: if self.plex_token:
@ -66,7 +66,7 @@ class InitialSetup(object):
Writes Plex username, token to plex.tv and Plex id to PKC settings Writes Plex username, token to plex.tv and Plex id to PKC settings
""" """
utils.settings('username', value=self.plex_login or '') utils.settings('username', value=self.plex_login or '')
utils.settings('userid', value=self.plexid or '') utils.settings('userid', value=self.plex_login_id or '')
utils.settings('plexToken', value=self.plex_token or '') utils.settings('plexToken', value=self.plex_token or '')
def enter_new_pms_address(self): def enter_new_pms_address(self):
@ -117,14 +117,11 @@ class InitialSetup(object):
Returns True if successful, or False if not Returns True if successful, or False if not
""" """
try: user = plex_tv.sign_in_with_pin()
user = plex_tv.sign_in_with_pin()
except:
utils.ERROR()
if user: if user:
self.plex_login = user.username self.plex_login = user.username
self.plex_token = user.authToken self.plex_token = user.authToken
self.plexid = user.id self.plex_login_id = user.id
return True return True
return False return False
@ -600,7 +597,7 @@ class InitialSetup(object):
goto_settings = utils.yesno_dialog(utils.lang(29999), goto_settings = utils.yesno_dialog(utils.lang(29999),
utils.lang(39017)) utils.lang(39017))
if goto_settings: if goto_settings:
state.PMS_STATUS = 'Stop' app.APP.suspend = True
executebuiltin( executebuiltin(
'Addon.OpenSettings(plugin.video.plexkodiconnect)') 'Addon.OpenSettings(plugin.video.plexkodiconnect)')
elif reboot is True: elif reboot is True:

View file

@ -54,6 +54,8 @@ class KodiMonitor(xbmc.Monitor):
Monitor the PKC settings for changes made by the user Monitor the PKC settings for changes made by the user
""" """
LOG.debug('PKC settings change detected') LOG.debug('PKC settings change detected')
# Assume that the user changed something so we can try to reconnect
app.APP.suspend = False
def onNotification(self, sender, method, data): def onNotification(self, sender, method, data):
""" """
@ -129,11 +131,10 @@ class KodiMonitor(xbmc.Monitor):
elif method == "System.OnSleep": elif method == "System.OnSleep":
# Connection is going to sleep # Connection is going to sleep
LOG.info("Marking the server as offline. SystemOnSleep activated.") LOG.info("Marking the server as offline. SystemOnSleep activated.")
utils.window('plex_online', value="sleep")
elif method == "System.OnWake": elif method == "System.OnWake":
# Allow network to wake up # Allow network to wake up
self.waitForAbort(10) self.waitForAbort(10)
utils.window('plex_online', value="false") app.CONN.online = False
elif method == "GUI.OnScreensaverDeactivated": elif method == "GUI.OnScreensaverDeactivated":
if utils.settings('dbSyncScreensaver') == "true": if utils.settings('dbSyncScreensaver') == "true":
self.waitForAbort(5) self.waitForAbort(5)
@ -511,7 +512,7 @@ def _record_playstate(status, ended):
xbmc.getCondVisibility('Window.IsVisible(Home.xml)')): xbmc.getCondVisibility('Window.IsVisible(Home.xml)')):
LOG.debug('Refreshing skin to update widgets') LOG.debug('Refreshing skin to update widgets')
xbmc.executebuiltin('ReloadSkin()') xbmc.executebuiltin('ReloadSkin()')
task = backgroundthread.FunctionAsTask(_clean_file_table) task = backgroundthread.FunctionAsTask(function=_clean_file_table)
backgroundthread.BGThreader.addTasksToFront(task) backgroundthread.BGThreader.addTasksToFront(task)

View file

@ -9,7 +9,7 @@ from .. import app
class libsync_mixin(object): class libsync_mixin(object):
def isCanceled(self): def isCanceled(self):
return (self._canceled or app.APP.stop_pkc or app.SYNC.stop_sync or return (self._canceled or app.APP.stop_pkc or app.SYNC.stop_sync or
app.SYNC.suspend_library_thread or app.SYNC.suspend_sync) app.APP.suspend_threads or app.SYNC.suspend_sync)
def update_kodi_library(video=True, music=True): def update_kodi_library(video=True, music=True):

View file

@ -18,7 +18,7 @@ PREFER_KODI_COLLECTION_ART = utils.settings('PreferKodiCollectionArt') == 'false
def suspends(): def suspends():
return (app.SYNC.suspend_library_thread or return (app.APP.suspend_threads or
app.SYNC.stop_sync or app.SYNC.stop_sync or
app.SYNC.db_scan or app.SYNC.db_scan or
app.SYNC.suspend_sync) app.SYNC.suspend_sync)

View file

@ -22,7 +22,7 @@ PLAYSTATE_SESSIONS = {}
def interrupt_processing(): def interrupt_processing():
return app.APP.stop_pkc or app.SYNC.suspend_library_thread or app.SYNC.stop_sync return app.APP.stop_pkc or app.APP.suspend_threads or app.SYNC.stop_sync
def multi_delete(input_list, delete_list): def multi_delete(input_list, delete_list):

View file

@ -101,7 +101,7 @@ class PlayqueueMonitor(backgroundthread.KillableThread):
""" """
Returns True if the thread is suspended Returns True if the thread is suspended
""" """
return self._suspended or app.CONN.pms_status return self._suspended or app.APP.suspend_threads
def _compare_playqueues(self, playqueue, new): def _compare_playqueues(self, playqueue, new):
""" """

View file

@ -84,7 +84,7 @@ class PlexCompanion(backgroundthread.KillableThread):
""" """
Returns True if the thread is suspended Returns True if the thread is suspended
""" """
return self._suspended or app.CONN.pms_status return self._suspended or app.APP.suspend
def _process_alexa(self, data): def _process_alexa(self, data):
xml = PF.GetPlexMetadata(data['key']) xml = PF.GetPlexMetadata(data['key'])

View file

@ -17,6 +17,7 @@ from . import playqueue
from . import variables as v from . import variables as v
from . import app from . import app
from . import loghandler from . import loghandler
from . import backgroundthread
from .windows import userselect from .windows import userselect
############################################################################### ###############################################################################
@ -25,11 +26,11 @@ LOG = logging.getLogger("PLEX.service")
############################################################################### ###############################################################################
WINDOW_PROPERTIES = ( WINDOW_PROPERTIES = (
"plex_online", "plex_command_processed", "plex_shouldStop", "plex_dbScan", "plex_command_processed", "plex_shouldStop", "plex_dbScan",
"plex_customplayqueue", "plex_playbackProps", "plex_customplayqueue", "plex_playbackProps",
"pms_token", "plex_token", "pms_server", "plex_machineIdentifier", "pms_token", "plex_token", "pms_server", "plex_machineIdentifier",
"plex_servername", "plex_authenticated", "PlexUserImage", "useDirectPaths", "plex_servername", "plex_authenticated", "PlexUserImage", "useDirectPaths",
"countError", "countUnauthorized", "plex_restricteduser", "plex_restricteduser",
"plex_allows_mediaDeletion", "plex_command", "plex_result", "plex_allows_mediaDeletion", "plex_command", "plex_result",
"plex_force_transcode_pix" "plex_force_transcode_pix"
) )
@ -40,13 +41,6 @@ class Service():
sync = None sync = None
plexcompanion = None plexcompanion = None
ws_running = False
alexa_running = False
sync_running = False
plexcompanion_running = False
kodimonitor_running = False
playback_starter_running = False
def __init__(self): def __init__(self):
# Initial logging # Initial logging
LOG.info("======== START %s ========", v.ADDON_NAME) LOG.info("======== START %s ========", v.ADDON_NAME)
@ -87,15 +81,71 @@ class Service():
# Init time-offset between Kodi and Plex # Init time-offset between Kodi and Plex
timing.KODI_PLEX_TIME_OFFSET = float(utils.settings('kodiplextimeoffset') or 0.0) timing.KODI_PLEX_TIME_OFFSET = float(utils.settings('kodiplextimeoffset') or 0.0)
def isCanceled(self): self.startup_completed = False
return xbmc.abortRequested self.server_has_been_online = True
self.welcome_msg = True
self.connection_check_counter = 0
# Flags for other threads
self.connection_check_running = False
self.auth_running = False
def on_connection_check(self, result):
"""
Call this method after PF.check_connection()
"""
try:
if result is False:
# Server is offline or cannot be reached
# Alert the user and suppress future warning
if app.CONN.online:
# PMS was online before
app.CONN.online = False
app.APP.suspend_threads = True
LOG.warn("Plex Media Server went offline")
if utils.settings('show_pms_offline') == 'true':
utils.dialog('notification',
utils.lang(33001),
"%s %s" % (utils.lang(29999),
utils.lang(33002)),
icon='{plex}',
sound=False)
self.connection_check_counter += 1
# Periodically check if the IP changed every 15 seconds
if self.connection_check_counter > 150:
self.connection_check_counter = 0
server = self.setup.pick_pms()
if server:
self.setup.write_pms_to_settings(server)
app.CONN.load()
else:
# Server is online
self.connection_check_counter = 0
if not app.CONN.online:
# Server was offline before
if (self.welcome_msg is False and
utils.settings('show_pms_offline') == 'true'):
# Alert the user that server is online
utils.dialog('notification',
utils.lang(29999),
utils.lang(33003),
icon='{plex}',
time=5000,
sound=False)
LOG.info("Server is online and ready")
if app.ACCOUNT.authenticated:
# Server got offline when we were authenticated.
# Hence resume threads
app.APP.suspend_threads = False
app.CONN.online = True
finally:
self.connection_check_running = False
def log_out(self): def log_out(self):
""" """
Ensures that lib sync threads are suspended; signs out user Ensures that lib sync threads are suspended; signs out user
""" """
LOG.info('Log-out requested') LOG.info('Log-out requested')
app.SYNC.suspend_library_thread = True app.APP.suspend_threads = True
i = 0 i = 0
while app.SYNC.db_scan: while app.SYNC.db_scan:
i += 1 i += 1
@ -105,7 +155,7 @@ class Service():
# Failed to reset PMS and plex.tv connects. Try to restart Kodi # Failed to reset PMS and plex.tv connects. Try to restart Kodi
utils.messageDialog(utils.lang(29999), utils.lang(39208)) utils.messageDialog(utils.lang(29999), utils.lang(39208))
# Resuming threads, just in case # Resuming threads, just in case
app.SYNC.suspend_library_thread = False app.APP.suspend_threads = False
return False return False
LOG.info('Successfully stopped library sync') LOG.info('Successfully stopped library sync')
app.ACCOUNT.log_out() app.ACCOUNT.log_out()
@ -132,6 +182,9 @@ class Service():
# Wipe Kodi and Plex database as well as playlists and video nodes # Wipe Kodi and Plex database as well as playlists and video nodes
utils.wipe_database() utils.wipe_database()
app.CONN.load() app.CONN.load()
app.ACCOUNT.set_unauthenticated()
self.server_has_been_online = False
self.welcome_msg = False
LOG.info("Choosing new PMS complete") LOG.info("Choosing new PMS complete")
return True return True
@ -142,16 +195,19 @@ class Service():
utils.delete_playlists() utils.delete_playlists()
# Remove video nodes # Remove video nodes
utils.delete_nodes() utils.delete_nodes()
app.ACCOUNT.set_unauthenticated()
return True return True
def toggle_plex_tv(self): def toggle_plex_tv(self):
if utils.settings('plexToken'): if app.ACCOUNT.plex_token:
LOG.info('Reseting plex.tv credentials in settings') LOG.info('Resetting plex.tv credentials in settings')
self.log_out()
app.ACCOUNT.clear() app.ACCOUNT.clear()
return True
else: else:
LOG.info('Login to plex.tv') LOG.info('Login to plex.tv')
return self.setup.plex_tv_sign_in() if self.setup.plex_tv_sign_in():
self.setup.write_credentials_to_settings()
app.ACCOUNT.load()
def authenticate(self): def authenticate(self):
""" """
@ -160,9 +216,30 @@ class Service():
Returns True if successful, False if not. 'aborted' if user chose to Returns True if successful, False if not. 'aborted' if user chose to
abort abort
""" """
if self._do_auth():
if self.welcome_msg is True:
# Reset authentication warnings
self.welcome_msg = False
utils.dialog('notification',
utils.lang(29999),
"%s %s" % (utils.lang(33000),
app.ACCOUNT.plex_username),
icon='{plex}',
time=2000,
sound=False)
app.APP.suspend_threads = False
self.auth_running = False
def enter_new_pms_address(self):
if self.setup.enter_new_pms_address():
app.CONN.load()
app.ACCOUNT.set_unauthenticated()
self.server_has_been_online = False
self.welcome_msg = False
def _do_auth(self):
LOG.info('Authenticating user') LOG.info('Authenticating user')
if app.ACCOUNT.plex_username and not app.ACCOUNT.force_login: if app.ACCOUNT.plex_username and not app.ACCOUNT.force_login: # Found a user in the settings, try to authenticate
# Found a user in the settings, try to authenticate
LOG.info('Trying to authenticate with old settings') LOG.info('Trying to authenticate with old settings')
res = PF.check_connection(app.CONN.server, res = PF.check_connection(app.CONN.server,
token=app.ACCOUNT.pms_token, token=app.ACCOUNT.pms_token,
@ -171,8 +248,9 @@ class Service():
LOG.error('Something went wrong while checking connection') LOG.error('Something went wrong while checking connection')
return False return False
elif res == 401: elif res == 401:
LOG.error('User token no longer valid. Sign user out') LOG.error('User %s no longer has access - signing user out',
app.ACCOUNT.clear() app.ACCOUNT.plex_username)
self.log_out()
return False return False
elif res >= 400: elif res >= 400:
LOG.error('Answer from PMS is not as expected') LOG.error('Answer from PMS is not as expected')
@ -188,7 +266,7 @@ class Service():
user, _ = userselect.start() user, _ = userselect.start()
if not user: if not user:
LOG.info('No user received') LOG.info('No user received')
app.CONN.pms_status = 'Stop' app.APP.suspend = True
return False return False
username = user.title username = user.title
user_id = user.id user_id = user.id
@ -206,10 +284,12 @@ class Service():
return False return False
elif res == 401: elif res == 401:
if app.ACCOUNT.plex_token: if app.ACCOUNT.plex_token:
LOG.error('Token no longer valid') LOG.error('User %s does not have access to PMS %s on %s',
# "Your Plex token is no longer valid. Logging-out of plex.tv" username, app.CONN.server_name, app.CONN.server)
utils.messageDialog(utils.lang(29999), utils.lang(33010)) # "User is unauthorized for server {0}"
app.ACCOUNT.clear() utils.messageDialog(utils.lang(29999),
utils.lang(33010).format(app.CONN.server_name))
self.log_out()
return False return False
else: else:
# "Failed to authenticate. Did you login to plex.tv?" # "Failed to authenticate. Did you login to plex.tv?"
@ -219,6 +299,7 @@ class Service():
app.ACCOUNT.load() app.ACCOUNT.load()
continue continue
else: else:
app.APP.suspend = True
return False return False
elif res >= 400: elif res >= 400:
LOG.error('Answer from PMS is not as expected') LOG.error('Answer from PMS is not as expected')
@ -235,12 +316,12 @@ class Service():
def ServiceEntryPoint(self): def ServiceEntryPoint(self):
# Important: Threads depending on abortRequest will not trigger # Important: Threads depending on abortRequest will not trigger
# if profile switch happens more than once. # if profile switch happens more than once.
app.init()
# Some plumbing # Some plumbing
app.init()
app.APP.monitor = kodimonitor.KodiMonitor() app.APP.monitor = kodimonitor.KodiMonitor()
app.APP.player = xbmc.Player() app.APP.player = xbmc.Player()
artwork.IMAGE_CACHING_SUSPENDS = [ artwork.IMAGE_CACHING_SUSPENDS = [
app.SYNC.suspend_library_thread, app.APP.suspend_threads,
app.SYNC.stop_sync, app.SYNC.stop_sync,
app.SYNC.db_scan app.SYNC.db_scan
] ]
@ -262,11 +343,9 @@ class Service():
self.playback_starter = playback_starter.PlaybackStarter() self.playback_starter = playback_starter.PlaybackStarter()
self.playqueue = playqueue.PlayqueueMonitor() self.playqueue = playqueue.PlayqueueMonitor()
server_online = True # Main PKC program loop
welcome_msg = True while not xbmc.abortRequested:
counter = 0 # Check for Kodi profile change
while not self.isCanceled():
if utils.window('plex_kodiProfile') != v.KODI_PROFILE: if utils.window('plex_kodiProfile') != v.KODI_PROFILE:
# Profile change happened, terminate this thread and others # Profile change happened, terminate this thread and others
LOG.info("Kodi profile was: %s and changed to: %s. " LOG.info("Kodi profile was: %s and changed to: %s. "
@ -274,6 +353,7 @@ class Service():
v.KODI_PROFILE, utils.window('plex_kodiProfile')) v.KODI_PROFILE, utils.window('plex_kodiProfile'))
break break
# Check for PKC commands from other Python instances
plex_command = utils.window('plex_command') plex_command = utils.window('plex_command')
if plex_command: if plex_command:
# Commands/user interaction received from other PKC Python # Commands/user interaction received from other PKC Python
@ -291,24 +371,21 @@ class Service():
'dummy?mode=context_menu&%s' 'dummy?mode=context_menu&%s'
% plex_command.replace('CONTEXT_menu?', '')) % plex_command.replace('CONTEXT_menu?', ''))
elif plex_command == 'choose_pms_server': elif plex_command == 'choose_pms_server':
if self.choose_pms_server(): task = backgroundthread.FunctionAsTask(
utils.window('plex_online', clear=True) self.choose_pms_server, None)
app.ACCOUNT.set_unauthenticated() backgroundthread.BGThreader.addTasksToFront([task])
server_online = False
welcome_msg = False
elif plex_command == 'switch_plex_user': elif plex_command == 'switch_plex_user':
if self.switch_plex_user(): task = backgroundthread.FunctionAsTask(
app.ACCOUNT.set_unauthenticated() self.switch_plex_user, None)
backgroundthread.BGThreader.addTasksToFront([task])
elif plex_command == 'enter_new_pms_address': elif plex_command == 'enter_new_pms_address':
if self.setup.enter_new_pms_address(): task = backgroundthread.FunctionAsTask(
if self.log_out(): self.enter_new_pms_address, None)
utils.window('plex_online', clear=True) backgroundthread.BGThreader.addTasksToFront([task])
app.ACCOUNT.set_unauthenticated()
server_online = False
welcome_msg = False
elif plex_command == 'toggle_plex_tv_sign_in': elif plex_command == 'toggle_plex_tv_sign_in':
if self.toggle_plex_tv(): task = backgroundthread.FunctionAsTask(
app.ACCOUNT.set_unauthenticated() self.toggle_plex_tv, None)
backgroundthread.BGThreader.addTasksToFront([task])
elif plex_command == 'repair-scan': elif plex_command == 'repair-scan':
app.SYNC.run_lib_scan = 'repair' app.SYNC.run_lib_scan = 'repair'
elif plex_command == 'full-scan': elif plex_command == 'full-scan':
@ -319,120 +396,51 @@ class Service():
app.SYNC.run_lib_scan = 'textures' app.SYNC.run_lib_scan = 'textures'
continue continue
if app.APP.suspend:
app.APP.monitor.waitForAbort(0.1)
continue
# Before proceeding, need to make sure: # Before proceeding, need to make sure:
# 1. Server is online # 1. Server is online
# 2. User is set # 2. User is set
# 3. User has access to the server # 3. User has access to the server
if utils.window('plex_online') == "true": if not app.CONN.online:
# Plex server is online # Not online
if app.CONN.pms_status == 'Stop':
app.APP.monitor.waitForAbort(0.05)
continue
elif app.CONN.pms_status == '401':
# Unauthorized access, revoke token
LOG.info('401 received - revoking token')
app.ACCOUNT.clear()
app.CONN.pms_status = 'Auth'
utils.window('plex_serverStatus', value='Auth')
continue
if not app.ACCOUNT.authenticated:
LOG.info('Not yet authenticated')
# Do authentication
if not self.authenticate():
continue
# Start up events
if welcome_msg is True:
# Reset authentication warnings
welcome_msg = False
utils.dialog('notification',
utils.lang(29999),
"%s %s" % (utils.lang(33000),
app.ACCOUNT.plex_username),
icon='{plex}',
time=2000,
sound=False)
# Start monitoring kodi events
if not self.kodimonitor_running:
self.kodimonitor_running = True
self.specialmonitor.start()
# Start the Websocket Client
if not self.ws_running:
self.ws_running = True
self.ws.start()
# Start the Alexa thread
if (not self.alexa_running and
utils.settings('enable_alexa') == 'true'):
self.alexa_running = True
self.alexa.start()
# Start the syncing thread
if not self.sync_running:
self.sync_running = True
self.sync.start()
# Start the Plex Companion thread
if not self.plexcompanion_running:
self.plexcompanion_running = True
self.plexcompanion.start()
if not self.playback_starter_running:
self.playback_starter_running = True
self.playback_starter.start()
self.playqueue.start()
else:
# Wait until Plex server is online
# or Kodi is shut down.
server = app.CONN.server server = app.CONN.server
if not server: if not server:
# No server info set in add-on settings # No server info set in add-on settings
pass pass
elif PF.check_connection(server, verifySSL=True) is False: elif not self.connection_check_running:
# Server is offline or cannot be reached self.connection_check_running = True
# Alert the user and suppress future warning task = backgroundthread.FunctionAsTask(
if server_online: PF.check_connection,
server_online = False self.on_connection_check,
utils.window('plex_online', value="false") server,
# Suspend threads verifySSL=True)
app.SYNC.suspend_library_thread = True backgroundthread.BGThreader.addTasksToFront([task])
LOG.warn("Plex Media Server went offline") continue
if utils.settings('show_pms_offline') == 'true': elif not app.ACCOUNT.authenticated:
utils.dialog('notification', # Plex server is online, but we're not yet authenticated
utils.lang(33001), if not self.auth_running:
"%s %s" % (utils.lang(29999), self.auth_running = True
utils.lang(33002)), task = backgroundthread.FunctionAsTask(
icon='{plex}', self.authenticate, None)
sound=False) backgroundthread.BGThreader.addTasksToFront([task])
counter += 1 continue
# Periodically check if the IP changed, e.g. per minute elif not self.startup_completed:
if counter > 20: self.startup_completed = True
counter = 0 self.specialmonitor.start()
setup = initialsetup.InitialSetup() self.ws.start()
tmp = setup.pick_pms() self.sync.start()
if tmp: self.plexcompanion.start()
setup.write_pms_to_settings(tmp) self.playback_starter.start()
app.CONN.load() self.playqueue.start()
else: if utils.settings('enable_alexa') == 'true':
# Server is online self.alexa.start()
counter = 0
if not server_online:
# Server was offline when Kodi started.
server_online = True
# Alert the user that server is online.
if (welcome_msg is False and
utils.settings('show_pms_offline') == 'true'):
utils.dialog('notification',
utils.lang(29999),
utils.lang(33003),
icon='{plex}',
time=5000,
sound=False)
LOG.info("Server %s is online and ready.", server)
utils.window('plex_online', value="true")
if app.ACCOUNT.authenticated:
# Server got offline when we were authenticated.
# Hence resume threads
app.SYNC.suspend_library_thread = False
if app.APP.monitor.waitForAbort(0.05): app.APP.monitor.waitForAbort(0.1)
# Abort was requested while waiting. We should exit
break # EXITING PKC
# Tell all threads to terminate (e.g. several lib sync threads) # Tell all threads to terminate (e.g. several lib sync threads)
app.APP.stop_pkc = True app.APP.stop_pkc = True
utils.window('plex_service_started', clear=True) utils.window('plex_service_started', clear=True)

View file

@ -41,7 +41,7 @@ class Sync(backgroundthread.KillableThread):
super(Sync, self).__init__() super(Sync, self).__init__()
def isSuspended(self): def isSuspended(self):
return self._suspended or app.SYNC.suspend_library_thread return self._suspended or app.APP.suspend_threads
def show_kodi_note(self, message, icon="plex", force=False): def show_kodi_note(self, message, icon="plex", force=False):
""" """

View file

@ -137,7 +137,7 @@ class PMS_Websocket(WebSocket):
Returns True if the thread is suspended Returns True if the thread is suspended
""" """
return (self._suspended or return (self._suspended or
app.SYNC.suspend_library_thread or app.APP.suspend_threads or
app.SYNC.background_sync_disabled) app.SYNC.background_sync_disabled)
def getUri(self): def getUri(self):