Move any download activities to one method

This commit is contained in:
tomkat83 2016-04-05 20:10:29 +02:00
parent 12add3d369
commit 8bad79413c
4 changed files with 145 additions and 189 deletions

View file

@ -37,7 +37,6 @@ import socket
import StringIO import StringIO
import gzip import gzip
from threading import Thread from threading import Thread
import Queue
import traceback import traceback
import requests import requests
import xml.etree.ElementTree as etree import xml.etree.ElementTree as etree
@ -71,7 +70,7 @@ class PlexAPI():
def __init__(self): def __init__(self):
self.__language__ = xbmcaddon.Addon().getLocalizedString self.__language__ = xbmcaddon.Addon().getLocalizedString
self.g_PMS = {} self.g_PMS = {}
self.doUtils = downloadutils.DownloadUtils() self.doUtils = downloadutils.DownloadUtils().downloadUrl
def GetPlexLoginFromSettings(self): def GetPlexLoginFromSettings(self):
""" """
@ -227,8 +226,9 @@ class PlexAPI():
Returns False if not yet done so, or the XML response file as etree Returns False if not yet done so, or the XML response file as etree
""" """
# Try to get a temporary token # Try to get a temporary token
url = 'https://plex.tv/pins/%s.xml' % identifier xml = self.doUtils('https://plex.tv/pins/%s.xml' % identifier,
xml = self.TalkToPlexServer(url, talkType="GET2") authenticate=False,
type="GET")
try: try:
temp_token = xml.find('auth_token').text temp_token = xml.find('auth_token').text
except: except:
@ -238,29 +238,31 @@ class PlexAPI():
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
url = 'https://plex.tv/users/account?X-Plex-Token=%s' % temp_token xml = self.doUtils('https://plex.tv/users/account?X-Plex-Token=%s'
xml = self.TalkToPlexServer(url, talkType="GET") % temp_token,
authenticate=False,
type="GET")
return xml return xml
def GetPlexPin(self): def GetPlexPin(self):
""" """
For plex.tv sign-in: returns 4-digit code and identifier as 2 str For plex.tv sign-in: returns 4-digit code and identifier as 2 str
""" """
url = 'https://plex.tv/pins.xml'
code = None code = None
identifier = None identifier = None
# Download # Download
xml = self.TalkToPlexServer(url, talkType="POST") xml = self.doUtils('https://plex.tv/pins.xml',
if xml is False: authenticate=False,
return code, identifier type="POST")
try: try:
code = xml.find('code').text xml.attrib
identifier = xml.find('id').text
self.logMsg('Successfully retrieved code and id from plex.tv', 1)
return code, identifier
except: except:
self.logMsg("Error, no PIN from plex.tv provided", -1) self.logMsg("Error, no PIN from plex.tv provided", -1)
return None, None return None, None
code = xml.find('code').text
identifier = xml.find('id').text
self.logMsg('Successfully retrieved code and id from plex.tv', 1)
return code, identifier
def TalkToPlexServer(self, url, talkType="GET", verify=True, token=None): def TalkToPlexServer(self, url, talkType="GET", verify=True, token=None):
""" """
@ -674,137 +676,95 @@ class PlexAPI():
get Plex media Server List from plex.tv/pms/resources get Plex media Server List from plex.tv/pms/resources
""" """
# dprint(__name__, 0, "***") xml = self.doUtils('https://plex.tv/api/resources?includeHttps=1',
# dprint(__name__, 0, "poke plex.tv - request Plex Media Server list") authenticate=False,
# dprint(__name__, 0, "***") headerOptions={'X-Plex-Token': authtoken})
XML = self.getXMLFromPMS('https://plex.tv', '/api/resources?includeHttps=1', {}, authtoken)
if XML==False:
pass # no data from MyPlex
else:
queue = Queue.Queue()
threads = []
for Dir in XML.getiterator('Device'):
if Dir.get('product','') == "Plex Media Server" and Dir.get('provides','') == "server":
uuid = Dir.get('clientIdentifier')
name = Dir.get('name')
token = Dir.get('accessToken', authtoken)
owned = Dir.get('owned', '0')
local = Dir.get('publicAddressMatches')
if Dir.find('Connection') == None:
continue # no valid connection - skip
uri = "" # flag to set first connection, possibly overwrite later with more suitable
for Con in Dir.getiterator('Connection'):
if uri=="" or Con.get('local','') == local:
protocol = Con.get('protocol')
ip = Con.get('address')
port = Con.get('port')
uri = Con.get('uri')
# todo: handle unforeseen - like we get multiple suitable connections. how to choose one?
# check MyPlex data age - skip if >1 days
infoAge = time.time() - int(Dir.get('lastSeenAt'))
oneDayInSec = 60*60*24
if infoAge > 1*oneDayInSec:
self.logMsg("Server %s not updated for 1 day - "
"skipping." % name, 0)
continue
# poke PMS, own thread for each poke
PMSInfo = { 'uuid': uuid, 'name': name, 'token': token, 'owned': owned, 'local': local, \
'protocol': protocol, 'ip': ip, 'port': port, 'uri': uri }
PMS = { 'baseURL': uri, 'path': '/', 'options': None, 'token': token, \
'data': PMSInfo }
t = Thread(target=self.getXMLFromPMSToQueue, args=(PMS, queue))
t.start()
threads.append(t)
# wait for requests being answered
for t in threads:
t.join()
# declare new PMSs
while not queue.empty():
(PMSInfo, PMS) = queue.get()
if PMS==False:
continue
uuid = PMSInfo['uuid']
name = PMSInfo['name']
token = PMSInfo['token']
owned = PMSInfo['owned']
local = PMSInfo['local']
protocol = PMSInfo['protocol']
ip = PMSInfo['ip']
port = PMSInfo['port']
uri = PMSInfo['uri']
self.declarePMS(ATV_udid, uuid, name, protocol, ip, port) # dflt: token='', local, owned - updated later
self.updatePMSProperty(ATV_udid, uuid, 'accesstoken', token)
self.updatePMSProperty(ATV_udid, uuid, 'owned', owned)
self.updatePMSProperty(ATV_udid, uuid, 'local', local)
self.updatePMSProperty(ATV_udid, uuid, 'baseURL', uri) # set in declarePMS, overwrite for https encryption
def getXMLFromPMS(self, baseURL, path, options={}, authtoken='', enableGzip=False):
"""
Plex Media Server communication
parameters:
host
path
options - dict() of PlexConnect-options as received from aTV
None for no
std. X-Plex-Args
authtoken - authentication answer from MyPlex Sign In
result:
returned XML or 'False' in case of error
"""
xargs = {}
if options is not None:
xargs = self.getXArgsDeviceInfo(options)
if not authtoken == '':
xargs['X-Plex-Token'] = authtoken
self.logMsg("URL for XML download: %s%s" % (baseURL, path), 1)
request = urllib2.Request(baseURL+path, None, xargs)
request.add_header('User-agent', 'PlexDB')
if enableGzip:
request.add_header('Accept-encoding', 'gzip')
try: try:
response = urllib2.urlopen(request, timeout=20) xml.attrib
except (urllib2.URLError, httplib.HTTPException) as e: except:
self.logMsg("No Response from Plex Media Server", 0) self.logMsg('Could not get list of PMS from plex.tv', -1)
if hasattr(e, 'reason'): return
self.logMsg("We failed to reach a server. Reason: %s" % e.reason, 0)
elif hasattr(e, 'code'):
self.logMsg("The server couldn't fulfill the request. Error code: %s" % e.code, 0)
self.logMsg("Traceback:\n%s" % traceback.format_exc(), 0)
return False
except IOError:
self.logMsg("Error loading response XML from Plex Media Server:\n%s" % traceback.format_exc(), 0)
return False
if response.info().get('Content-Encoding') == 'gzip': import Queue
buf = StringIO.StringIO(response.read()) queue = Queue.Queue()
file = gzip.GzipFile(fileobj=buf) threads = []
XML = etree.parse(file)
for Dir in xml.iter(tag='Device'):
if "server" in Dir.get('provides'):
if Dir.find('Connection') is None:
# no valid connection - skip
continue
# check MyPlex data age - skip if >2 days
PMS = {}
PMS['name'] = Dir.get('name')
infoAge = time.time() - int(Dir.get('lastSeenAt'))
oneDayInSec = 2*60*60*24
if infoAge > 1*oneDayInSec:
self.logMsg("Server %s not seen for 1 day - "
"skipping." % PMS['name'], 0)
continue
PMS['uuid'] = Dir.get('clientIdentifier')
PMS['token'] = Dir.get('accessToken', authtoken)
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'] = ""
for Con in Dir.iter(tag='Connection'):
if (PMS['baseURL'] == "" or
Con.get('local') == PMS['local']):
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?
# poke PMS, own thread for each poke
t = Thread(target=self.pokePMS,
args=(PMS['baseURL'], PMS['token'], PMS, queue))
t.start()
threads.append(t)
# wait for requests being answered
for t in threads:
t.join()
# declare new PMSs
while not queue.empty():
PMS = queue.get()
self.declarePMS(ATV_udid, 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'])
self.updatePMSProperty(
ATV_udid, PMS['uuid'], 'owned', PMS['owned'])
self.updatePMSProperty(
ATV_udid, PMS['uuid'], 'local', PMS['local'])
# set in declarePMS, overwrite for https encryption
self.updatePMSProperty(
ATV_udid, PMS['uuid'], 'baseURL', PMS['baseURL'])
self.updatePMSProperty(
ATV_udid, PMS['uuid'], 'ownername', PMS['ownername'])
queue.task_done()
def pokePMS(self, url, token, PMS, queue):
xml = self.doUtils(url,
authenticate=False,
headerOptions={'X-Plex-Token': token})
try:
xml.attrib
except:
return
else: else:
# parse into etree queue.put(PMS)
XML = etree.parse(response)
# Log received XML if debugging enabled.
self.logMsg("====== received PMS-XML ======", 1)
self.logMsg(XML, 1)
self.logMsg("====== PMS-XML finished ======", 1)
return XML
def getXMLFromPMSToQueue(self, PMS, queue):
XML = self.getXMLFromPMS(PMS['baseURL'],PMS['path'],PMS['options'],PMS['token'])
queue.put( (PMS['data'], XML) )
def getURL(self, baseURL, path, key): def getURL(self, baseURL, path, key):
if key.startswith('http://') or key.startswith('https://'): # external server if key.startswith('http://') or key.startswith('https://'): # external server
@ -1046,7 +1006,10 @@ class PlexAPI():
if pin: if pin:
url += '?pin=' + pin url += '?pin=' + pin
self.logMsg('Switching to user %s' % userId, 0) self.logMsg('Switching to user %s' % userId, 0)
answer = self.TalkToPlexServer(url, talkType="POST", token=token) answer = self.doUtils(url,
authenticate=False,
type="POST",
headerOptions={'X-Plex-Token': token})
try: try:
answer.attrib answer.attrib
except: except:
@ -1064,7 +1027,10 @@ class PlexAPI():
# Get final token to the PMS we've chosen # Get final token to the PMS we've chosen
url = 'https://plex.tv/api/resources?includeHttps=1' url = 'https://plex.tv/api/resources?includeHttps=1'
xml = self.TalkToPlexServer(url, talkType="GET", token=token) xml = self.doUtils(url,
authenticate=False,
type="GET",
headerOptions={'X-Plex-Token': token})
try: try:
xml.attrib xml.attrib
except: except:
@ -1096,12 +1062,12 @@ class PlexAPI():
% username, 0) % username, 0)
return result return result
def MyPlexListHomeUsers(self, authtoken): def MyPlexListHomeUsers(self, token):
""" """
Returns a list for myPlex home users for the current plex.tv account. Returns a list for myPlex home users for the current plex.tv account.
Input: Input:
authtoken for plex.tv token for plex.tv
Output: Output:
List of users, where one entry is of the form: List of users, where one entry is of the form:
"id": userId, "id": userId,
@ -1117,16 +1083,16 @@ class PlexAPI():
If any value is missing, None is returned instead (or "" from plex.tv) If any value is missing, None is returned instead (or "" from plex.tv)
If an error is encountered, False is returned If an error is encountered, False is returned
""" """
XML = self.getXMLFromPMS( xml = self.doUtils('https://plex.tv/api/home/users/',
'https://plex.tv', '/api/home/users/', {}, authtoken) authenticate=False,
if not XML: headerOptions={'X-Plex-Token': token})
try:
xml.attrib
except:
self.logMsg('Download of Plex home users failed.', -1) self.logMsg('Download of Plex home users failed.', -1)
self.logMsg('plex.tv xml received was: %s' % XML, -1)
return False return False
# analyse response
root = XML.getroot()
users = [] users = []
for user in root: for user in xml:
users.append(user.attrib) users.append(user.attrib)
return users return users

