Rewired download and PMS connection

- Look for PMS in the LAN, even if plex.tv is available
This commit is contained in:
tomkat83 2016-04-06 16:24:03 +02:00
parent 8bad79413c
commit 260fc7adf8
9 changed files with 468 additions and 664 deletions

View file

@ -363,7 +363,7 @@
<string id="39019">[COLOR red]Partial or full reset of Database and PKC[/COLOR]</string> <string id="39019">[COLOR red]Partial or full reset of Database and PKC[/COLOR]</string>
<string id="39020">[COLOR yellow]Cache all images to Kodi texture cache[/COLOR]</string> <string id="39020">[COLOR yellow]Cache all images to Kodi texture cache[/COLOR]</string>
<string id="39021">[COLOR yellow]Sync Emby Theme Media to Kodi[/COLOR]</string> <string id="39021">[COLOR yellow]Sync Emby Theme Media to Kodi[/COLOR]</string>
<string id="39022"> (local)</string> <string id="39022">local</string>
<string id="39023">Failed to authenticate. Did you login to plex.tv?</string> <string id="39023">Failed to authenticate. Did you login to plex.tv?</string>
<string id="39024">[COLOR yellow]Reset PMS and plex.tv connections to re-login[/COLOR]</string> <string id="39024">[COLOR yellow]Reset PMS and plex.tv connections to re-login[/COLOR]</string>
<string id="39025">Automatically log into plex.tv on startup</string> <string id="39025">Automatically log into plex.tv on startup</string>
@ -396,6 +396,8 @@
<string id="39051">Wait before sync new/changed PMS item [s]</string> <string id="39051">Wait before sync new/changed PMS item [s]</string>
<string id="39052">Background Sync</string> <string id="39052">Background Sync</string>
<string id="39053">Do a full library sync every x minutes</string> <string id="39053">Do a full library sync every x minutes</string>
<string id="39054">remote</string>
<string id="39055">Searching for Plex Server</string>

View file

@ -301,7 +301,7 @@
<string id="39019">[COLOR red]Datenbank und auf Wunsch PKC zurücksetzen[/COLOR]</string> <string id="39019">[COLOR red]Datenbank und auf Wunsch PKC zurücksetzen[/COLOR]</string>
<string id="39020">[COLOR yellow]Alle Plex Bilder in Kodi zwischenspeichern[/COLOR]</string> <string id="39020">[COLOR yellow]Alle Plex Bilder in Kodi zwischenspeichern[/COLOR]</string>
<string id="39021">[COLOR yellow]Plex Themes zu Kodi synchronisieren[/COLOR]</string> <string id="39021">[COLOR yellow]Plex Themes zu Kodi synchronisieren[/COLOR]</string>
<string id="39022"> (lokal)</string> <string id="39022">lokal</string>
<string id="39023">Plex Media Server Authentifizierung fehlgeschlagen. Haben Sie sich bei plex.tv eingeloggt?</string> <string id="39023">Plex Media Server Authentifizierung fehlgeschlagen. Haben Sie sich bei plex.tv eingeloggt?</string>
<string id="39024">[COLOR yellow]PMS und plex.tv Verbindungen zurücksetzen für erneuten Login[/COLOR]</string> <string id="39024">[COLOR yellow]PMS und plex.tv Verbindungen zurücksetzen für erneuten Login[/COLOR]</string>
<string id="39025">Automatisch beim Starten bei plex.tv einloggen</string> <string id="39025">Automatisch beim Starten bei plex.tv einloggen</string>
@ -334,6 +334,8 @@
<string id="39051">Warten bevor neue/geänderte PMS Einträge gesynct werden [s]</string> <string id="39051">Warten bevor neue/geänderte PMS Einträge gesynct werden [s]</string>
<string id="39052">Hintergrund-Synchronisation</string> <string id="39052">Hintergrund-Synchronisation</string>
<string id="39053">Kompletten Scan aller Bibliotheken alle x Minuten durchführen</string> <string id="39053">Kompletten Scan aller Bibliotheken alle x Minuten durchführen</string>
<string id="39054">remote</string>
<string id="39055">Suche Plex Server</string>
<!-- Plex Entrypoint.py --> <!-- Plex Entrypoint.py -->

View file

