Rewired download and PMS connection
- Look for PMS in the LAN, even if plex.tv is available
This commit is contained in:
parent
8bad79413c
commit
260fc7adf8
9 changed files with 468 additions and 664 deletions
|
@ -363,7 +363,7 @@
|
|||
<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="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="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>
|
||||
|
@ -396,6 +396,8 @@
|
|||
<string id="39051">Wait before sync new/changed PMS item [s]</string>
|
||||
<string id="39052">Background Sync</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>
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -301,7 +301,7 @@
|
|||
<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="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="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>
|
||||
|
@ -334,6 +334,8 @@
|
|||
<string id="39051">Warten bevor neue/geänderte PMS Einträge gesynct werden [s]</string>
|
||||
<string id="39052">Hintergrund-Synchronisation</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 -->
|
||||
|
|
|
@ -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,23 +446,21 @@ 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,
|
||||
self.g_PMS[uuid] = {
|
||||
'name': name,
|
||||
'scheme': scheme,
|
||||
'ip': ip,
|
||||
'port': port,
|
||||
'address': address,
|
||||
'baseURL': baseURL,
|
||||
'local': '1',
|
||||
|
@ -480,48 +469,22 @@ class PlexAPI():
|
|||
'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
|
||||
try:
|
||||
self.g_PMS[uuid][tag] = value
|
||||
except:
|
||||
self.logMsg('%s has not yet been declared ' % uuid, -1)
|
||||
return False
|
||||
|
||||
self.g_PMS[ATV_udid][uuid][tag] = value
|
||||
|
||||
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)
|
||||
GDM.settimeout(2.0)
|
||||
|
||||
# Set the time-to-live for messages to 1 for local network
|
||||
ttl = struct.pack('b', 1)
|
||||
# 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
|
||||
|
@ -566,13 +527,11 @@ class PlexAPI():
|
|||
finally:
|
||||
GDM.close()
|
||||
|
||||
discovery_complete = True
|
||||
pmsList = {}
|
||||
|
||||
PMS_list = {}
|
||||
if returnData:
|
||||
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'):
|
||||
|
@ -587,98 +546,83 @@ class PlexAPI():
|
|||
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
|
||||
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
|
||||
|
||||
PMS_list[update['uuid']] = update
|
||||
return pmsList
|
||||
|
||||
# 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
|
||||
|
||||
def discoverPMS(self, ATV_udid, CSettings, IP_self, tokenDict={}):
|
||||
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:
|
||||
# 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 + \
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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)
|
||||
self.logMsg("Set token: xxxxxxx", 0)
|
||||
|
||||
# Post any permanent additional users
|
||||
# additionalUsers = utils.settings('additionalUsers')
|
||||
# if additionalUsers:
|
||||
def setSSL(self, verifySSL=None, certificate=None):
|
||||
"""
|
||||
Reserved for userclient only
|
||||
|
||||
# additionalUsers = additionalUsers.split(',')
|
||||
# self.logMsg(
|
||||
# "List of permanent users added to the session: %s"
|
||||
# % additionalUsers, 1)
|
||||
verifySSL must be 'true' to enable certificate validation
|
||||
|
||||
# # Get the user list from server to get the userId
|
||||
# url = "{server}/emby/Users?format=json"
|
||||
# result = self.downloadUrl(url)
|
||||
|
||||
# for additional in additionalUsers:
|
||||
# addUser = additional.decode('utf-8').lower()
|
||||
|
||||
# # 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()
|
||||
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
|
||||
|
||||
try:
|
||||
# If user is authenticated
|
||||
if (authenticate):
|
||||
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)
|
||||
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)
|
||||
# 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')
|
||||
|
||||
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:
|
||||
# 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:
|
||||
# Allow for xml responses
|
||||
r = etree.fromstring(r.content)
|
||||
# self.logMsg("====== 200 Success ======", 2)
|
||||
# self.logMsg("Received an XML response for: %s" % url, 2)
|
||||
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
|
||||
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
|
||||
|
||||
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:
|
||||
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()
|
||||
# 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 #####
|
||||
|
||||
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)
|
||||
self.logMsg("Unable to convert the response for: %s"
|
||||
% url, -1)
|
||||
self.logMsg("Received headers were: %s" % r.headers, -1)
|
||||
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):
|
||||
# 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
|
||||
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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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,
|
||||
response = self.download.downloadUrl(url,
|
||||
postBody=msg,
|
||||
type="POSTXML")
|
||||
type="POST")
|
||||
if response in [False, None, 401]:
|
||||
self.subMgr.removeSubscriber(self.uuid)
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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:
|
||||
|
|
Loading…
Reference in a new issue