View file

@ -2,12 +2,9 @@
############################################################################### ###############################################################################
# import json
import requests import requests
import xml.etree.ElementTree as etree import xml.etree.ElementTree as etree
# import logging
# import xbmc
import xbmcgui import xbmcgui
import utils import utils
@ -36,8 +33,6 @@ class DownloadUtils():
def __init__(self): def __init__(self):
self.__dict__ = self._shared_state self.__dict__ = self._shared_state
self.clientInfo = clientinfo.ClientInfo()
def setUsername(self, username): def setUsername(self, username):
# Reserved for userclient only # Reserved for userclient only
self.username = username self.username = username
@ -65,7 +60,6 @@ class DownloadUtils():
self.logMsg("Verify SSL host certificate: %s" % ssl, 2) self.logMsg("Verify SSL host certificate: %s" % ssl, 2)
self.logMsg("SSL client side certificate: %s" % sslclient, 2) self.logMsg("SSL client side certificate: %s" % sslclient, 2)
def postCapabilities(self, deviceId): def postCapabilities(self, deviceId):
# Post settings to session # Post settings to session
@ -136,17 +130,12 @@ class DownloadUtils():
# ) # )
# self.downloadUrl(url, postBody={}, type="POST") # self.downloadUrl(url, postBody={}, type="POST")
def startSession(self): def startSession(self):
# User should be authenticated when this method is called
client = clientinfo.ClientInfo()
log = self.logMsg self.deviceId = client.getDeviceId()
self.deviceId = self.clientInfo.getDeviceId()
# User is identified from this point
# Attach authenticated header to the session
verify = False verify = False
header = self.getHeader()
# If user enabled host certificate verification # If user enabled host certificate verification
try: try:
@ -154,17 +143,17 @@ class DownloadUtils():
if self.sslclient is not None: if self.sslclient is not None:
verify = self.sslclient verify = self.sslclient
except: except:
log("Could not load SSL settings.", 1) self.logMsg("Could not load SSL settings.", -1)
# Start session # Start session
self.s = requests.Session() self.s = requests.Session()
self.s.headers = header # Attach authenticated header to the session
self.s.headers = client.getXArgsDeviceInfo()
self.s.verify = verify self.s.verify = verify
# 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))
log("Requests session started on: %s" % self.server, 1) self.logMsg("Requests session started on: %s" % self.server, 1)
def stopSession(self): def stopSession(self):
try: try:
@ -172,15 +161,17 @@ class DownloadUtils():
except: except:
self.logMsg("Requests session could not be terminated.", 1) self.logMsg("Requests session could not be terminated.", 1)
def getHeader(self, authenticate=True, options={}): def getHeader(self, options=None):
if authenticate: try:
header = self.clientInfo.getXArgsDeviceInfo(options=options) header = self.s.headers.copy()
else: except:
header = self.clientInfo.getXArgsDeviceInfo(options=options) header = clientinfo.ClientInfo().getXArgsDeviceInfo()
if options is not None:
header.update(options)
return header return header
def downloadUrl(self, url, postBody=None, type="GET", parameters=None, def downloadUrl(self, url, postBody=None, type="GET", parameters=None,
authenticate=True, headerOptions={}): authenticate=True, headerOptions=None):
timeout = self.timeout timeout = self.timeout
default_link = "" default_link = ""
@ -271,8 +262,7 @@ class DownloadUtils():
# If user is not authenticated # If user is not authenticated
elif not authenticate: elif not authenticate:
header = self.getHeader(authenticate=False, header = self.getHeader(options=headerOptions)
options=headerOptions)
# If user enables ssl verification # If user enables ssl verification
try: try:
@ -285,7 +275,6 @@ class DownloadUtils():
else: else:
verifyssl = False verifyssl = False
self.logMsg("Set SSL verification to: %s" % verifyssl, 2) self.logMsg("Set SSL verification to: %s" % verifyssl, 2)
# Prepare request # Prepare request
if type == "GET": if type == "GET":
r = requests.get(url, r = requests.get(url,

View file

@ -22,7 +22,7 @@ class InitialSetup():
def __init__(self): def __init__(self):
self.clientInfo = clientinfo.ClientInfo() self.clientInfo = clientinfo.ClientInfo()
self.addonId = self.clientInfo.getAddonId() self.addonId = self.clientInfo.getAddonId()
self.doUtils = downloadutils.DownloadUtils() self.doUtils = downloadutils.DownloadUtils().downloadUrl
self.userClient = userclient.UserClient() self.userClient = userclient.UserClient()
self.plx = PlexAPI.PlexAPI() self.plx = PlexAPI.PlexAPI()
@ -76,11 +76,14 @@ class InitialSetup():
else: else:
self.logMsg('plex.tv connection with token successful', 0) self.logMsg('plex.tv connection with token successful', 0)
# Refresh the info from Plex.tv # Refresh the info from Plex.tv
url = 'https://plex.tv/' xml = self.doUtils('https://plex.tv/users/account',
path = 'users/account' authenticate=False,
xml = self.plx.getXMLFromPMS(url, path, authtoken=plexToken) headerOptions={'X-Plex-Token': plexToken})
if xml: try:
xml = xml.getroot() xml.attrib
except:
self.logMsg('Failed to update Plex info from plex.tv', -1)
else:
plexLogin = xml.attrib.get('title') plexLogin = xml.attrib.get('title')
utils.settings('plexLogin', value=plexLogin) utils.settings('plexLogin', value=plexLogin)
home = 'true' if xml.attrib.get('home') == '1' else 'false' home = 'true' if xml.attrib.get('home') == '1' else 'false'
@ -89,8 +92,6 @@ class InitialSetup():
utils.settings( utils.settings(
'plexHomeSize', value=xml.attrib.get('homeSize', '1')) 'plexHomeSize', value=xml.attrib.get('homeSize', '1'))
self.logMsg('Updated Plex info from plex.tv', 0) self.logMsg('Updated Plex info from plex.tv', 0)
else:
self.logMsg('Failed to update Plex info from plex.tv', -1)
# If a Plex server IP has already been set, return. # If a Plex server IP has already been set, return.
if server and forcePlexTV is False and chooseServer is False: if server and forcePlexTV is False and chooseServer is False:

View file

@ -142,7 +142,7 @@ class plexgdm:
else: else:
if "M-SEARCH * HTTP/1." in data: if "M-SEARCH * HTTP/1." in data:
self.logMsg("Detected client discovery request from %s. " self.logMsg("Detected client discovery request from %s. "
" Replying" % addr, 2) " Replying" % str(addr), 2)
try: try:
update_sock.sendto("HTTP/1.0 200 OK\r\n%s" update_sock.sendto("HTTP/1.0 200 OK\r\n%s"
% self.client_data, % self.client_data,