@ -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 http://stackoverflow.com/questions/111945/is-there-any-way-to-do-http-put-in-python
(and others...) (and others...)
""" """
import struct
import time import time
import urllib2 import urllib2
import httplib
import socket import socket
import StringIO
import gzip
from threading import Thread from threading import Thread
import traceback
import requests import requests
import xml.etree.ElementTree as etree import xml.etree.ElementTree as etree
from uuid import uuid4
import re import re
import json import json
@ -232,15 +226,15 @@ class PlexAPI():
try: try:
temp_token = xml.find('auth_token').text temp_token = xml.find('auth_token').text
except: 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 return False
self.logMsg("temp token from plex.tv is: %s" % temp_token, 2) self.logMsg("temp token from plex.tv is: %s" % temp_token, 2)
if not temp_token: if not temp_token:
return False return False
# Use temp token to get the final plex credentials # Use temp token to get the final plex credentials
xml = self.doUtils('https://plex.tv/users/account?X-Plex-Token=%s' xml = self.doUtils('https://plex.tv/users/account',
% temp_token,
authenticate=False, authenticate=False,
parameters={'X-Plex-Token': temp_token},
type="GET") type="GET")
return xml return xml
@ -320,66 +314,63 @@ class PlexAPI():
return False return False
return xml 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 Checks connection to a Plex server, available at url. Can also be used
to check for connection with plex.tv. to check for connection with plex.tv.
Will check up to 3x until reply with False 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: Input:
url URL to Plex server (e.g. https://192.168.1.1:32400) url URL to Plex server (e.g. https://192.168.1.1:32400)
token appropriate token to access server. If None is passed, token appropriate token to access server. If None is passed,
the current token is used the current token is used
Output: Output:
False if server could not be reached or timeout occured 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 int or other HTML status codes as received from the server
""" """
# Add '/clients' to URL because then an authentication is necessary # Add '/clients' to URL because then an authentication is necessary
# If a plex.tv URL was passed, this does not work. # If a plex.tv URL was passed, this does not work.
header = clientinfo.ClientInfo().getXArgsDeviceInfo() headerOptions = None
if token: if token is not None:
header['X-Plex-Token'] = token headerOptions = {'X-Plex-Token': token}
sslverify = utils.settings('sslverify') if verifySSL is True:
if sslverify == "true": verifySSL = None if utils.settings('sslverify') == 'true' \
sslverify = True else False
else:
sslverify = False
self.logMsg("Checking connection to server %s with sslverify=%s"
% (url, sslverify), 1)
timeout = (3, 10)
if 'plex.tv' in url: if 'plex.tv' in url:
url = 'https://plex.tv/api/home/users' url = 'https://plex.tv/api/home/users'
else: else:
url = url + '/library/onDeck' url = url + '/library/onDeck'
# Check up to 3 times before giving up - this sometimes happens when self.logMsg("Checking connection to server %s with verifySSL=%s"
# PKC was just started % (url, verifySSL), 1)
# Check up to 3 times before giving up
count = 0 count = 0
while count < 3: 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: try:
answer = requests.get(url, answer.attrib
headers={}, except:
params=header, pass
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
else: else:
result = answer.status_code # Success - we downloaded an xml!
self.logMsg("Result was: %s" % result, 1) answer = 200
return result self.logMsg("Checking connection successfull. Answer: %s"
self.logMsg('Failed to connect to %s too many times.' % url, -1) % answer, 1)
return answer
self.logMsg('Failed to connect to %s too many times. PMS is dead'
% url, 0)
return False return False
def GetgPMSKeylist(self): def GetgPMSKeylist(self):
@ -455,23 +446,21 @@ class PlexAPI():
addon.setSetting('serverlist', serverlist) addon.setSetting('serverlist', serverlist)
return return
def declarePMS(self, ATV_udid, uuid, name, scheme, ip, port): def declarePMS(self, uuid, name, scheme, ip, port):
""" """
Plex Media Server handling Plex Media Server handling
parameters: parameters:
ATV_udid
uuid - PMS ID uuid - PMS ID
name, scheme, ip, port, type, owned, token 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 address = ip + ':' + port
baseURL = scheme+'://'+ip+':'+port baseURL = scheme+'://'+ip+':'+port
self.g_PMS[ATV_udid][uuid] = { 'name': name, self.g_PMS[uuid] = {
'scheme':scheme, 'ip': ip , 'port': port, 'name': name,
'scheme': scheme,
'ip': ip,
'port': port,
'address': address, 'address': address,
'baseURL': baseURL, 'baseURL': baseURL,
'local': '1', 'local': '1',
@ -480,48 +469,22 @@ class PlexAPI():
'enableGzip': False 'enableGzip': False
} }
def updatePMSProperty(self, ATV_udid, uuid, tag, value): def updatePMSProperty(self, uuid, tag, value):
# set property element of PMS by UUID # set property element of PMS by UUID
if not ATV_udid in self.g_PMS: try:
return '' # no server known for this aTV self.g_PMS[uuid][tag] = value
if not uuid in self.g_PMS[ATV_udid]: except:
return '' # requested PMS not available self.logMsg('%s has not yet been declared ' % uuid, -1)
return False
self.g_PMS[ATV_udid][uuid][tag] = value def getPMSProperty(self, uuid, tag):
def getPMSProperty(self, ATV_udid, uuid, tag):
# get name of PMS by UUID # get name of PMS by UUID
if not ATV_udid in self.g_PMS: try:
return '' # no server known for this aTV answ = self.g_PMS[uuid].get(tag, '')
if not uuid in self.g_PMS[ATV_udid]: except:
return '' # requested PMS not available self.logMsg('%s not found in PMS catalogue' % uuid, -1)
answ = False
return self.g_PMS[ATV_udid][uuid].get(tag, '') return answ
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])
def PlexGDM(self): def PlexGDM(self):
""" """
@ -532,25 +495,23 @@ class PlexAPI():
result: result:
PMS_list - dict() of PMSs found PMS_list - dict() of PMSs found
""" """
import struct
IP_PlexGDM = '239.0.0.250' # multicast to PMS IP_PlexGDM = '239.0.0.250' # multicast to PMS
Port_PlexGDM = 32414 Port_PlexGDM = 32414
Msg_PlexGDM = 'M-SEARCH * HTTP/1.0' 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 # setup socket for discovery -> multicast message
GDM = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) GDM = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
GDM.settimeout(1.0) GDM.settimeout(2.0)
# Set the time-to-live for messages to 1 for local network # Set the time-to-live for messages to 2 for local network
ttl = struct.pack('b', 1) ttl = struct.pack('b', 2)
GDM.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, ttl) GDM.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, ttl)
returnData = [] returnData = []
try: try:
# Send data to the multicast group # Send data to the multicast group
# dprint(__name__, 1, "Sending discovery message: {0}", Msg_PlexGDM)
GDM.sendto(Msg_PlexGDM, (IP_PlexGDM, Port_PlexGDM)) GDM.sendto(Msg_PlexGDM, (IP_PlexGDM, Port_PlexGDM))
# Look for responses from all recipients # Look for responses from all recipients
@ -559,126 +520,109 @@ class PlexAPI():
data, server = GDM.recvfrom(1024) data, server = GDM.recvfrom(1024)
# dprint(__name__, 1, "Received data from {0}", server) # dprint(__name__, 1, "Received data from {0}", server)
# dprint(__name__, 1, "Data received:\n {0}", data) # dprint(__name__, 1, "Data received:\n {0}", data)
returnData.append( { 'from' : server, returnData.append({'from': server,
'data' : data } ) 'data': data})
except socket.timeout: except socket.timeout:
break break
finally: finally:
GDM.close() GDM.close()
discovery_complete = True pmsList = {}
PMS_list = {} self.logMsg('returndata is: %s' % returnData)
if returnData:
for response in returnData: for response in returnData:
update = { 'ip' : response.get('from')[0] } update = {'ip': response.get('from')[0]}
# Check if we had a positive HTTP response # Check if we had a positive HTTP response
if "200 OK" in response.get('data'): if "200 OK" in response.get('data'):
for each in response.get('data').split('\n'): for each in response.get('data').split('\n'):
# decode response data # decode response data
update['discovery'] = "auto" update['discovery'] = "auto"
#update['owned']='1' # update['owned']='1'
#update['master']= 1 # update['master']= 1
#update['role']='master' # update['role']='master'
if "Content-Type:" in each: if "Content-Type:" in each:
update['content-type'] = each.split(':')[1].strip() update['content-type'] = each.split(':')[1].strip()
elif "Resource-Identifier:" in each: elif "Resource-Identifier:" in each:
update['uuid'] = each.split(':')[1].strip() update['uuid'] = each.split(':')[1].strip()
elif "Name:" in each: elif "Name:" in each:
update['serverName'] = each.split(':')[1].strip().decode('utf-8', 'replace') # store in utf-8 update['serverName'] = each.split(':')[1].strip().decode('utf-8', 'replace')
elif "Port:" in each: elif "Port:" in each:
update['port'] = each.split(':')[1].strip() update['port'] = each.split(':')[1].strip()
elif "Updated-At:" in each: elif "Updated-At:" in each:
update['updated'] = each.split(':')[1].strip() update['updated'] = each.split(':')[1].strip()
elif "Version:" in each: elif "Version:" in each:
update['version'] = each.split(':')[1].strip() update['version'] = each.split(':')[1].strip()
pmsList[update['uuid']] = update
PMS_list[update['uuid']] = update return pmsList
# if PMS_list=={}: def discoverPMS(self, IP_self, plexToken=None):
# 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
def discoverPMS(self, ATV_udid, CSettings, IP_self, tokenDict={}):
""" """
discoverPMS
parameters: parameters:
ATV_udid IP_self Own IP
CSettings - for manual PMS configuration. this one looks strange.
IP_self
optional: optional:
tokenDict - dictionary of tokens for MyPlex, PlexHome plexToken token for plex.tv
result: 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 # Look first for local PMS in the LAN
self.declarePMS(ATV_udid, 'plex.tv', 'plex.tv', 'https', 'plex.tv', '443') pmsList = self.PlexGDM()
self.updatePMSProperty(ATV_udid, 'plex.tv', 'local', '-') self.logMsg('pmslist: %s' % pmsList, 1)
self.updatePMSProperty(ATV_udid, 'plex.tv', 'owned', '-') for uuid in pmsList:
self.updatePMSProperty(ATV_udid, 'plex.tv', 'accesstoken', tokenDict.get('MyPlexToken', '')) PMS = pmsList[uuid]
self.declarePMS(PMS['uuid'], PMS['serverName'], 'http',
if 'PlexHomeToken' in tokenDict: PMS['ip'], PMS['port'])
authtoken = tokenDict.get('PlexHomeToken') self.updatePMSProperty(PMS['uuid'], 'owned', '-')
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, {}):
# Ping to check whether we need HTTPs or HTTP # Ping to check whether we need HTTPs or HTTP
url = (self.getPMSProperty(ATV_udid, uuid_id, 'ip') + ':' url = '%s:%s' % (PMS['ip'], PMS['port'])
+ self.getPMSProperty(ATV_udid, uuid_id, 'port'))
https = PMSHttpsEnabled(url) https = PMSHttpsEnabled(url)
if https is None: if https is None:
# Error contacting url # Error contacting url. Skip for now
continue continue
elif https: elif https is True:
self.updatePMSProperty(ATV_udid, uuid_id, 'scheme', 'https') self.updatePMSProperty(PMS['uuid'], 'scheme', 'https')
self.updatePMSProperty(
PMS['uuid'],
'baseURL',
'https://%s:%s' % (PMS['ip'], PMS['port']))
else: else:
self.updatePMSProperty(ATV_udid, uuid_id, 'scheme', 'http') # Already declared with http
# enable Gzip if not on same host, local&remote PMS depending pass
# 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)
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 getPMSListFromMyPlex
get Plex media Server List from plex.tv/pms/resources 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, authenticate=False,
headerOptions={'X-Plex-Token': authtoken}) parameters={'includeHttps': 1},
headerOptions={'X-Plex-Token': token})
try: try:
xml.attrib xml.attrib
except: except:
@ -699,36 +643,43 @@ class PlexAPI():
PMS = {} PMS = {}
PMS['name'] = Dir.get('name') PMS['name'] = Dir.get('name')
infoAge = time.time() - int(Dir.get('lastSeenAt')) infoAge = time.time() - int(Dir.get('lastSeenAt'))
oneDayInSec = 2*60*60*24 oneDayInSec = 60*60*24
if infoAge > 1*oneDayInSec: if infoAge > 2*oneDayInSec:
self.logMsg("Server %s not seen for 1 day - " self.logMsg("Server %s not seen for 2 days - "
"skipping." % PMS['name'], 0) "skipping." % PMS['name'], 0)
continue continue
PMS['uuid'] = Dir.get('clientIdentifier') PMS['uuid'] = Dir.get('clientIdentifier')
PMS['token'] = Dir.get('accessToken', authtoken) PMS['token'] = Dir.get('accessToken', token)
PMS['owned'] = Dir.get('owned', '0') PMS['owned'] = Dir.get('owned', '0')
PMS['local'] = Dir.get('publicAddressMatches') PMS['local'] = Dir.get('publicAddressMatches')
PMS['ownername'] = Dir.get('sourceTitle', '') PMS['ownername'] = Dir.get('sourceTitle', '')
PMS['path'] = '/' PMS['path'] = '/'
PMS['options'] = None PMS['options'] = None
# flag to set first connection, possibly overwrite later with # If PMS seems (!!) local, try a local connection first
# more suitable # Backup to remote connection, if that failes
PMS['baseURL'] = "" PMS['baseURL'] = ''
for Con in Dir.iter(tag='Connection'): for Con in Dir.iter(tag='Connection'):
if (PMS['baseURL'] == "" or localConn = Con.get('local')
Con.get('local') == PMS['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['protocol'] = Con.get('protocol')
PMS['ip'] = Con.get('address') PMS['ip'] = Con.get('address')
PMS['port'] = Con.get('port') PMS['port'] = Con.get('port')
PMS['baseURL'] = Con.get('baseURL') PMS['baseURL'] = Con.get('uri')
# todo: handle unforeseen - like we get multiple suitable elif PMS['local'] == '1' and localConn == '0':
# connections. how to choose one? # 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 # poke PMS, own thread for each poke
t = Thread(target=self.pokePMS, t = Thread(target=self.pokePMS,
args=(PMS['baseURL'], PMS['token'], PMS, queue)) args=(PMS, queue))
t.start() t.start()
threads.append(t) threads.append(t)
@ -739,45 +690,49 @@ class PlexAPI():
# declare new PMSs # declare new PMSs
while not queue.empty(): while not queue.empty():
PMS = queue.get() PMS = queue.get()
self.declarePMS(ATV_udid, PMS['uuid'], PMS['name'], self.declarePMS(PMS['uuid'], PMS['name'],
PMS['protocol'], PMS['ip'], PMS['port']) PMS['protocol'], PMS['ip'], PMS['port'])
# dflt: token='', local, owned - updated later # dflt: token='', local, owned - updated later
self.updatePMSProperty( self.updatePMSProperty(
ATV_udid, PMS['uuid'], 'accesstoken', PMS['token']) PMS['uuid'], 'accesstoken', PMS['token'])
self.updatePMSProperty( self.updatePMSProperty(
ATV_udid, PMS['uuid'], 'owned', PMS['owned']) PMS['uuid'], 'owned', PMS['owned'])
self.updatePMSProperty( self.updatePMSProperty(
ATV_udid, PMS['uuid'], 'local', PMS['local']) PMS['uuid'], 'local', PMS['local'])
# set in declarePMS, overwrite for https encryption # set in declarePMS, overwrite for https encryption
self.updatePMSProperty( self.updatePMSProperty(
ATV_udid, PMS['uuid'], 'baseURL', PMS['baseURL']) PMS['uuid'], 'baseURL', PMS['baseURL'])
self.updatePMSProperty( self.updatePMSProperty(
ATV_udid, PMS['uuid'], 'ownername', PMS['ownername']) PMS['uuid'], 'ownername', PMS['ownername'])
queue.task_done() queue.task_done()
def pokePMS(self, url, token, PMS, queue): def pokePMS(self, PMS, queue):
xml = self.doUtils(url, # Ignore SSL certificates for now
xml = self.doUtils(PMS['baseURL'],
authenticate=False, authenticate=False,
headerOptions={'X-Plex-Token': token}) headerOptions={'X-Plex-Token': PMS['token']},
verifySSL=False)
try: try:
xml.attrib xml.attrib
except: 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 return
else: else:
# Connection successful, process later
queue.put(PMS) 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): def MyPlexSignIn(self, username, password, options):
""" """
MyPlex Sign In, Sign Out MyPlex Sign In, Sign Out
@ -1226,13 +1181,12 @@ class PlexAPI():
return path 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 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 g_PMS format, for the client device with unique ID ATV_udid
Input: Input:
ATV_udid Unique client ID
data e.g. self.g_PMS data e.g. self.g_PMS
Output: List of all servers, with an entry of the form: Output: List of all servers, with an entry of the form:
@ -1247,21 +1201,23 @@ class PlexAPI():
'machineIdentifier': id, Plex server machine identifier 'machineIdentifier': id, Plex server machine identifier
'accesstoken': token Access token to this server 'accesstoken': token Access token to this server
'baseURL': baseURL scheme://ip:port 'baseURL': baseURL scheme://ip:port
'ownername' Plex username of PMS owner
} }
""" """
serverlist = [] serverlist = []
for key, value in data[ATV_udid].items(): for key, value in data.items():
serverlist.append({ serverlist.append({
'name': value['name'], 'name': value.get('name'),
'address': value['address'], 'address': value.get('address'),
'ip': value['ip'], 'ip': value.get('ip'),
'port': value['port'], 'port': value.get('port'),
'scheme': value['scheme'], 'scheme': value.get('scheme'),
'local': value['local'], 'local': value.get('local'),
'owned': value['owned'], 'owned': value.get('owned'),
'machineIdentifier': key, 'machineIdentifier': key,
'accesstoken': value['accesstoken'], 'accesstoken': value.get('accesstoken'),
'baseURL': value['baseURL'] 'baseURL': value.get('baseURL'),
'ownername': value.get('ownername')
}) })
return serverlist return serverlist
@ -2001,6 +1957,7 @@ class API():
return url return url
# For Direct Streaming or Transcoding # For Direct Streaming or Transcoding
from uuid import uuid4
# Path/key to VIDEO item of xml PMS response is needed, not part # Path/key to VIDEO item of xml PMS response is needed, not part
path = self.item.attrib['key'] path = self.item.attrib['key']
transcodePath = self.server + \ transcodePath = self.server + \

