From 260fc7adf80af7a51286bf202fc33a281c7b6a1c Mon Sep 17 00:00:00 2001 From: tomkat83 Date: Wed, 6 Apr 2016 16:24:03 +0200 Subject: [PATCH] Rewired download and PMS connection - Look for PMS in the LAN, even if plex.tv is available --- resources/language/English/strings.xml | 4 +- resources/language/German/strings.xml | 4 +- resources/lib/PlexAPI.py | 461 +++++++++---------- resources/lib/PlexFunctions.py | 47 +- resources/lib/downloadutils.py | 510 ++++++++------------- resources/lib/initialsetup.py | 62 +-- resources/lib/plexbmchelper/subscribers.py | 7 +- resources/lib/userclient.py | 35 +- service.py | 2 +- 9 files changed, 468 insertions(+), 664 deletions(-) diff --git a/resources/language/English/strings.xml b/resources/language/English/strings.xml index ad00d419..02e39ff8 100644 --- a/resources/language/English/strings.xml +++ b/resources/language/English/strings.xml @@ -363,7 +363,7 @@ [COLOR red]Partial or full reset of Database and PKC[/COLOR] [COLOR yellow]Cache all images to Kodi texture cache[/COLOR] [COLOR yellow]Sync Emby Theme Media to Kodi[/COLOR] - (local) + local Failed to authenticate. Did you login to plex.tv? [COLOR yellow]Reset PMS and plex.tv connections to re-login[/COLOR] Automatically log into plex.tv on startup @@ -396,6 +396,8 @@ Wait before sync new/changed PMS item [s] Background Sync Do a full library sync every x minutes + remote + Searching for Plex Server diff --git a/resources/language/German/strings.xml b/resources/language/German/strings.xml index d00087d6..f0393e33 100644 --- a/resources/language/German/strings.xml +++ b/resources/language/German/strings.xml @@ -301,7 +301,7 @@ [COLOR red]Datenbank und auf Wunsch PKC zurücksetzen[/COLOR] [COLOR yellow]Alle Plex Bilder in Kodi zwischenspeichern[/COLOR] [COLOR yellow]Plex Themes zu Kodi synchronisieren[/COLOR] - (lokal) + lokal Plex Media Server Authentifizierung fehlgeschlagen. Haben Sie sich bei plex.tv eingeloggt? [COLOR yellow]PMS und plex.tv Verbindungen zurücksetzen für erneuten Login[/COLOR] Automatisch beim Starten bei plex.tv einloggen @@ -334,6 +334,8 @@ Warten bevor neue/geänderte PMS Einträge gesynct werden [s] Hintergrund-Synchronisation Kompletten Scan aller Bibliotheken alle x Minuten durchführen + remote + Suche Plex Server diff --git a/resources/lib/PlexAPI.py b/resources/lib/PlexAPI.py index 96bbe544..0e52015f 100644 --- a/resources/lib/PlexAPI.py +++ b/resources/lib/PlexAPI.py @@ -29,18 +29,12 @@ http://stackoverflow.com/questions/2407126/python-urllib2-basic-auth-problem http://stackoverflow.com/questions/111945/is-there-any-way-to-do-http-put-in-python (and others...) """ -import struct import time import urllib2 -import httplib import socket -import StringIO -import gzip from threading import Thread -import traceback import requests import xml.etree.ElementTree as etree -from uuid import uuid4 import re import json @@ -232,15 +226,15 @@ class PlexAPI(): try: temp_token = xml.find('auth_token').text except: - self.logMsg("Error: Could not find token in plex.tv answer.", -1) + self.logMsg("Could not find token in plex.tv answer.", -1) return False self.logMsg("temp token from plex.tv is: %s" % temp_token, 2) if not temp_token: return False # Use temp token to get the final plex credentials - xml = self.doUtils('https://plex.tv/users/account?X-Plex-Token=%s' - % temp_token, + xml = self.doUtils('https://plex.tv/users/account', authenticate=False, + parameters={'X-Plex-Token': temp_token}, type="GET") return xml @@ -320,66 +314,63 @@ class PlexAPI(): return False return xml - def CheckConnection(self, url, token=None): + def CheckConnection(self, url, token=None, verifySSL=None): """ Checks connection to a Plex server, available at url. Can also be used to check for connection with plex.tv. Will check up to 3x until reply with False + Override SSL to skip the check by setting verifySSL=False + if 'None', SSL will be checked (standard requests setting) + if 'True', SSL settings from file settings are used (False/True) + Input: url URL to Plex server (e.g. https://192.168.1.1:32400) token appropriate token to access server. If None is passed, the current token is used Output: False if server could not be reached or timeout occured - e.g. 200 if connection was successfull + 200 if connection was successfull int or other HTML status codes as received from the server """ # Add '/clients' to URL because then an authentication is necessary # If a plex.tv URL was passed, this does not work. - header = clientinfo.ClientInfo().getXArgsDeviceInfo() - if token: - header['X-Plex-Token'] = token - sslverify = utils.settings('sslverify') - if sslverify == "true": - sslverify = True - else: - sslverify = False - self.logMsg("Checking connection to server %s with sslverify=%s" - % (url, sslverify), 1) - timeout = (3, 10) + headerOptions = None + if token is not None: + headerOptions = {'X-Plex-Token': token} + if verifySSL is True: + verifySSL = None if utils.settings('sslverify') == 'true' \ + else False if 'plex.tv' in url: url = 'https://plex.tv/api/home/users' else: url = url + '/library/onDeck' - # Check up to 3 times before giving up - this sometimes happens when - # PKC was just started + self.logMsg("Checking connection to server %s with verifySSL=%s" + % (url, verifySSL), 1) + # Check up to 3 times before giving up count = 0 while count < 3: + answer = self.doUtils(url, + authenticate=False, + headerOptions=headerOptions, + verifySSL=verifySSL) + if answer is False: + self.logMsg("Could not connect to %s" % url, 0) + count += 1 + xbmc.sleep(500) + continue try: - answer = requests.get(url, - headers={}, - params=header, - verify=sslverify, - timeout=timeout) - except requests.exceptions.ConnectionError as e: - self.logMsg("Server is offline or cannot be reached. Url: %s " - "Header: %s Error message: %s" - % (url, header, e), 0) - count += 1 - xbmc.sleep(1000) - continue - except requests.exceptions.ReadTimeout: - self.logMsg("Server timeout reached for Url %s with header %s" - % (url, header), 0) - count += 1 - xbmc.sleep(1000) - continue + answer.attrib + except: + pass else: - result = answer.status_code - self.logMsg("Result was: %s" % result, 1) - return result - self.logMsg('Failed to connect to %s too many times.' % url, -1) + # Success - we downloaded an xml! + answer = 200 + self.logMsg("Checking connection successfull. Answer: %s" + % answer, 1) + return answer + self.logMsg('Failed to connect to %s too many times. PMS is dead' + % url, 0) return False def GetgPMSKeylist(self): @@ -455,73 +446,45 @@ class PlexAPI(): addon.setSetting('serverlist', serverlist) return - def declarePMS(self, ATV_udid, uuid, name, scheme, ip, port): + def declarePMS(self, uuid, name, scheme, ip, port): """ Plex Media Server handling parameters: - ATV_udid uuid - PMS ID name, scheme, ip, port, type, owned, token """ - # store PMS information in g_PMS database - if ATV_udid not in self.g_PMS: - self.g_PMS[ATV_udid] = {} - address = ip + ':' + port baseURL = scheme+'://'+ip+':'+port - self.g_PMS[ATV_udid][uuid] = { 'name': name, - 'scheme':scheme, 'ip': ip , 'port': port, - 'address': address, - 'baseURL': baseURL, - 'local': '1', - 'owned': '1', - 'accesstoken': '', - 'enableGzip': False - } + self.g_PMS[uuid] = { + 'name': name, + 'scheme': scheme, + 'ip': ip, + 'port': port, + 'address': address, + 'baseURL': baseURL, + 'local': '1', + 'owned': '1', + 'accesstoken': '', + 'enableGzip': False + } - def updatePMSProperty(self, ATV_udid, uuid, tag, value): + def updatePMSProperty(self, uuid, tag, value): # set property element of PMS by UUID - if not ATV_udid in self.g_PMS: - return '' # no server known for this aTV - if not uuid in self.g_PMS[ATV_udid]: - return '' # requested PMS not available - - self.g_PMS[ATV_udid][uuid][tag] = value + try: + self.g_PMS[uuid][tag] = value + except: + self.logMsg('%s has not yet been declared ' % uuid, -1) + return False - def getPMSProperty(self, ATV_udid, uuid, tag): + def getPMSProperty(self, uuid, tag): # get name of PMS by UUID - if not ATV_udid in self.g_PMS: - return '' # no server known for this aTV - if not uuid in self.g_PMS[ATV_udid]: - return '' # requested PMS not available - - return self.g_PMS[ATV_udid][uuid].get(tag, '') - - def getPMSFromAddress(self, ATV_udid, address): - # find PMS by IP, return UUID - if not ATV_udid in self.g_PMS: - return '' # no server known for this aTV - - for uuid in self.g_PMS[ATV_udid]: - if address in self.g_PMS[ATV_udid][uuid].get('address', None): - return uuid - return '' # IP not found - - def getPMSAddress(self, ATV_udid, uuid, data): - # get address of PMS by UUID - if not ATV_udid in data: - return '' # no server known for this aTV - if not uuid in data[ATV_udid]: - return '' # requested PMS not available - return data[ATV_udid][uuid]['ip'] + ':' + data[ATV_udid][uuid]['port'] - - def getPMSCount(self, ATV_udid): - # get count of discovered PMS by UUID - if not ATV_udid in self.g_PMS: - return 0 # no server known for this aTV - - return len(self.g_PMS[ATV_udid]) + try: + answ = self.g_PMS[uuid].get(tag, '') + except: + self.logMsg('%s not found in PMS catalogue' % uuid, -1) + answ = False + return answ def PlexGDM(self): """ @@ -532,25 +495,23 @@ class PlexAPI(): result: PMS_list - dict() of PMSs found """ + import struct + IP_PlexGDM = '239.0.0.250' # multicast to PMS Port_PlexGDM = 32414 Msg_PlexGDM = 'M-SEARCH * HTTP/1.0' - # dprint(__name__, 0, "***") - # dprint(__name__, 0, "PlexGDM - looking up Plex Media Server") - # dprint(__name__, 0, "***") - + # setup socket for discovery -> multicast message GDM = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) - GDM.settimeout(1.0) - - # Set the time-to-live for messages to 1 for local network - ttl = struct.pack('b', 1) + GDM.settimeout(2.0) + + # Set the time-to-live for messages to 2 for local network + ttl = struct.pack('b', 2) GDM.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, ttl) - + returnData = [] try: # Send data to the multicast group - # dprint(__name__, 1, "Sending discovery message: {0}", Msg_PlexGDM) GDM.sendto(Msg_PlexGDM, (IP_PlexGDM, Port_PlexGDM)) # Look for responses from all recipients @@ -559,126 +520,109 @@ class PlexAPI(): data, server = GDM.recvfrom(1024) # dprint(__name__, 1, "Received data from {0}", server) # dprint(__name__, 1, "Data received:\n {0}", data) - returnData.append( { 'from' : server, - 'data' : data } ) + returnData.append({'from': server, + 'data': data}) except socket.timeout: break finally: GDM.close() - discovery_complete = True + pmsList = {} - PMS_list = {} - if returnData: - for response in returnData: - update = { 'ip' : response.get('from')[0] } - - # Check if we had a positive HTTP response - if "200 OK" in response.get('data'): - for each in response.get('data').split('\n'): - # decode response data - update['discovery'] = "auto" - #update['owned']='1' - #update['master']= 1 - #update['role']='master' - - if "Content-Type:" in each: - update['content-type'] = each.split(':')[1].strip() - elif "Resource-Identifier:" in each: - update['uuid'] = each.split(':')[1].strip() - elif "Name:" in each: - update['serverName'] = each.split(':')[1].strip().decode('utf-8', 'replace') # store in utf-8 - elif "Port:" in each: - update['port'] = each.split(':')[1].strip() - elif "Updated-At:" in each: - update['updated'] = each.split(':')[1].strip() - elif "Version:" in each: - update['version'] = each.split(':')[1].strip() - - PMS_list[update['uuid']] = update - - # if PMS_list=={}: - # dprint(__name__, 0, "GDM: No servers discovered") - # else: - # dprint(__name__, 0, "GDM: Servers discovered: {0}", len(PMS_list)) - # for uuid in PMS_list: - # dprint(__name__, 1, "{0} {1}:{2}", PMS_list[uuid]['serverName'], PMS_list[uuid]['ip'], PMS_list[uuid]['port']) - - return PMS_list + self.logMsg('returndata is: %s' % returnData) + for response in returnData: + update = {'ip': response.get('from')[0]} + # Check if we had a positive HTTP response + if "200 OK" in response.get('data'): + for each in response.get('data').split('\n'): + # decode response data + update['discovery'] = "auto" + # update['owned']='1' + # update['master']= 1 + # update['role']='master' - def discoverPMS(self, ATV_udid, CSettings, IP_self, tokenDict={}): + if "Content-Type:" in each: + update['content-type'] = each.split(':')[1].strip() + elif "Resource-Identifier:" in each: + update['uuid'] = each.split(':')[1].strip() + elif "Name:" in each: + update['serverName'] = each.split(':')[1].strip().decode('utf-8', 'replace') + elif "Port:" in each: + update['port'] = each.split(':')[1].strip() + elif "Updated-At:" in each: + update['updated'] = each.split(':')[1].strip() + elif "Version:" in each: + update['version'] = each.split(':')[1].strip() + pmsList[update['uuid']] = update + + return pmsList + + def discoverPMS(self, IP_self, plexToken=None): """ - discoverPMS - parameters: - ATV_udid - CSettings - for manual PMS configuration. this one looks strange. - IP_self + IP_self Own IP optional: - tokenDict - dictionary of tokens for MyPlex, PlexHome + plexToken token for plex.tv result: - self.g_PMS dictionary for ATV_udid + self.g_PMS dict set """ - self.g_PMS[ATV_udid] = {} + self.g_PMS = {} + xbmcgui.Dialog().notification( + heading=self.addonName, + message=self.__language__(39055), + icon="special://home/addons/plugin.video.plexkodiconnect/icon.png", + time=3000, + sound=False) - # install plex.tv "virtual" PMS - for myPlex, PlexHome - self.declarePMS(ATV_udid, 'plex.tv', 'plex.tv', 'https', 'plex.tv', '443') - self.updatePMSProperty(ATV_udid, 'plex.tv', 'local', '-') - self.updatePMSProperty(ATV_udid, 'plex.tv', 'owned', '-') - self.updatePMSProperty(ATV_udid, 'plex.tv', 'accesstoken', tokenDict.get('MyPlexToken', '')) - - if 'PlexHomeToken' in tokenDict: - authtoken = tokenDict.get('PlexHomeToken') - else: - authtoken = tokenDict.get('MyPlexToken', '') - - if authtoken == '': - # not logged into myPlex - # local PMS - # PlexGDM - PMS_list = self.PlexGDM() - for uuid_id in PMS_list: - PMS = PMS_list[uuid_id] - self.declarePMS(ATV_udid, PMS['uuid'], PMS['serverName'], 'http', PMS['ip'], PMS['port']) # dflt: token='', local, owned - 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, {}): + # Look first for local PMS in the LAN + pmsList = self.PlexGDM() + self.logMsg('pmslist: %s' % pmsList, 1) + for uuid in pmsList: + PMS = pmsList[uuid] + 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 = (self.getPMSProperty(ATV_udid, uuid_id, 'ip') + ':' - + self.getPMSProperty(ATV_udid, uuid_id, 'port')) + url = '%s:%s' % (PMS['ip'], PMS['port']) https = PMSHttpsEnabled(url) if https is None: - # Error contacting url + # Error contacting url. Skip for now continue - elif https: - self.updatePMSProperty(ATV_udid, uuid_id, 'scheme', 'https') + elif https is True: + self.updatePMSProperty(PMS['uuid'], 'scheme', 'https') + self.updatePMSProperty( + PMS['uuid'], + 'baseURL', + 'https://%s:%s' % (PMS['ip'], PMS['port'])) 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) \ - and ( - (self.getPMSProperty(ATV_udid, uuid_id, 'local') == '1' - and False) - or - (self.getPMSProperty(ATV_udid, uuid_id, 'local') == '0' - and True) == 'True' - ) - self.updatePMSProperty(ATV_udid, uuid_id, 'enableGzip', enableGzip) + # Already declared with http + pass - def getPMSListFromMyPlex(self, ATV_udid, authtoken): + 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', '-') + # self.updatePMSProperty('plex.tv', 'owned', '-') + # self.updatePMSProperty( + # '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 get Plex media Server List from plex.tv/pms/resources """ - xml = self.doUtils('https://plex.tv/api/resources?includeHttps=1', + xml = self.doUtils('https://plex.tv/api/resources', authenticate=False, - headerOptions={'X-Plex-Token': authtoken}) + parameters={'includeHttps': 1}, + headerOptions={'X-Plex-Token': token}) try: xml.attrib except: @@ -699,36 +643,43 @@ class PlexAPI(): PMS = {} PMS['name'] = Dir.get('name') infoAge = time.time() - int(Dir.get('lastSeenAt')) - oneDayInSec = 2*60*60*24 - if infoAge > 1*oneDayInSec: - self.logMsg("Server %s not seen for 1 day - " + oneDayInSec = 60*60*24 + if infoAge > 2*oneDayInSec: + self.logMsg("Server %s not seen for 2 days - " "skipping." % PMS['name'], 0) continue PMS['uuid'] = Dir.get('clientIdentifier') - PMS['token'] = Dir.get('accessToken', authtoken) + 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 - # flag to set first connection, possibly overwrite later with - # more suitable - PMS['baseURL'] = "" + # If PMS seems (!!) local, try a local connection first + # Backup to remote connection, if that failes + PMS['baseURL'] = '' for Con in Dir.iter(tag='Connection'): - if (PMS['baseURL'] == "" or - Con.get('local') == PMS['local']): + 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('baseURL') - # todo: handle unforeseen - like we get multiple suitable - # connections. how to choose one? + 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') # poke PMS, own thread for each poke t = Thread(target=self.pokePMS, - args=(PMS['baseURL'], PMS['token'], PMS, queue)) + args=(PMS, queue)) t.start() threads.append(t) @@ -739,45 +690,49 @@ class PlexAPI(): # declare new PMSs while not queue.empty(): PMS = queue.get() - self.declarePMS(ATV_udid, PMS['uuid'], PMS['name'], + self.declarePMS(PMS['uuid'], PMS['name'], PMS['protocol'], PMS['ip'], PMS['port']) # dflt: token='', local, owned - updated later self.updatePMSProperty( - ATV_udid, PMS['uuid'], 'accesstoken', PMS['token']) + PMS['uuid'], 'accesstoken', PMS['token']) self.updatePMSProperty( - ATV_udid, PMS['uuid'], 'owned', PMS['owned']) + PMS['uuid'], 'owned', PMS['owned']) self.updatePMSProperty( - ATV_udid, PMS['uuid'], 'local', PMS['local']) + PMS['uuid'], 'local', PMS['local']) # set in declarePMS, overwrite for https encryption self.updatePMSProperty( - ATV_udid, PMS['uuid'], 'baseURL', PMS['baseURL']) + PMS['uuid'], 'baseURL', PMS['baseURL']) self.updatePMSProperty( - ATV_udid, PMS['uuid'], 'ownername', PMS['ownername']) + PMS['uuid'], 'ownername', PMS['ownername']) queue.task_done() - def pokePMS(self, url, token, PMS, queue): - xml = self.doUtils(url, + def pokePMS(self, PMS, queue): + # Ignore SSL certificates for now + xml = self.doUtils(PMS['baseURL'], authenticate=False, - headerOptions={'X-Plex-Token': token}) + headerOptions={'X-Plex-Token': PMS['token']}, + verifySSL=False) try: xml.attrib except: - return + # 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 else: + # Connection successful, process later queue.put(PMS) - def getURL(self, baseURL, path, key): - if key.startswith('http://') or key.startswith('https://'): # external server - URL = key - elif key.startswith('/'): # internal full path. - URL = baseURL + key - elif key == '': # internal path - URL = baseURL + path - else: # internal path, add-on - URL = baseURL + path + '/' + key - - return URL - def MyPlexSignIn(self, username, password, options): """ MyPlex Sign In, Sign Out @@ -1226,13 +1181,12 @@ class PlexAPI(): return path - def returnServerList(self, ATV_udid, data): + def returnServerList(self, data): """ Returns a nicer list of all servers found in data, where data is in g_PMS format, for the client device with unique ID ATV_udid Input: - ATV_udid Unique client ID data e.g. self.g_PMS Output: List of all servers, with an entry of the form: @@ -1247,21 +1201,23 @@ class PlexAPI(): 'machineIdentifier': id, Plex server machine identifier 'accesstoken': token Access token to this server 'baseURL': baseURL scheme://ip:port + 'ownername' Plex username of PMS owner } """ serverlist = [] - for key, value in data[ATV_udid].items(): + for key, value in data.items(): serverlist.append({ - 'name': value['name'], - 'address': value['address'], - 'ip': value['ip'], - 'port': value['port'], - 'scheme': value['scheme'], - 'local': value['local'], - 'owned': value['owned'], + 'name': value.get('name'), + 'address': value.get('address'), + 'ip': value.get('ip'), + 'port': value.get('port'), + 'scheme': value.get('scheme'), + 'local': value.get('local'), + 'owned': value.get('owned'), 'machineIdentifier': key, - 'accesstoken': value['accesstoken'], - 'baseURL': value['baseURL'] + 'accesstoken': value.get('accesstoken'), + 'baseURL': value.get('baseURL'), + 'ownername': value.get('ownername') }) return serverlist @@ -2001,6 +1957,7 @@ class API(): return url # For Direct Streaming or Transcoding + from uuid import uuid4 # Path/key to VIDEO item of xml PMS response is needed, not part path = self.item.attrib['key'] transcodePath = self.server + \ diff --git a/resources/lib/PlexFunctions.py b/resources/lib/PlexFunctions.py index 1c6fed10..db977784 100644 --- a/resources/lib/PlexFunctions.py +++ b/resources/lib/PlexFunctions.py @@ -410,47 +410,38 @@ def getPlexRepeat(kodiRepeat): def PMSHttpsEnabled(url): """ - Returns True if the PMS wants to talk https, False otherwise. None if error - occured, e.g. the connection timed out + Returns True if the PMS can talk https, False otherwise. + None if error occured, e.g. the connection timed out - With with e.g. url=192.168.0.1:32400 (NO http/https) + Call with e.g. url='192.168.0.1:32400' (NO http/https) This is done by GET /identity (returns an error if https is enabled and we are trying to use http) Prefers HTTPS over HTTP """ - # True if https, False if http - answer = True + doUtils = downloadutils.DownloadUtils().downloadUrl + res = doUtils('https://%s/identity' % url, + authenticate=False, + verifySSL=False) try: - # Don't use downloadutils here, otherwise we may get un-authorized! - res = requests.get('https://%s/identity' % url, - headers={}, - verify=False, - timeout=(3, 10)) - # Don't verify SSL since we can connect for sure then! - except requests.exceptions.ConnectionError as e: + res.attrib + except: # Might have SSL deactivated. Try with http + res = doUtils('http://%s/identity' % url, + authenticate=False, + verifySSL=False) try: - res = requests.get('http://%s/identity' % url, - headers={}, - timeout=(3, 10)) - except requests.exceptions.ConnectionError as e: - logMsg(title, "Server is offline or cannot be reached. Url: %s" - ", Error message: %s" % (url, e), -1) - return None - except requests.exceptions.ReadTimeout: - logMsg(title, "Server timeout reached for Url %s" % url, -1) + res.attrib + except: + logMsg(title, "Could not contact PMS %s" % url, -1) return None else: - answer = False - except requests.exceptions.ReadTimeout: - logMsg(title, "Server timeout reached for Url %s" % url, -1) - return None - if res.status_code == requests.codes.ok: - return answer + # Received a valid XML. Server wants to talk HTTP + return False else: - return None + # Received a valid XML. Server wants to talk HTTPS + return True def GetMachineIdentifier(url): diff --git a/resources/lib/downloadutils.py b/resources/lib/downloadutils.py index be34ac3b..c1c6ce30 100644 --- a/resources/lib/downloadutils.py +++ b/resources/lib/downloadutils.py @@ -5,9 +5,7 @@ import requests import xml.etree.ElementTree as etree -import xbmcgui - -import utils +from utils import logging, settings, window import clientinfo ############################################################################### @@ -20,382 +18,258 @@ requests.packages.urllib3.disable_warnings(InsecureRequestWarning) ############################################################################### -@utils.logging +@logging class DownloadUtils(): + """ + Manages any up/downloads with PKC. Careful to initiate correctly + Use startSession() to initiate. + If not initiated, e.g. SSL check will fallback to False + """ # Borg - multiple instances, shared state _shared_state = {} # Requests session - s = None timeout = 30 def __init__(self): self.__dict__ = self._shared_state def setUsername(self, username): - # Reserved for userclient only + """ + Reserved for userclient only + """ self.username = username - self.logMsg("Set username: %s" % username, 2) + self.logMsg("Set username: %s" % username, 0) def setUserId(self, userId): - # Reserved for userclient only + """ + Reserved for userclient only + """ self.userId = userId - self.logMsg("Set userId: %s" % userId, 2) + self.logMsg("Set userId: %s" % userId, 0) def setServer(self, server): - # Reserved for userclient only + """ + Reserved for userclient only + """ self.server = server - self.logMsg("Set server: %s" % server, 2) + self.logMsg("Set server: %s" % server, 0) def setToken(self, token): - # Reserved for userclient only + """ + Reserved for userclient only + """ self.token = token - self.logMsg("Set token: xxxxxxx", 2) - - def setSSL(self, ssl, sslclient): - # Reserved for userclient only - self.sslverify = ssl - self.sslclient = sslclient - self.logMsg("Verify SSL host certificate: %s" % ssl, 2) - self.logMsg("SSL client side certificate: %s" % sslclient, 2) - - def postCapabilities(self, deviceId): - - # Post settings to session - url = "{server}/emby/Sessions/Capabilities/Full?format=json" - data = { - - 'PlayableMediaTypes': "Audio,Video", - 'SupportsMediaControl': True, - 'SupportedCommands': ( - - "MoveUp,MoveDown,MoveLeft,MoveRight,Select," - "Back,ToggleContextMenu,ToggleFullscreen,ToggleOsdMenu," - "GoHome,PageUp,NextLetter,GoToSearch," - "GoToSettings,PageDown,PreviousLetter,TakeScreenshot," - "VolumeUp,VolumeDown,ToggleMute,SendString,DisplayMessage," - "SetAudioStreamIndex,SetSubtitleStreamIndex," - - "Mute,Unmute,SetVolume," - "Play,Playstate,PlayNext" - ) - } - - self.logMsg("Capabilities URL: %s" % url, 2) - self.logMsg("Postdata: %s" % data, 2) - - self.downloadUrl(url, postBody=data, type="POST") - self.logMsg("Posted capabilities to %s" % self.server, 2) - - # Attempt at getting sessionId - url = "{server}/emby/Sessions?DeviceId=%s&format=json" % deviceId - result = self.downloadUrl(url) - try: - sessionId = result[0]['Id'] - - except (KeyError, TypeError): - self.logMsg("Failed to retrieve sessionId.", 1) - + if token == '': + self.logMsg('Set token: empty token!', 0) else: - self.logMsg("Session: %s" % result, 2) - self.logMsg("SessionId: %s" % sessionId, 1) - utils.window('emby_sessionId', value=sessionId) - - # Post any permanent additional users - # additionalUsers = utils.settings('additionalUsers') - # if additionalUsers: - - # additionalUsers = additionalUsers.split(',') - # self.logMsg( - # "List of permanent users added to the session: %s" - # % additionalUsers, 1) + self.logMsg("Set token: xxxxxxx", 0) - # # Get the user list from server to get the userId - # url = "{server}/emby/Users?format=json" - # result = self.downloadUrl(url) + def setSSL(self, verifySSL=None, certificate=None): + """ + Reserved for userclient only - # for additional in additionalUsers: - # addUser = additional.decode('utf-8').lower() + verifySSL must be 'true' to enable certificate validation - # # Compare to server users to list of permanent additional users - # for user in result: - # username = user['Name'].lower() - - # if username in addUser: - # userId = user['Id'] - # url = ( - # "{server}/emby/Sessions/%s/Users/%s?format=json" - # % (sessionId, userId) - # ) - # self.downloadUrl(url, postBody={}, type="POST") + certificate must be path to certificate or 'None' + """ + if verifySSL is None: + verifySSL = settings('sslverify') + if certificate is None: + certificate = settings('sslcert') + self.logMsg("Verify SSL certificates set to: %s" % verifySSL, 0) + self.logMsg("SSL client side certificate set to: %s" % certificate, 0) + if verifySSL != 'true': + self.s.verify = False + if certificate != 'None': + self.s.cert = certificate def startSession(self): - # User should be authenticated when this method is called - client = clientinfo.ClientInfo() - - self.deviceId = client.getDeviceId() - verify = False - - # If user enabled host certificate verification - try: - verify = self.sslverify - if self.sslclient is not None: - verify = self.sslclient - except: - self.logMsg("Could not load SSL settings.", -1) + """ + User should be authenticated when this method is called (via + userclient) + """ # Start session self.s = requests.Session() + + client = clientinfo.ClientInfo() + self.deviceId = client.getDeviceId() # Attach authenticated header to the session self.s.headers = client.getXArgsDeviceInfo() - self.s.verify = verify + self.s.encoding = 'utf-8' + # Set SSL settings + self.setSSL() + + # Set other stuff + self.setServer(window('pms_server')) + self.setToken(window('pms_token')) + self.setUserId(window('currUserId')) + self.setUsername(window('plex_username')) + # Retry connections to the server self.s.mount("http://", requests.adapters.HTTPAdapter(max_retries=1)) self.s.mount("https://", requests.adapters.HTTPAdapter(max_retries=1)) - self.logMsg("Requests session started on: %s" % self.server, 1) + self.logMsg("Requests session started on: %s" % self.server, 0) def stopSession(self): try: self.s.close() except: - self.logMsg("Requests session could not be terminated.", 1) + self.logMsg("Requests session could not be terminated.", 0) + try: + del self.s + except: + pass + self.logMsg('Request session stopped', 0) def getHeader(self, options=None): - try: - header = self.s.headers.copy() - except: - header = clientinfo.ClientInfo().getXArgsDeviceInfo() + header = clientinfo.ClientInfo().getXArgsDeviceInfo() if options is not None: header.update(options) return header - def downloadUrl(self, url, postBody=None, type="GET", parameters=None, - authenticate=True, headerOptions=None): - timeout = self.timeout - default_link = "" + def __doDownload(self, s, type, **kwargs): + if type == "GET": + r = s.get(**kwargs) + elif type == "POST": + r = s.post(**kwargs) + elif type == "DELETE": + r = s.delete(**kwargs) + elif type == "OPTIONS": + r = s.options(**kwargs) + elif type == "PUT": + r = s.put(**kwargs) + return r + def downloadUrl(self, url, type="GET", postBody=None, parameters=None, + authenticate=True, headerOptions=None, verifySSL=True): + """ + Override SSL check with verifySSL=False + + If authenticate=True, existing request session will be used/started + Otherwise, 'empty' request will be made + + Returns: + False If an error occured + True If connection worked but no body was received + 401, ... integer if PMS answered with HTTP error 401 + (unauthorized) or other http error codes + xml xml etree root object, if applicable + JSON json() object, if applicable + """ + kwargs = {} + if authenticate: + # Get requests session + try: + s = self.s + except AttributeError: + self.logMsg("Request session does not exist: start one", 0) + self.startSession() + s = self.s + # Replace for the real values + url = url.replace("{server}", self.server) + else: + # User is not (yet) authenticated. Used to communicate with + # plex.tv and to check for PMS servers + s = requests + headerOptions = self.getHeader(options=headerOptions) + kwargs['timeout'] = self.timeout + if settings('sslcert') != 'None': + kwargs['cert'] = settings('sslcert') + + # Set the variables we were passed (fallback to request session + # otherwise - faster) + kwargs['url'] = url + if verifySSL is False: + kwargs['verify'] = False + if headerOptions is not None: + kwargs['headers'] = headerOptions + if postBody is not None: + kwargs['data'] = postBody + if parameters is not None: + kwargs['params'] = parameters + + # ACTUAL DOWNLOAD HAPPENING HERE try: - # If user is authenticated - if (authenticate): - # Get requests session - try: - s = self.s - # Replace for the real values - url = url.replace("{server}", self.server) - url = url.replace("{UserId}", self.userId) - header = self.getHeader(options=headerOptions) - # Prepare request - if type == "GET": - r = s.get(url, json=postBody, params=parameters, timeout=timeout, headers=header) - elif type == "POST": - r = s.post(url, json=postBody, timeout=timeout, headers=header) - elif type == "DELETE": - r = s.delete(url, json=postBody, timeout=timeout, headers=header) - elif type == "OPTIONS": - r = s.options(url, json=postBody, timeout=timeout, headers=header) - # For Plex Companion - elif type == "POSTXML": - r = s.post(url, postBody, timeout=timeout, headers=header) - elif type == "PUT": - r = s.put(url, timeout=timeout, headers=header) - - except AttributeError: - # request session does not exists - self.logMsg("Request session does not exist: start one", 1) - # Get user information - 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 - - # IF user enables ssl verification - if utils.settings('sslverify') == "true": - verifyssl = True - if utils.settings('sslcert') != "None": - verifyssl = utils.settings('sslcert') - - # Replace for the real values - url = url.replace("{server}", self.server) - url = url.replace("{UserId}", self.userId) - - # Prepare request - if type == "GET": - r = requests.get(url, - json=postBody, - params=parameters, - headers=header, - timeout=timeout, - verify=verifyssl) - - elif type == "POST": - r = requests.post(url, - json=postBody, - headers=header, - timeout=timeout, - verify=verifyssl) - - elif type == "DELETE": - r = requests.delete(url, - json=postBody, - headers=header, - timeout=timeout, - verify=verifyssl) - - elif type == "OPTIONS": - r = requests.options(url, - json=postBody, - headers=header, - timeout=timeout, - cert=cert, - verify=verifyssl) - - elif type == "PUT": - r = requests.put(url, - json=postBody, - headers=header, - timeout=timeout, - cert=cert, - verify=verifyssl) - # If user is not authenticated - elif not authenticate: - - header = self.getHeader(options=headerOptions) - - # If user enables ssl verification - try: - verifyssl = self.sslverify - if self.sslclient is not None: - verifyssl = self.sslclient - except AttributeError: - if utils.settings('sslverify') == "true": - verifyssl = True - else: - verifyssl = False - self.logMsg("Set SSL verification to: %s" % verifyssl, 2) - # Prepare request - if type == "GET": - r = requests.get(url, - json=postBody, - params=parameters, - headers=header, - timeout=timeout, - verify=verifyssl) - - elif type == "POST": - r = requests.post(url, - json=postBody, - headers=header, - timeout=timeout, - verify=verifyssl) - - elif type == "PUT": - r = requests.put(url, - json=postBody, - headers=header, - timeout=timeout, - verify=verifyssl) - ##### THE RESPONSE ##### - # self.logMsg(r.url, 2) - if r.status_code == 204: - # No body in the response - # self.logMsg("====== 204 Success ======", 2) - pass - - elif r.status_code == requests.codes.ok: - - try: - # Allow for xml responses - r = etree.fromstring(r.content) - # self.logMsg("====== 200 Success ======", 2) - # self.logMsg("Received an XML response for: %s" % url, 2) - - return r - - except: - try: - # UNICODE - JSON object - r = r.json() - # self.logMsg("====== 200 Success ======", 2) - # self.logMsg("Response: %s" % r, 2) - return r - except: - try: - if r.text == '' and r.status_code == 200: - # self.logMsg("====== 200 Success ======", 2) - # self.logMsg("Answer from PMS does not contain a body", 2) - pass - # self.logMsg("Unable to convert the response for: %s" % url, 2) - # self.logMsg("Content-type was: %s" % r.headers['content-type'], 2) - except: - self.logMsg("Unable to convert the response for: %s" % url, 2) - self.logMsg("Content-type was: %s" % r.headers['content-type'], 2) - else: - r.raise_for_status() - - ##### EXCEPTIONS ##### + r = self.__doDownload(s, type, **kwargs) + # THE EXCEPTIONS except requests.exceptions.ConnectionError as e: + # Connection error + self.logMsg("Server unreachable at: %s" % url, -1) + self.logMsg(e, 2) # Make the addon aware of status - if utils.window('emby_online') != "false": - self.logMsg("Server unreachable at: %s" % url, -1) - self.logMsg(e, 2) - utils.window('emby_online', value="false") + window('emby_online', value="false") + return False except requests.exceptions.ConnectTimeout as e: - self.logMsg("Server timeout at: %s" % url, 0) - self.logMsg(e, 1) + self.logMsg("Server timeout at: %s" % url, -1) + self.logMsg(e, 2) + return False except requests.exceptions.HTTPError as e: - - if r.status_code == 401: + r = r.status_code + if r == 401: # Unauthorized - status = utils.window('emby_serverStatus') - - if 'X-Application-Error-Code' in r.headers: - # 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, - message="Access restricted.", - icon=xbmcgui.NOTIFICATION_ERROR, - time=5000) - return False - - elif r.headers['X-Application-Error-Code'] == "UnauthorizedAccessException": - # User tried to do something his emby account doesn't allow - pass - - elif status not in ("401", "Auth"): - # Tell userclient token has been revoked. - self.logMsg('Error 401 contacting %s' % url, 0) - self.logMsg('Setting emby_serverStatus to 401', 0) - utils.window('emby_serverStatus', value="401") - self.logMsg("HTTP Error: %s" % e, 0) - xbmcgui.Dialog().notification( - heading=self.addonName, - message="Error connecting: Unauthorized.", - icon=xbmcgui.NOTIFICATION_ERROR) - return 401 - - elif r.status_code in (301, 302): + self.logMsg('Error 401 contacting %s' % url, -1) + elif r in (301, 302): # Redirects - pass - elif r.status_code == 400: + self.logMsg('HTTP redirect error %s at %s' % (r, url), -1) + elif r == 400: # Bad requests - pass + self.logMsg('Bad request at %s' % url, -1) + else: + self.logMsg('HTTP Error %s at %s' % (r, url), -1) + self.logMsg(e, 2) + return r except requests.exceptions.SSLError as e: - self.logMsg("Invalid SSL certificate for: %s" % url, 0) - self.logMsg(e, 1) + self.logMsg("Invalid SSL certificate for: %s" % url, -1) + self.logMsg(e, 2) + return False except requests.exceptions.RequestException as e: - self.logMsg("Unknown error connecting to: %s" % url, 0) - self.logMsg(e, 1) + self.logMsg("Unknown error connecting to: %s" % url, -1) + self.logMsg("Error message: %s" % e, 2) + return False - return default_link + except: + self.logMsg('Unknown requests error', -1) + import traceback + self.logMsg(traceback.format_exc(), 0) + return False + + # THE RESPONSE ##### + if r.status_code == 204: + # No body in the response + return True + + elif r.status_code in (200, 201): + # 200: OK + # 201: Created + try: + # xml response + r = etree.fromstring(r.content) + return r + except: + r.encoding = 'utf-8' + if r.text == '': + # Answer does not contain a body (even though it should) + return True + try: + # UNICODE - JSON object + r = r.json() + return r + except: + self.logMsg("Unable to convert the response for: %s" + % url, -1) + self.logMsg("Received headers were: %s" % r.headers, -1) + return False + else: + self.logMsg('Unknown answer from PMS %s with status code %s. ' + 'Message:' % (url, r.status_code), -1) + r.encoding = 'utf-8' + self.logMsg(r.text, -1) + return True diff --git a/resources/lib/initialsetup.py b/resources/lib/initialsetup.py index 224b2bf5..c592f6c4 100644 --- a/resources/lib/initialsetup.py +++ b/resources/lib/initialsetup.py @@ -36,7 +36,6 @@ class InitialSetup(): # SERVER INFO ##### self.logMsg("Initial setup called.", 0) server = self.userClient.getServer() - clientId = self.clientInfo.getDeviceId() serverid = utils.settings('plex_machineIdentifier') # Get Plex credentials from settings file, if they exist plexdict = self.plx.GetPlexLoginFromSettings() @@ -54,8 +53,15 @@ class InitialSetup(): if (plexToken and myplexlogin == 'true' and forcePlexTV is False and chooseServer is False): chk = self.plx.CheckConnection('plex.tv', plexToken) + try: + chk.attrib + except: + pass + else: + # Success - we downloaded an xml! + chk = 200 # HTTP Error: unauthorized. Token is no longer valid - if chk == 401 or chk == 403: + if chk in (401, 403): self.logMsg('plex.tv connection returned HTTP %s' % chk, 0) # Delete token in the settings utils.settings('plexToken', value='') @@ -113,18 +119,13 @@ class InitialSetup(): httpsUpdated = False while True: if httpsUpdated is False: - tokenDict = {'MyPlexToken': plexToken} if plexToken else {} # Populate g_PMS variable with the found Plex servers - self.plx.discoverPMS(clientId, - None, - xbmc.getIPAddress(), - tokenDict=tokenDict) - self.logMsg("Result of setting g_PMS variable: %s" - % self.plx.g_PMS, 1) + self.plx.discoverPMS(xbmc.getIPAddress(), + plexToken=plexToken) isconnected = False - serverlist = self.plx.returnServerList(clientId, - self.plx.g_PMS) - self.logMsg('PMS serverlist: %s' % serverlist) + self.logMsg('g_PMS: %s' % self.plx.g_PMS, 1) + serverlist = self.plx.returnServerList(self.plx.g_PMS) + self.logMsg('PMS serverlist: %s' % serverlist, 2) # Let user pick server from a list # Get a nicer list dialoglist = [] @@ -138,11 +139,20 @@ class InitialSetup(): for server in serverlist: if server['local'] == '1': # server is in the same network as client. Add "local" - dialoglist.append( - server['name'] - + string(39022)) + msg = string(39022) else: - dialoglist.append(server['name']) + # Add 'remote' + msg = string(39054) + if server.get('ownername'): + # Display username if its not our PMS + dialoglist.append('%s (%s, %s)' + % (server['name'], + server['ownername'], + msg)) + else: + dialoglist.append('%s (%s)' + % (server['name'], + msg)) resp = dialog.select(string(39012), dialoglist) server = serverlist[resp] activeServer = server['machineIdentifier'] @@ -154,15 +164,20 @@ class InitialSetup(): else: url = server['baseURL'] # Deactive SSL verification if the server is local! + # Watch out - settings is cached by Kodi - use dedicated var! if server['local'] == '1': utils.settings('sslverify', 'false') self.logMsg("Setting SSL verify to false, because server is " "local", 1) + verifySSL = False else: utils.settings('sslverify', 'true') self.logMsg("Setting SSL verify to true, because server is " "not local", 1) - chk = self.plx.CheckConnection(url, server['accesstoken']) + verifySSL = None + chk = self.plx.CheckConnection(url, + server['accesstoken'], + verifySSL=verifySSL) if chk == 504 and httpsUpdated is False: # Not able to use HTTP, try HTTPs for now serverlist[resp]['scheme'] = 'https' @@ -221,19 +236,6 @@ class InitialSetup(): % (activeServer, server['ip'], server['port'], server['scheme']), 0) - # ADDITIONAL PROMPTS ##### - # directPaths = dialog.yesno( - # heading="%s: Playback Mode" % self.addonName, - # line1=( - # "Caution! If you choose Native mode, you " - # "will probably lose access to certain Plex " - # "features."), - # nolabel="Addon (Default)", - # yeslabel="Native (Direct Paths)") - # if directPaths: - # self.logMsg("User opted to use direct paths.", 1) - # utils.settings('useDirectPaths', value="1") - if forcePlexTV is True or chooseServer is True: return diff --git a/resources/lib/plexbmchelper/subscribers.py b/resources/lib/plexbmchelper/subscribers.py index b742a21d..9c4caf10 100644 --- a/resources/lib/plexbmchelper/subscribers.py +++ b/resources/lib/plexbmchelper/subscribers.py @@ -278,9 +278,8 @@ class Subscriber: Threaded POST request, because they stall due to PMS response missing the Content-Length header :-( """ - response = self.download.downloadUrl( - url, - postBody=msg, - type="POSTXML") + response = self.download.downloadUrl(url, + postBody=msg, + type="POST") if response in [False, None, 401]: self.subMgr.removeSubscriber(self.uuid) diff --git a/resources/lib/userclient.py b/resources/lib/userclient.py index 842bcf87..0f520354 100644 --- a/resources/lib/userclient.py +++ b/resources/lib/userclient.py @@ -106,29 +106,12 @@ class UserClient(threading.Thread): def getSSLverify(self): # Verify host certificate - settings = utils.settings - - s_sslverify = settings('sslverify') - if settings('altip') == "true": - s_sslverify = settings('secondsslverify') - - if s_sslverify == "true": - return True - else: - return False + return None if utils.settings('sslverify') == 'true' else False def getSSL(self): # Client side certificate - settings = utils.settings - - s_cert = settings('sslcert') - if settings('altip') == "true": - s_cert = settings('secondsslcert') - - if s_cert == "None": - return None - else: - return s_cert + return None if utils.settings('sslcert') == 'None' \ + else utils.settings('sslcert') def setUserPref(self): self.logMsg('Setting user preferences', 0) @@ -183,8 +166,9 @@ class UserClient(threading.Thread): if authenticated is False: self.logMsg('Testing validity of current token', 0) - res = PlexAPI.PlexAPI().CheckConnection( - self.currServer, self.currToken) + res = PlexAPI.PlexAPI().CheckConnection(self.currServer, + token=self.currToken, + verifySSL=self.ssl) if res is False: self.logMsg('Answer from PMS is not as expected. Retrying', -1) return False @@ -227,13 +211,6 @@ class UserClient(threading.Thread): window('remapSMB%sOrg' % item, value=org) window('remapSMB%sNew' % item, value=new) - # Set DownloadUtils values - doUtils.setUsername(username) - doUtils.setUserId(self.currUserId) - doUtils.setServer(self.currServer) - doUtils.setToken(self.currToken) - doUtils.setSSL(self.ssl, self.sslcert) - # Start DownloadUtils session doUtils.startSession() # self.getAdditionalUsers() diff --git a/service.py b/service.py index 6749450e..754037fa 100644 --- a/service.py +++ b/service.py @@ -233,7 +233,7 @@ class Service(): if server is False: # No server info set in add-on settings pass - elif plx.CheckConnection(server) is False: + elif plx.CheckConnection(server, verifySSL=True) is False: # Server is offline or cannot be reached # Alert the user and suppress future warning if self.server_online: