From ae34b63de9ed39969b4a92348766cb316e66ceac Mon Sep 17 00:00:00 2001 From: tomkat83 Date: Tue, 24 May 2016 19:00:39 +0200 Subject: [PATCH] Re-wired connection manager --- resources/lib/PlexAPI.py | 203 +++++++++++++++++---------------- resources/lib/PlexFunctions.py | 15 +-- resources/lib/initialsetup.py | 47 ++++++-- 3 files changed, 151 insertions(+), 114 deletions(-) diff --git a/resources/lib/PlexAPI.py b/resources/lib/PlexAPI.py index 6782ce00..581848ed 100644 --- a/resources/lib/PlexAPI.py +++ b/resources/lib/PlexAPI.py @@ -508,24 +508,33 @@ class PlexAPI(): self.g_PMS dict set """ self.g_PMS = {} + # "Searching for Plex Server" xbmcgui.Dialog().notification( heading=self.addonName, message=self.__language__(39055), icon="special://home/addons/plugin.video.plexkodiconnect/icon.png", - time=3000, + time=4000, sound=False) # Look first for local PMS in the LAN pmsList = self.PlexGDM() - self.logMsg('pmslist: %s' % pmsList, 1) + self.logMsg('PMS found in the local LAN via GDM: %s' % pmsList, 2) + + # Get PMS from plex.tv + if plexToken: + self.logMsg('Checking with plex.tv for more PMS to connect to', 1) + self.getPMSListFromMyPlex(plexToken) + else: + self.logMsg('No plex token supplied, only checked LAN for PMS', 1) + for uuid in pmsList: PMS = pmsList[uuid] + if PMS['uuid'] in self.g_PMS: + continue self.declarePMS(PMS['uuid'], PMS['serverName'], 'http', PMS['ip'], PMS['port']) - self.updatePMSProperty(PMS['uuid'], 'owned', '-') # Ping to check whether we need HTTPs or HTTP - url = '%s:%s' % (PMS['ip'], PMS['port']) - https = PMSHttpsEnabled(url) + https = PMSHttpsEnabled('%s:%s' % (PMS['ip'], PMS['port'])) if https is None: # Error contacting url. Skip for now continue @@ -539,10 +548,6 @@ class PlexAPI(): # Already declared with http pass - if not plexToken: - self.logMsg('No plex.tv token supplied, checked LAN for PMS', 0) - return - # install plex.tv "virtual" PMS - for myPlex, PlexHome # self.declarePMS('plex.tv', 'plex.tv', 'https', 'plex.tv', '443') # self.updatePMSProperty('plex.tv', 'local', '-') @@ -551,9 +556,6 @@ class PlexAPI(): # 'plex.tv', 'accesstoken', plexToken) # (remote and local) servers from plex.tv - # Get PMS from plex.tv. This will overwrite any PMS we already found - self.getPMSListFromMyPlex(plexToken) - def getPMSListFromMyPlex(self, token): """ getPMSListFromMyPlex @@ -566,7 +568,7 @@ class PlexAPI(): headerOptions={'X-Plex-Token': token}) try: xml.attrib - except: + except AttributeError: self.logMsg('Could not get list of PMS from plex.tv', -1) return @@ -574,105 +576,110 @@ class PlexAPI(): queue = Queue.Queue() threads = [] + maxAgeSeconds = 2*60*60*24 for Dir in xml.findall('Device'): - if "server" in Dir.get('provides'): - if Dir.find('Connection') is None: - # no valid connection - skip - continue + if 'server' not in Dir.get('provides'): + # No PMS - skip + continue + if Dir.find('Connection') is None: + # no valid connection - skip + continue - # check MyPlex data age - skip if >2 days - PMS = {} - PMS['name'] = Dir.get('name') - infoAge = time.time() - int(Dir.get('lastSeenAt')) - oneDayInSec = 60 * 60 * 24 - if infoAge > 2 * oneDayInSec: - self.logMsg("Server %s not seen for 2 days - " - "skipping." % PMS['name'], 0) - continue + # check MyPlex data age - skip if >2 days + PMS = {} + PMS['name'] = Dir.get('name') + infoAge = time.time() - int(Dir.get('lastSeenAt')) + if infoAge > maxAgeSeconds: + self.logMsg("Server %s not seen for 2 days - skipping." + % PMS['name'], 1) + continue - PMS['uuid'] = Dir.get('clientIdentifier') - PMS['token'] = Dir.get('accessToken', token) - PMS['owned'] = Dir.get('owned', '0') - PMS['local'] = Dir.get('publicAddressMatches') - PMS['ownername'] = Dir.get('sourceTitle', '') - PMS['path'] = '/' - PMS['options'] = None + PMS['uuid'] = Dir.get('clientIdentifier') + PMS['token'] = Dir.get('accessToken', token) + PMS['owned'] = Dir.get('owned', '1') + PMS['local'] = Dir.get('publicAddressMatches') + PMS['ownername'] = Dir.get('sourceTitle', '') + PMS['path'] = '/' + PMS['options'] = None - # If PMS seems (!!) local, try a local connection first - # Backup to remote connection, if that failes - PMS['baseURL'] = '' - for Con in Dir.findall('Connection'): - localConn = Con.get('local') - if ((PMS['local'] == '1' and localConn == '1') or - (PMS['local'] == '0' and localConn == '0')): - # Either both local or both remote - PMS['protocol'] = Con.get('protocol') - PMS['ip'] = Con.get('address') - PMS['port'] = Con.get('port') - PMS['baseURL'] = Con.get('uri') - elif PMS['local'] == '1' and localConn == '0': - # Backup connection if local one did not work - PMS['backup'] = {} - PMS['backup']['protocol'] = Con.get('protocol') - PMS['backup']['ip'] = Con.get('address') - PMS['backup']['port'] = Con.get('port') - PMS['backup']['baseURL'] = Con.get('uri') + # Try a local connection first + # Backup to remote connection, if that failes + PMS['connections'] = [] + for Con in Dir.findall('Connection'): + if Con.get('local') == '1': + PMS['connections'].append(Con) + # Append non-local + for Con in Dir.findall('Connection'): + if Con.get('local') != '1': + PMS['connections'].append(Con) - # poke PMS, own thread for each poke - t = Thread(target=self.pokePMS, - args=(PMS, queue)) - t.start() - threads.append(t) + # poke PMS, own thread for each poke + t = Thread(target=self.pokePMS, + args=(PMS, queue)) + t.start() + threads.append(t) - # wait for requests being answered - for t in threads: - t.join() + # wait for requests being answered + for t in threads: + t.join() - # declare new PMSs - while not queue.empty(): - PMS = queue.get() - self.declarePMS(PMS['uuid'], PMS['name'], - PMS['protocol'], PMS['ip'], PMS['port']) - # dflt: token='', local, owned - updated later - self.updatePMSProperty( - PMS['uuid'], 'accesstoken', PMS['token']) - self.updatePMSProperty( - PMS['uuid'], 'owned', PMS['owned']) - self.updatePMSProperty( - PMS['uuid'], 'local', PMS['local']) - # set in declarePMS, overwrite for https encryption - self.updatePMSProperty( - PMS['uuid'], 'baseURL', PMS['baseURL']) - self.updatePMSProperty( - PMS['uuid'], 'ownername', PMS['ownername']) - queue.task_done() + # declare new PMSs + while not queue.empty(): + PMS = queue.get() + self.declarePMS(PMS['uuid'], PMS['name'], + PMS['protocol'], PMS['ip'], PMS['port']) + # dflt: token='', local, owned - updated later + self.updatePMSProperty( + PMS['uuid'], 'accesstoken', PMS['token']) + self.updatePMSProperty( + PMS['uuid'], 'owned', PMS['owned']) + self.updatePMSProperty( + PMS['uuid'], 'local', PMS['local']) + # set in declarePMS, overwrite for https encryption + self.updatePMSProperty( + PMS['uuid'], 'baseURL', PMS['baseURL']) + self.updatePMSProperty( + PMS['uuid'], 'ownername', PMS['ownername']) + queue.task_done() def pokePMS(self, PMS, queue): - # Ignore SSL certificates for now - xml = self.doUtils(PMS['baseURL'], + if PMS['connections'][0].get('local') == '1': + protocol = PMS['connections'][0].get('protocol') + address = PMS['connections'][0].get('address') + port = PMS['connections'][0].get('port') + url = '%s://%s:%s' % (protocol, address, port) + else: + url = PMS['connections'][0].get('uri') + protocol, address, port = url.split(':') + address = address.replace('/', '') + + xml = self.doUtils('%s/identity' % url, authenticate=False, headerOptions={'X-Plex-Token': PMS['token']}, - verifySSL=False) + verifySSL=False, + timeout=3) try: xml.attrib - except: - # Connection failed - # retry with remote connection if we just tested local one. - if PMS['local'] == '1' and PMS.get('backup'): - self.logMsg('Couldnt talk to local PMS locally.' - 'Trying again remotely.', 0) - PMS['protocol'] = PMS['backup']['protocol'] - PMS['ip'] = PMS['backup']['ip'] - PMS['port'] = PMS['backup']['port'] - PMS['baseURL'] = PMS['backup']['baseURL'] - PMS['local'] = '0' - # Try again - self.pokePMS(PMS, queue) - else: - return + except AttributeError: + # No connection, delete the one we just tested + del PMS['connections'][0] + if len(PMS['connections']) > 0: + # Still got connections left, try them + return self.pokePMS(PMS, queue) + return else: - # Connection successful, process later - queue.put(PMS) + # Connection successful - correct PMS? + if xml.get('machineIdentifier') == PMS['uuid']: + # process later + PMS['baseURL'] = url + PMS['protocol'] = protocol + PMS['ip'] = address + PMS['port'] = port + queue.put(PMS) + return + self.logMsg('Found a PMS at %s, but the expected machineIdentifier of ' + '%s did not match the one we found: %s' + % (url, PMS['uuid'], xml.get('machineIdentifier')), -1) def MyPlexSignIn(self, username, password, options): """ diff --git a/resources/lib/PlexFunctions.py b/resources/lib/PlexFunctions.py index f6ba413f..95af2655 100644 --- a/resources/lib/PlexFunctions.py +++ b/resources/lib/PlexFunctions.py @@ -424,14 +424,14 @@ def PMSHttpsEnabled(url): verifySSL=False) try: res.attrib - except: + except AttributeError: # Might have SSL deactivated. Try with http res = doUtils('http://%s/identity' % url, authenticate=False, verifySSL=False) try: res.attrib - except: + except AttributeError: logMsg(title, "Could not contact PMS %s" % url, -1) return None else: @@ -448,15 +448,16 @@ def GetMachineIdentifier(url): Returns None if something went wrong """ - xml = downloadutils.DownloadUtils().downloadUrl(url + '/identity') + xml = downloadutils.DownloadUtils().downloadUrl('%s/identity' % url, + authenticate=False, + verifySSL=False) try: - xml.attrib - except: + machineIdentifier = xml.attrib['machineIdentifier'] + except (AttributeError, KeyError): logMsg(title, 'Could not get the PMS machineIdentifier for %s' % url, -1) return None - machineIdentifier = xml.attrib.get('machineIdentifier') - logMsg(title, 'Found machineIdentifier %s for %s' + logMsg(title, 'Found machineIdentifier %s for the PMS %s' % (machineIdentifier, url), 1) return machineIdentifier diff --git a/resources/lib/initialsetup.py b/resources/lib/initialsetup.py index 68d01e32..a4dae14e 100644 --- a/resources/lib/initialsetup.py +++ b/resources/lib/initialsetup.py @@ -12,6 +12,7 @@ import downloadutils import userclient import PlexAPI +from PlexFunctions import GetMachineIdentifier ############################################################################### @@ -26,6 +27,11 @@ class InitialSetup(): self.userClient = userclient.UserClient() self.plx = PlexAPI.PlexAPI() + def PlexTvSignIn(self): + """ + Checks existing connection to plex.tv. If not, triggers sign in + """ + def setup(self, forcePlexTV=False, chooseServer=False): """ Initial setup. Run once upon startup. @@ -44,7 +50,7 @@ class InitialSetup(): plexToken = plexdict['plexToken'] plexid = plexdict['plexid'] if plexToken: - self.logMsg('Found plex.tv token in settings', 0) + self.logMsg('Found a plex.tv token in the settings', 0) dialog = xbmcgui.Dialog() @@ -79,11 +85,10 @@ class InitialSetup(): authenticate=False, headerOptions={'X-Plex-Token': plexToken}) try: - xml.attrib - except: + plexLogin = xml.attrib['title'] + except (AttributeError, KeyError): self.logMsg('Failed to update Plex info from plex.tv', -1) else: - plexLogin = xml.attrib.get('title') utils.settings('plexLogin', value=plexLogin) home = 'true' if xml.attrib.get('home') == '1' else 'false' utils.settings('plexhome', value=home) @@ -92,12 +97,36 @@ class InitialSetup(): 'plexHomeSize', value=xml.attrib.get('homeSize', '1')) self.logMsg('Updated Plex info from plex.tv', 0) - # If a Plex server IP has already been set, return. + # If a Plex server IP has already been set + # return only if the right machine identifier is found + getNewPMS = False if server and forcePlexTV is False and chooseServer is False: - self.logMsg("Server is already set.", 0) - self.logMsg("url: %s, Plex machineIdentifier: %s" - % (server, serverid), 0) - return + self.logMsg("PMS is already set: %s. Checking now..." % server, 0) + chk = self.plx.CheckConnection(server, verifySSL=False) + if chk is False: + self.logMsg('Could not reach PMS %s' % server, -1) + getNewPMS = True + if getNewPMS is False and not serverid: + self.logMsg('No PMS machineIdentifier found for %s. Trying to ' + 'get the PMS unique ID' % server, 1) + serverid = GetMachineIdentifier(server) + if serverid is None: + self.logMsg('Could not retrieve machineIdentifier', -1) + getNewPMS = True + else: + utils.settings('plex_machineIdentifier', value=serverid) + elif getNewPMS is False: + tempServerid = GetMachineIdentifier(server) + if tempServerid != serverid: + self.logMsg('The current PMS %s was expected to have a ' + 'unique machineIdentifier of %s. But we got ' + '%s. Pick a new server to be sure' + % (server, serverid, tempServerid), 1) + getNewPMS = True + if getNewPMS is False: + self.logMsg("Using PMS %s with machineIdentifier %s" + % (server, serverid), 0) + return # If not already retrieved myplex info, optionally let user sign in # to plex.tv. This DOES get called on very first install run