View file

@ -410,47 +410,38 @@ def getPlexRepeat(kodiRepeat):
def PMSHttpsEnabled(url): def PMSHttpsEnabled(url):
""" """
Returns True if the PMS wants to talk https, False otherwise. None if error Returns True if the PMS can talk https, False otherwise.
occured, e.g. the connection timed out 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 This is done by GET /identity (returns an error if https is enabled and we
are trying to use http) are trying to use http)
Prefers HTTPS over HTTP Prefers HTTPS over HTTP
""" """
# True if https, False if http doUtils = downloadutils.DownloadUtils().downloadUrl
answer = True res = doUtils('https://%s/identity' % url,
authenticate=False,
verifySSL=False)
try: try:
# Don't use downloadutils here, otherwise we may get un-authorized! res.attrib
res = requests.get('https://%s/identity' % url, except:
headers={},
verify=False,
timeout=(3, 10))
# Don't verify SSL since we can connect for sure then!
except requests.exceptions.ConnectionError as e:
# Might have SSL deactivated. Try with http # Might have SSL deactivated. Try with http
res = doUtils('http://%s/identity' % url,
authenticate=False,
verifySSL=False)
try: try:
res = requests.get('http://%s/identity' % url, res.attrib
headers={}, except:
timeout=(3, 10)) logMsg(title, "Could not contact PMS %s" % url, -1)
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)
return None return None
else: else:
answer = False # Received a valid XML. Server wants to talk HTTP
except requests.exceptions.ReadTimeout: return False
logMsg(title, "Server timeout reached for Url %s" % url, -1)
return None
if res.status_code == requests.codes.ok:
return answer
else: else:
return None # Received a valid XML. Server wants to talk HTTPS
return True
def GetMachineIdentifier(url): def GetMachineIdentifier(url):

