From a3c2d217579945222f456eb803b7f339d4ce581f Mon Sep 17 00:00:00 2001 From: tomkat83 Date: Thu, 10 Mar 2016 16:02:46 +0100 Subject: [PATCH] Fix userclient --- resources/language/English/strings.xml | 1 + resources/language/German/strings.xml | 1 + resources/lib/PlexAPI.py | 87 ++++++++++----- resources/lib/PlexCompanion.py | 7 ++ resources/lib/PlexFunctions.py | 15 +-- resources/lib/artwork.py | 4 +- resources/lib/downloadutils.py | 9 +- resources/lib/entrypoint.py | 16 ++- resources/lib/initialsetup.py | 9 +- resources/lib/itemtypes.py | 4 +- resources/lib/playbackutils.py | 4 +- resources/lib/playlist.py | 4 +- resources/lib/playutils.py | 4 +- resources/lib/plexbmchelper/subscribers.py | 4 +- resources/lib/read_embyserver.py | 4 +- resources/lib/userclient.py | 117 ++++++--------------- service.py | 5 +- 17 files changed, 144 insertions(+), 151 deletions(-) diff --git a/resources/language/English/strings.xml b/resources/language/English/strings.xml index e5e9cf6b..283c52bf 100644 --- a/resources/language/English/strings.xml +++ b/resources/language/English/strings.xml @@ -397,6 +397,7 @@ Could not log in user Please try again. unknown + or press No to not sign in. Library sync thread has crashed. You should restart Kodi now. Please report this on the forum diff --git a/resources/language/German/strings.xml b/resources/language/German/strings.xml index f513de82..e8d7aca7 100644 --- a/resources/language/German/strings.xml +++ b/resources/language/German/strings.xml @@ -329,6 +329,7 @@ Anmeldung fehlgeschlagen für Benutzer Bitte erneut versuchen. unbekannt + oder Nein drücken, um nicht bei plex.tv anzumelden Die Synchronisierung der Plex Bibliotheken ist abgestürzt. Bitte Kodi neu starten. Danke, wenn Sie sich die Zeit nehmen und im Plex Forum vom Absturz berichten. diff --git a/resources/lib/PlexAPI.py b/resources/lib/PlexAPI.py index 1f1e47a4..33f57771 100644 --- a/resources/lib/PlexAPI.py +++ b/resources/lib/PlexAPI.py @@ -67,7 +67,7 @@ except ImportError: class PlexAPI(): # CONSTANTS # Timeout for POST/GET commands, I guess in seconds - timeout = 60 + timeout = 10 # VARIABLES def __init__(self): @@ -79,9 +79,9 @@ class PlexAPI(): self.deviceName = client.getDeviceName() self.plexversion = client.getVersion() self.platform = client.getPlatform() - self.userId = utils.window('emby_currUser') - self.token = utils.window('emby_accessToken%s' % self.userId) - self.server = utils.window('emby_server%s' % self.userId) + self.userId = utils.window('currUserId') + self.token = utils.window('pms_token') + self.server = utils.window('pms_server') self.doUtils = downloadutils.DownloadUtils() @@ -182,9 +182,11 @@ class PlexAPI(): dialog.ok(self.addonName, string(39303)) return False # Go to https://plex.tv/pin and enter the code: + # Or press No to cancel the sign in. answer = dialog.yesno(self.addonName, string(39304) + "\n\n", - code) + code + "\n\n", + string(39311)) if not answer: return False count = 0 @@ -638,14 +640,17 @@ class PlexAPI(): else: # MyPlex servers self.getPMSListFromMyPlex(ATV_udid, authtoken) + # Delete plex.tv again + del self.g_PMS[ATV_udid]['plex.tv'] # all servers - update enableGzip for uuid_id in self.g_PMS.get(ATV_udid, {}): # Ping to check whether we need HTTPs or HTTP url = (self.getPMSProperty(ATV_udid, uuid_id, 'ip') + ':' + self.getPMSProperty(ATV_udid, uuid_id, 'port')) if PMSHttpsEnabled(url): - self.logMsg('PMS %s talks HTTPS' % uuid_id, 1) self.updatePMSProperty(ATV_udid, uuid_id, 'scheme', 'https') + else: + self.updatePMSProperty(ATV_udid, uuid_id, 'scheme', 'http') # enable Gzip if not on same host, local&remote PMS depending # on setting enableGzip = (not self.getPMSProperty(ATV_udid, uuid_id, 'ip') == IP_self) \ @@ -657,8 +662,6 @@ class PlexAPI(): and True) == 'True' ) self.updatePMSProperty(ATV_udid, uuid_id, 'enableGzip', enableGzip) - # Delete plex.tv again - del self.g_PMS[ATV_udid]['plex.tv'] def getPMSListFromMyPlex(self, ATV_udid, authtoken): """ @@ -1128,13 +1131,18 @@ class PlexAPI(): trials += 1 continue # Switch to this Plex Home user, if applicable - username, usertoken = self.PlexSwitchHomeUser( + result = self.PlexSwitchHomeUser( user['id'], pin, plexToken, utils.settings('plex_machineIdentifier')) + if result: + # Successfully retrieved username: break out of while loop + username = result['username'] + usertoken = result['usertoken'] + break # Couldn't get user auth - if not username: + else: trials += 1 # Could not login user, please try again if not dialog.yesno(self.addonName, @@ -1142,9 +1150,6 @@ class PlexAPI(): string(39309)): # User chose to cancel break - else: - # Successfully retrieved username: break out of while loop - break if not username: self.logMsg('Failed signing in a user to plex.tv', -1) @@ -1161,6 +1166,7 @@ class PlexAPI(): def PlexSwitchHomeUser(self, userId, pin, token, machineIdentifier): """ Retrieves Plex home token for a Plex home user. + Returns False if unsuccessful Input: userId id of the Plex home user @@ -1168,9 +1174,14 @@ class PlexAPI(): token token for plex.tv Output: - (username, token) + { + 'username' + 'usertoken' Might be empty strings if no token found + for the machineIdentifier that was chosen + } - Returns 2 empty strings if unsuccessful + Also sets the settings variable settings('accessToken'), + settings('userid') and settings('username') with new plex token """ url = 'https://plex.tv/api/home/users/' + userId + '/switch' if pin: @@ -1181,32 +1192,50 @@ class PlexAPI(): answer.attrib except: self.logMsg('Error: plex.tv switch HomeUser change failed', -1) - return ('', '') + return False username = answer.attrib.get('title', '') token = answer.attrib.get('authenticationToken', '') + userid = answer.attrib.get('id', '') - # Get final token + # Write to settings file + utils.settings('username', username) + utils.settings('userid', userid) + utils.settings('accessToken', token) + + # Get final token to the PMS we've chosen url = 'https://plex.tv/api/resources?includeHttps=1' xml = self.TalkToPlexServer(url, talkType="GET", token=token) try: xml.attrib except: - self.logMsg('switch HomeUser failed - plex.tv answer wrong', -1) - return ('', '') + self.logMsg('Answer from plex.tv not as excepted', -1) + # Set to empty iterable list for loop + xml = [] found = 0 + self.logMsg('Our machineIdentifier is %s' % machineIdentifier, 0) for device in xml: - if device.attrib.get('clientIdentifier') == machineIdentifier: + identifier = device.attrib.get('clientIdentifier') + self.logMsg('Found a Plex machineIdentifier: %s' % identifier, 0) + if (identifier in machineIdentifier or + machineIdentifier in identifier): found += 1 token = device.attrib.get('accessToken') - if found == 0: - self.logMsg('No tokens found for your server!', -1) - return ('', '') - self.logMsg('Plex.tv switch HomeUser change successfull', 0) - self.logMsg("username: %s, token: xxxx. " % username, 0) - return (username, token) + result = { + 'username': username, + } + if found == 0: + self.logMsg('No tokens found for your server!', 0) + self.logMsg('Using empty string as token', 0) + result['usertoken'] = '' + else: + result['usertoken'] = token + + self.logMsg('Plex.tv switch HomeUser change successfull for user %s' + % username, 0) + return result def MyPlexListHomeUsers(self, authtoken): """ @@ -1430,9 +1459,9 @@ class API(): self.clientId = self.clientinfo.getDeviceId() self.__language__ = xbmcaddon.Addon().getLocalizedString - self.userId = utils.window('emby_currUser') - self.server = utils.window('emby_server%s' % self.userId) - self.token = utils.window('emby_accessToken%s' % self.userId) + self.userId = utils.window('currUserId') + self.server = utils.window('pms_server') + self.token = utils.window('pms_token') def setPartNumber(self, number=None): """ diff --git a/resources/lib/PlexCompanion.py b/resources/lib/PlexCompanion.py index c79cb5bc..bf46a9a1 100644 --- a/resources/lib/PlexCompanion.py +++ b/resources/lib/PlexCompanion.py @@ -36,6 +36,7 @@ class PlexCompanion(threading.Thread): def run(self): start_count = 0 + window = utils.window while True: try: httpd = listener.ThreadedHTTPServer( @@ -67,6 +68,12 @@ class PlexCompanion(threading.Thread): if self.threadStopped(): break xbmc.sleep(3000) + # If we are not authorized, sleep + # Otherwise, we trigger a download which leads to a + # re-authorizations + if window('emby_serverStatus'): + xbmc.sleep(3000) + continue try: httpd.handle_request() diff --git a/resources/lib/PlexFunctions.py b/resources/lib/PlexFunctions.py index 6fc744f4..6c740346 100644 --- a/resources/lib/PlexFunctions.py +++ b/resources/lib/PlexFunctions.py @@ -357,14 +357,17 @@ def PMSHttpsEnabled(url): This is done by GET /identity (returns an error if https is enabled and we are trying to use http) + + Prefers HTTPS over HTTP """ - xml = downloadutils.DownloadUtils().downloadUrl('http://%s/identity' % url) + xml = downloadutils.DownloadUtils().downloadUrl( + 'https://%s/identity' % url) try: - # received a valid XML - http connection is possible + # received a valid XML - https connection is possible xml.attrib - logMsg('PMSHttpsEnabled', 'PMS on %s talks HTTP' % url, 1) - return False - except: - # couldn't get an xml - switch to https traffic logMsg('PMSHttpsEnabled', 'PMS on %s talks HTTPS' % url, 1) return True + except: + # couldn't get an xml - switch to http traffic + logMsg('PMSHttpsEnabled', 'PMS on %s talks HTTPS' % url, 1) + return False diff --git a/resources/lib/artwork.py b/resources/lib/artwork.py index 9bface89..75988e63 100644 --- a/resources/lib/artwork.py +++ b/resources/lib/artwork.py @@ -38,8 +38,8 @@ class Artwork(): if not self.xbmc_port and self.enableTextureCache: self.setKodiWebServerDetails() - self.userId = utils.window('emby_currUser') - self.server = utils.window('emby_server%s' % self.userId) + self.userId = utils.window('currUserId') + self.server = utils.window('pms_server') def double_urlencode(self, text): text = self.single_urlencode(text) diff --git a/resources/lib/downloadutils.py b/resources/lib/downloadutils.py index c366d464..4b75b252 100644 --- a/resources/lib/downloadutils.py +++ b/resources/lib/downloadutils.py @@ -222,10 +222,9 @@ class DownloadUtils(): # request session does not exists self.logMsg("Request session does not exist: start one", 1) # Get user information - self.userId = utils.window('emby_currUser') - self.server = utils.window('emby_server%s' % self.userId) - self.token = utils.window( - 'emby_accessToken%s' % self.userId) + self.userId = utils.window('currUserId') + self.server = utils.window('pms_server') + self.token = utils.window('pms_token') header = self.getHeader(options=headerOptions) verifyssl = False cert = None @@ -378,6 +377,7 @@ class DownloadUtils(): # Emby server errors if r.headers['X-Application-Error-Code'] == "ParentalControl": # Parental control - access restricted + self.logMsg('Setting emby_serverStatus to restricted') utils.window('emby_serverStatus', value="restricted") xbmcgui.Dialog().notification( heading=self.addonName, @@ -392,6 +392,7 @@ class DownloadUtils(): elif status not in ("401", "Auth"): # Tell userclient token has been revoked. + self.logMsg('Setting emby_serverStatus to 401') utils.window('emby_serverStatus', value="401") self.logMsg("HTTP Error: %s" % e, 0) xbmcgui.Dialog().notification( diff --git a/resources/lib/entrypoint.py b/resources/lib/entrypoint.py index ce41f56b..d64d510b 100644 --- a/resources/lib/entrypoint.py +++ b/resources/lib/entrypoint.py @@ -93,17 +93,15 @@ def reConnect(): utils.settings('plexLogin', value="") utils.settings('plexToken', value=""), utils.settings('plexid', value="") - utils.settings('plexHomeSize', value="") + utils.settings('plexHomeSize', value="1") utils.settings('plexAvatar', value="") # Wait max for 5 seconds for all lib scans to finish counter = 0 while utils.window('emby_dbScan') == 'true': if counter > 100: - dialog.ok( - heading=addonName, - message=string(39208), - ) + dialog.ok(heading=addonName, + message=string(39208)) # Resuming threads, just in case utils.window('suspend_LibraryThread', clear=True) # Abort reConnection @@ -117,10 +115,8 @@ def reConnect(): counter = 0 while utils.window('emby_serverStatus') == "401": if counter > 100: - dialog.ok( - heading=addonName, - message=string(39208), - ) + dialog.ok(heading=addonName, + message=string(39208)) # Abort reConnection return counter += 1 @@ -343,7 +339,7 @@ def addUser(): clientInfo = clientinfo.ClientInfo() deviceId = clientInfo.getDeviceId() deviceName = clientInfo.getDeviceName() - userid = utils.window('emby_currUser') + userid = utils.window('currUserId') dialog = xbmcgui.Dialog() # Get session diff --git a/resources/lib/initialsetup.py b/resources/lib/initialsetup.py index 8c4f137c..b1fb8dcb 100644 --- a/resources/lib/initialsetup.py +++ b/resources/lib/initialsetup.py @@ -44,7 +44,8 @@ class InitialSetup(): plexLogin = plexdict['plexLogin'] plexToken = plexdict['plexToken'] plexid = plexdict['plexid'] - self.logMsg('Plex info retrieved from settings', 1) + if plexToken: + self.logMsg('Found plex.tv token in settings', 0) dialog = xbmcgui.Dialog() @@ -54,6 +55,7 @@ class InitialSetup(): chk = self.plx.CheckConnection('plex.tv', plexToken) # HTTP Error: unauthorized. Token is no longer valid if chk == 401: + self.logMsg('plex.tv connection returned HTTP 401', 0) # Delete token in the settings utils.settings('plexToken', value='') # Could not login, please try again @@ -66,10 +68,12 @@ class InitialSetup(): plexid = result['plexid'] elif chk is False or chk >= 400: # Problems connecting to plex.tv. Network or internet issue? + self.logMsg('plex.tv connection returned HTTP %s' + % str(chk), 0) dialog.ok(self.addonName, string(39010)) else: - # Successful connected to plex.tv + self.logMsg('plex.tv connection with token successful', 0) # Refresh the info from Plex.tv url = 'https://plex.tv/' path = 'users/account' @@ -86,6 +90,7 @@ class InitialSetup(): self.logMsg('Updated Plex info from plex.tv', 0) else: self.logMsg('Failed to update Plex info from plex.tv', -1) + # If a Plex server IP has already been set, return. if server and forcePlexTV is False: self.logMsg("Server is already set.", 0) diff --git a/resources/lib/itemtypes.py b/resources/lib/itemtypes.py index 7ba5405a..b7b3e0b3 100644 --- a/resources/lib/itemtypes.py +++ b/resources/lib/itemtypes.py @@ -1595,8 +1595,8 @@ class Music(Items): self.enableimportsongrating = utils.settings('enableImportSongRating') == "true" self.enableexportsongrating = utils.settings('enableExportSongRating') == "true" self.enableupdatesongrating = utils.settings('enableUpdateSongRating') == "true" - self.userid = utils.window('emby_currUser') - self.server = utils.window('emby_server%s' % self.userid) + self.userid = utils.window('currUserId') + self.server = utils.window('pms_server') def __enter__(self): """ diff --git a/resources/lib/playbackutils.py b/resources/lib/playbackutils.py index d00099c1..5d6ec4dd 100644 --- a/resources/lib/playbackutils.py +++ b/resources/lib/playbackutils.py @@ -34,8 +34,8 @@ class PlaybackUtils(): self.clientInfo = clientinfo.ClientInfo() self.addonName = self.clientInfo.getAddonName() - self.userid = utils.window('emby_currUser') - self.server = utils.window('emby_server%s' % self.userid) + self.userid = utils.window('currUserId') + self.server = utils.window('pms_server') self.artwork = artwork.Artwork() self.emby = embyserver.Read_EmbyServer() diff --git a/resources/lib/playlist.py b/resources/lib/playlist.py index 9d0334ee..2b3ef2bd 100644 --- a/resources/lib/playlist.py +++ b/resources/lib/playlist.py @@ -21,8 +21,8 @@ import PlexAPI class Playlist(): def __init__(self): - self.userid = utils.window('emby_currUser') - self.server = utils.window('emby_server%s' % self.userid) + self.userid = utils.window('currUserId') + self.server = utils.window('pms_server') self.emby = embyserver.Read_EmbyServer() diff --git a/resources/lib/playutils.py b/resources/lib/playutils.py index 019d7614..8e4647ff 100644 --- a/resources/lib/playutils.py +++ b/resources/lib/playutils.py @@ -26,8 +26,8 @@ class PlayUtils(): self.clientInfo = clientinfo.ClientInfo() - self.userid = utils.window('emby_currUser') - self.server = utils.window('emby_server%s' % self.userid) + self.userid = utils.window('currUserId') + self.server = utils.window('pms_server') self.machineIdentifier = utils.window('plex_machineIdentifier') def getPlayUrl(self, partNumber=None): diff --git a/resources/lib/plexbmchelper/subscribers.py b/resources/lib/plexbmchelper/subscribers.py index 4bc9a3b1..c5e63ab2 100644 --- a/resources/lib/plexbmchelper/subscribers.py +++ b/resources/lib/plexbmchelper/subscribers.py @@ -70,8 +70,8 @@ class SubscriptionManager: WINDOW = xbmcgui.Window(10000) # pbmc_server = str(WINDOW.getProperty('plexbmc.nowplaying.server')) - # userId = str(WINDOW.getProperty('emby_currUser')) - # pbmc_server = str(WINDOW.getProperty('emby_server%s' % userId)) + # userId = str(WINDOW.getProperty('currUserId')) + # pbmc_server = str(WINDOW.getProperty('pms_server')) pbmc_server = None keyid = None count = 0 diff --git a/resources/lib/read_embyserver.py b/resources/lib/read_embyserver.py index 9f907e73..a2851d5d 100644 --- a/resources/lib/read_embyserver.py +++ b/resources/lib/read_embyserver.py @@ -19,8 +19,8 @@ class Read_EmbyServer(): self.doUtils = downloadutils.DownloadUtils().downloadUrl - self.userId = utils.window('emby_currUser') - self.server = utils.window('emby_server%s' % self.userId) + self.userId = utils.window('currUserId') + self.server = utils.window('pms_server') def split_list(self, itemlist, size): # Split up list in pieces of size. Will generate a list of lists diff --git a/resources/lib/userclient.py b/resources/lib/userclient.py index ccac74fb..72084b36 100644 --- a/resources/lib/userclient.py +++ b/resources/lib/userclient.py @@ -71,41 +71,6 @@ class UserClient(threading.Thread): return logLevel - def getUserId(self, username=None): - - log = self.logMsg - window = utils.window - settings = utils.settings - - if username is None: - username = self.getUsername() - w_userId = window('emby_currUser') - s_userId = settings('userId') - - # Verify the window property - if w_userId: - if not s_userId: - # Save access token if it's missing from settings - settings('userId', value=w_userId) - log("Returning userId %s from WINDOW for username %s" - % (w_userId, username), 0) - return w_userId - # Verify the settings - elif s_userId: - log("Returning userId %s from SETTINGS for username %s" - % (w_userId, username), 0) - return s_userId - # No userId found - log("No userId saved. Trying to get Plex to use instead", 0) - plexId = settings('plexid') - if not plexId: - log('Also no Plex ID found in settings', 0) - return '' - log('Using Plex ID %s as userid for username %s' - % (plexId, username), 0) - settings('userId', value=plexId) - return plexId - def getServer(self, prefix=True): settings = utils.settings @@ -135,37 +100,6 @@ class UserClient(threading.Thread): elif not prefix: return server - def getToken(self, username=None, userId=None): - - log = self.logMsg - window = utils.window - settings = utils.settings - - if username is None: - username = self.getUsername() - if userId is None: - userId = self.getUserId() - w_token = window('emby_accessToken%s' % userId) - s_token = settings('accessToken') - - # Verify the window property - if w_token: - if not s_token: - # Save access token if it's missing from settings - settings('accessToken', value=w_token) - log("Returning accessToken from WINDOW for username: %s " - "accessToken: xxxx" % username, 2) - return w_token - # Verify the settings - elif s_token: - log("Returning accessToken from SETTINGS for username: %s " - "accessToken: xxxx" % username, 2) - window('emby_accessToken%s' % userId, value=s_token) - return s_token - else: - log("No token found.", 1) - return "" - def getSSLverify(self): # Verify host certificate settings = utils.settings @@ -245,9 +179,9 @@ class UserClient(threading.Thread): if authenticated is False: self.logMsg('Testing validity of current token', 0) - window('emby_currUser', value=userId) + window('currUserId', value=userId) window('plex_username', value=username) - window('emby_accessToken%s' % userId, value=self.currToken) + window('pms_token', value=self.currToken) res = PlexAPI.PlexAPI().CheckConnection( self.currServer, self.currToken) if res is False: @@ -261,10 +195,14 @@ class UserClient(threading.Thread): return False # Set to windows property - window('emby_currUser', value=userId) + window('currUserId', value=userId) window('plex_username', value=username) - window('emby_accessToken%s' % userId, value=self.currToken) - window('emby_server%s' % userId, value=self.currServer) + # This is the token for the current PMS (might also be '') + window('pms_token', value=self.currToken) + # 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('accessToken')) + window('pms_server', value=self.currServer) window('plex_machineIdentifier', value=self.machineIdentifier) window('plex_servername', value=self.servername) @@ -387,12 +325,18 @@ class UserClient(threading.Thread): def resetClient(self): self.logMsg("Reset UserClient authentication.", 1) + + self.doUtils.stopSession() + settings = utils.settings window = utils.window - window('emby_accessToken%s' % self.currUserId, clear=True) - window('emby_server%s' % self.currUserId, clear=True) - window('emby_currUser', clear=True) + window('pms_token', clear=True) + window('plex_token', clear=True) + window('pms_server', clear=True) + window('plex_machineIdentifier', clear=True) + window('plex_servername', clear=True) + window('currUserId', clear=True) window('plex_username', clear=True) settings('username', value='') @@ -423,17 +367,21 @@ class UserClient(threading.Thread): xbmc.sleep(1000) status = window('emby_serverStatus') - if status: - # Verify the connection status to server - if status == "restricted": - # Parental control is restricting access - self.HasAccess = False - elif status == "401": - # Unauthorized access, revoke token - window('emby_serverStatus', value="Auth") - self.resetClient() - xbmc.sleep(2000) + if status == "Stop": + xbmc.sleep(500) + continue + + # Verify the connection status to server + elif status == "restricted": + # Parental control is restricting access + self.HasAccess = False + + elif status == "401": + # Unauthorized access, revoke token + window('emby_serverStatus', value="Auth") + self.resetClient() + xbmc.sleep(2000) if self.auth and (self.currUser is None): # Try to authenticate user @@ -455,6 +403,7 @@ class UserClient(threading.Thread): server = self.getServer() # The status Stop is for when user cancelled password dialog. + # Or retried too many times if server and status != "Stop": # Only if there's information found to login log("Server found: %s" % server, 2) diff --git a/service.py b/service.py index 0eda6df4..70518944 100644 --- a/service.py +++ b/service.py @@ -75,9 +75,10 @@ class Service(): "emby_online", "emby_serverStatus", "emby_onWake", "emby_syncRunning", "emby_dbCheck", "emby_kodiScan", - "emby_shouldStop", "emby_currUser", "emby_dbScan", "emby_sessionId", + "emby_shouldStop", "currUserId", "emby_dbScan", "emby_sessionId", "emby_initialScan", "emby_customplaylist", "emby_playbackProps", - "plex_runLibScan", "plex_username" + "plex_runLibScan", "plex_username", "pms_token", "plex_token", + "pms_server", "plex_machineIdentifier", "plex_servername" ] for prop in properties: window(prop, clear=True)