View file

@ -5,9 +5,7 @@
import requests import requests
import xml.etree.ElementTree as etree import xml.etree.ElementTree as etree
import xbmcgui from utils import logging, settings, window
import utils
import clientinfo import clientinfo
############################################################################### ###############################################################################
@ -20,382 +18,258 @@ requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
############################################################################### ###############################################################################
@utils.logging @logging
class DownloadUtils(): 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 # Borg - multiple instances, shared state
_shared_state = {} _shared_state = {}
# Requests session # Requests session
s = None
timeout = 30 timeout = 30
def __init__(self): def __init__(self):
self.__dict__ = self._shared_state self.__dict__ = self._shared_state
def setUsername(self, username): def setUsername(self, username):
# Reserved for userclient only """
Reserved for userclient only
"""
self.username = username self.username = username
self.logMsg("Set username: %s" % username, 2) self.logMsg("Set username: %s" % username, 0)
def setUserId(self, userId): def setUserId(self, userId):
# Reserved for userclient only """
Reserved for userclient only
"""
self.userId = userId self.userId = userId
self.logMsg("Set userId: %s" % userId, 2) self.logMsg("Set userId: %s" % userId, 0)
def setServer(self, server): def setServer(self, server):
# Reserved for userclient only """
Reserved for userclient only
"""
self.server = server self.server = server
self.logMsg("Set server: %s" % server, 2) self.logMsg("Set server: %s" % server, 0)
def setToken(self, token): def setToken(self, token):
# Reserved for userclient only """
Reserved for userclient only
"""
self.token = token self.token = token
self.logMsg("Set token: xxxxxxx", 2) if token == '':
self.logMsg('Set token: empty token!', 0)
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)
else: else:
self.logMsg("Session: %s" % result, 2) self.logMsg("Set token: xxxxxxx", 0)
self.logMsg("SessionId: %s" % sessionId, 1)
utils.window('emby_sessionId', value=sessionId)
# Post any permanent additional users def setSSL(self, verifySSL=None, certificate=None):
# additionalUsers = utils.settings('additionalUsers') """
# if additionalUsers: Reserved for userclient only
# additionalUsers = additionalUsers.split(',') verifySSL must be 'true' to enable certificate validation
# self.logMsg(
# "List of permanent users added to the session: %s"
# % additionalUsers, 1)
# # Get the user list from server to get the userId certificate must be path to certificate or 'None'
# url = "{server}/emby/Users?format=json" """
# result = self.downloadUrl(url) if verifySSL is None:
verifySSL = settings('sslverify')
# for additional in additionalUsers: if certificate is None:
# addUser = additional.decode('utf-8').lower() certificate = settings('sslcert')
self.logMsg("Verify SSL certificates set to: %s" % verifySSL, 0)
# # Compare to server users to list of permanent additional users self.logMsg("SSL client side certificate set to: %s" % certificate, 0)
# for user in result: if verifySSL != 'true':
# username = user['Name'].lower() self.s.verify = False
if certificate != 'None':
# if username in addUser: self.s.cert = certificate
# userId = user['Id']
# url = (
# "{server}/emby/Sessions/%s/Users/%s?format=json"
# % (sessionId, userId)
# )
# self.downloadUrl(url, postBody={}, type="POST")
def startSession(self): def startSession(self):
# User should be authenticated when this method is called """
client = clientinfo.ClientInfo() User should be authenticated when this method is called (via
userclient)
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)
# Start session # Start session
self.s = requests.Session() self.s = requests.Session()
client = clientinfo.ClientInfo()
self.deviceId = client.getDeviceId()
# Attach authenticated header to the session # Attach authenticated header to the session
self.s.headers = client.getXArgsDeviceInfo() 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 # 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))
self.s.mount("https://", 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): def stopSession(self):
try: try:
self.s.close() self.s.close()
except: 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): 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: if options is not None:
header.update(options) header.update(options)
return header return header
def downloadUrl(self, url, postBody=None, type="GET", parameters=None, def __doDownload(self, s, type, **kwargs):
authenticate=True, headerOptions=None): if type == "GET":
timeout = self.timeout r = s.get(**kwargs)
default_link = "" 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
try: def downloadUrl(self, url, type="GET", postBody=None, parameters=None,
# If user is authenticated authenticate=True, headerOptions=None, verifySSL=True):
if (authenticate): """
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 # Get requests session
try: try:
s = self.s 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 # Replace for the real values
url = url.replace("{server}", self.server) 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: else:
verifyssl = False # User is not (yet) authenticated. Used to communicate with
self.logMsg("Set SSL verification to: %s" % verifyssl, 2) # plex.tv and to check for PMS servers
# Prepare request s = requests
if type == "GET": headerOptions = self.getHeader(options=headerOptions)
r = requests.get(url, kwargs['timeout'] = self.timeout
json=postBody, if settings('sslcert') != 'None':
params=parameters, kwargs['cert'] = settings('sslcert')
headers=header,
timeout=timeout,
verify=verifyssl)
elif type == "POST": # Set the variables we were passed (fallback to request session
r = requests.post(url, # otherwise - faster)
json=postBody, kwargs['url'] = url
headers=header, if verifySSL is False:
timeout=timeout, kwargs['verify'] = False
verify=verifyssl) if headerOptions is not None:
kwargs['headers'] = headerOptions
elif type == "PUT": if postBody is not None:
r = requests.put(url, kwargs['data'] = postBody
json=postBody, if parameters is not None:
headers=header, kwargs['params'] = parameters
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:
# ACTUAL DOWNLOAD HAPPENING HERE
try: try:
# Allow for xml responses r = self.__doDownload(s, type, **kwargs)
r = etree.fromstring(r.content)
# self.logMsg("====== 200 Success ======", 2)
# self.logMsg("Received an XML response for: %s" % url, 2)
# 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
window('emby_online', value="false")
return False
except requests.exceptions.ConnectTimeout as e:
self.logMsg("Server timeout at: %s" % url, -1)
self.logMsg(e, 2)
return False
except requests.exceptions.HTTPError as e:
r = r.status_code
if r == 401:
# Unauthorized
self.logMsg('Error 401 contacting %s' % url, -1)
elif r in (301, 302):
# Redirects
self.logMsg('HTTP redirect error %s at %s' % (r, url), -1)
elif r == 400:
# Bad requests
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 return r
except requests.exceptions.SSLError as e:
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, -1)
self.logMsg("Error message: %s" % e, 2)
return False
except: 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: try:
# UNICODE - JSON object # UNICODE - JSON object
r = r.json() r = r.json()
# self.logMsg("====== 200 Success ======", 2)
# self.logMsg("Response: %s" % r, 2)
return r return r
except: except:
try: self.logMsg("Unable to convert the response for: %s"
if r.text == '' and r.status_code == 200: % url, -1)
# self.logMsg("====== 200 Success ======", 2) self.logMsg("Received headers were: %s" % r.headers, -1)
# 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 #####
except requests.exceptions.ConnectionError as e:
# 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")
except requests.exceptions.ConnectTimeout as e:
self.logMsg("Server timeout at: %s" % url, 0)
self.logMsg(e, 1)
except requests.exceptions.HTTPError as e:
if r.status_code == 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 return False
else:
elif r.headers['X-Application-Error-Code'] == "UnauthorizedAccessException": self.logMsg('Unknown answer from PMS %s with status code %s. '
# User tried to do something his emby account doesn't allow 'Message:' % (url, r.status_code), -1)
pass r.encoding = 'utf-8'
self.logMsg(r.text, -1)
elif status not in ("401", "Auth"): return True
# 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):
# Redirects
pass
elif r.status_code == 400:
# Bad requests
pass
except requests.exceptions.SSLError as e:
self.logMsg("Invalid SSL certificate for: %s" % url, 0)
self.logMsg(e, 1)
except requests.exceptions.RequestException as e:
self.logMsg("Unknown error connecting to: %s" % url, 0)
self.logMsg(e, 1)
return default_link

View file

@ -36,7 +36,6 @@ class InitialSetup():
# SERVER INFO ##### # SERVER INFO #####
self.logMsg("Initial setup called.", 0) self.logMsg("Initial setup called.", 0)
server = self.userClient.getServer() server = self.userClient.getServer()
clientId = self.clientInfo.getDeviceId()
serverid = utils.settings('plex_machineIdentifier') serverid = utils.settings('plex_machineIdentifier')
# Get Plex credentials from settings file, if they exist # Get Plex credentials from settings file, if they exist
plexdict = self.plx.GetPlexLoginFromSettings() plexdict = self.plx.GetPlexLoginFromSettings()
@ -54,8 +53,15 @@ class InitialSetup():
if (plexToken and myplexlogin == 'true' and forcePlexTV is False if (plexToken and myplexlogin == 'true' and forcePlexTV is False
and chooseServer is False): and chooseServer is False):
chk = self.plx.CheckConnection('plex.tv', plexToken) 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 # 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) self.logMsg('plex.tv connection returned HTTP %s' % chk, 0)
# Delete token in the settings # Delete token in the settings
utils.settings('plexToken', value='') utils.settings('plexToken', value='')
@ -113,18 +119,13 @@ class InitialSetup():
httpsUpdated = False httpsUpdated = False
while True: while True:
if httpsUpdated is False: if httpsUpdated is False:
tokenDict = {'MyPlexToken': plexToken} if plexToken else {}
# Populate g_PMS variable with the found Plex servers # Populate g_PMS variable with the found Plex servers
self.plx.discoverPMS(clientId, self.plx.discoverPMS(xbmc.getIPAddress(),
None, plexToken=plexToken)
xbmc.getIPAddress(),
tokenDict=tokenDict)
self.logMsg("Result of setting g_PMS variable: %s"
% self.plx.g_PMS, 1)
isconnected = False isconnected = False
serverlist = self.plx.returnServerList(clientId, self.logMsg('g_PMS: %s' % self.plx.g_PMS, 1)
self.plx.g_PMS) serverlist = self.plx.returnServerList(self.plx.g_PMS)
self.logMsg('PMS serverlist: %s' % serverlist) self.logMsg('PMS serverlist: %s' % serverlist, 2)
# Let user pick server from a list # Let user pick server from a list
# Get a nicer list # Get a nicer list
dialoglist = [] dialoglist = []
@ -138,11 +139,20 @@ class InitialSetup():
for server in serverlist: for server in serverlist:
if server['local'] == '1': if server['local'] == '1':
# server is in the same network as client. Add "local" # server is in the same network as client. Add "local"
dialoglist.append( msg = string(39022)
server['name']
+ string(39022))
else: 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) resp = dialog.select(string(39012), dialoglist)
server = serverlist[resp] server = serverlist[resp]
activeServer = server['machineIdentifier'] activeServer = server['machineIdentifier']
@ -154,15 +164,20 @@ class InitialSetup():
else: else:
url = server['baseURL'] url = server['baseURL']
# Deactive SSL verification if the server is local! # Deactive SSL verification if the server is local!
# Watch out - settings is cached by Kodi - use dedicated var!
if server['local'] == '1': if server['local'] == '1':
utils.settings('sslverify', 'false') utils.settings('sslverify', 'false')
self.logMsg("Setting SSL verify to false, because server is " self.logMsg("Setting SSL verify to false, because server is "
"local", 1) "local", 1)
verifySSL = False
else: else:
utils.settings('sslverify', 'true') utils.settings('sslverify', 'true')
self.logMsg("Setting SSL verify to true, because server is " self.logMsg("Setting SSL verify to true, because server is "
"not local", 1) "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: if chk == 504 and httpsUpdated is False:
# Not able to use HTTP, try HTTPs for now # Not able to use HTTP, try HTTPs for now
serverlist[resp]['scheme'] = 'https' serverlist[resp]['scheme'] = 'https'
@ -221,19 +236,6 @@ class InitialSetup():
% (activeServer, server['ip'], server['port'], % (activeServer, server['ip'], server['port'],
server['scheme']), 0) 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: if forcePlexTV is True or chooseServer is True:
return return

View file

@ -278,9 +278,8 @@ class Subscriber:
Threaded POST request, because they stall due to PMS response missing Threaded POST request, because they stall due to PMS response missing
the Content-Length header :-( the Content-Length header :-(
""" """
response = self.download.downloadUrl( response = self.download.downloadUrl(url,
url,
postBody=msg, postBody=msg,
type="POSTXML") type="POST")
if response in [False, None, 401]: if response in [False, None, 401]:
self.subMgr.removeSubscriber(self.uuid) self.subMgr.removeSubscriber(self.uuid)

View file

@ -106,29 +106,12 @@ class UserClient(threading.Thread):
def getSSLverify(self): def getSSLverify(self):
# Verify host certificate # Verify host certificate
settings = utils.settings return None if utils.settings('sslverify') == 'true' else False
s_sslverify = settings('sslverify')
if settings('altip') == "true":
s_sslverify = settings('secondsslverify')
if s_sslverify == "true":
return True
else:
return False
def getSSL(self): def getSSL(self):
# Client side certificate # Client side certificate
settings = utils.settings return None if utils.settings('sslcert') == 'None' \
else utils.settings('sslcert')
s_cert = settings('sslcert')
if settings('altip') == "true":
s_cert = settings('secondsslcert')
if s_cert == "None":
return None
else:
return s_cert
def setUserPref(self): def setUserPref(self):
self.logMsg('Setting user preferences', 0) self.logMsg('Setting user preferences', 0)
@ -183,8 +166,9 @@ class UserClient(threading.Thread):
if authenticated is False: if authenticated is False:
self.logMsg('Testing validity of current token', 0) self.logMsg('Testing validity of current token', 0)
res = PlexAPI.PlexAPI().CheckConnection( res = PlexAPI.PlexAPI().CheckConnection(self.currServer,
self.currServer, self.currToken) token=self.currToken,
verifySSL=self.ssl)
if res is False: if res is False:
self.logMsg('Answer from PMS is not as expected. Retrying', -1) self.logMsg('Answer from PMS is not as expected. Retrying', -1)
return False return False
@ -227,13 +211,6 @@ class UserClient(threading.Thread):
window('remapSMB%sOrg' % item, value=org) window('remapSMB%sOrg' % item, value=org)
window('remapSMB%sNew' % item, value=new) 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 # Start DownloadUtils session
doUtils.startSession() doUtils.startSession()
# self.getAdditionalUsers() # self.getAdditionalUsers()

View file

@ -233,7 +233,7 @@ class Service():
if server is False: if server is False:
# No server info set in add-on settings # No server info set in add-on settings
pass pass
elif plx.CheckConnection(server) is False: elif plx.CheckConnection(server, verifySSL=True) is False:
# Server is offline or cannot be reached # Server is offline or cannot be reached
# Alert the user and suppress future warning # Alert the user and suppress future warning
if self.server_online: if self.server_online: