PlexKodiConnect/resources/lib/DownloadUtils.py

348 lines
13 KiB
Python
Raw Normal View History

import xbmc
import xbmcgui
import xbmcaddon
import requests
import json
import logging
import Utils as utils
from ClientInformation import ClientInformation
from requests.packages.urllib3.exceptions import InsecureRequestWarning
2015-04-22 07:34:56 +10:00
# Disable requests logging
2015-05-27 13:42:14 +10:00
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
#logging.getLogger("requests").setLevel(logging.WARNING)
2015-03-14 08:24:59 +11:00
class DownloadUtils():
2015-04-22 07:34:56 +10:00
# Borg - multiple instances, shared state
_shared_state = {}
clientInfo = ClientInformation()
2015-04-22 07:34:56 +10:00
addonName = clientInfo.getAddonName()
addon = xbmcaddon.Addon()
WINDOW = xbmcgui.Window(10000)
2015-04-03 21:13:01 +11:00
2015-04-22 07:34:56 +10:00
# Requests session
s = None
timeout = 60
2015-04-22 07:34:56 +10:00
def __init__(self):
2015-03-14 08:24:59 +11:00
2015-04-22 07:34:56 +10:00
self.__dict__ = self._shared_state
2015-03-14 08:24:59 +11:00
2015-04-22 07:34:56 +10:00
def logMsg(self, msg, lvl=1):
2015-03-14 08:24:59 +11:00
self.className = self.__class__.__name__
utils.logMsg("%s %s" % (self.addonName, self.className), msg, int(lvl))
2015-03-14 08:24:59 +11:00
2015-04-22 07:34:56 +10:00
def setUsername(self, username):
# Reserved for UserClient only
2015-04-22 07:34:56 +10:00
self.username = username
self.logMsg("Set username: %s" % username, 2)
2015-03-14 08:24:59 +11:00
2015-04-22 07:34:56 +10:00
def setUserId(self, userId):
# Reserved for UserClient only
2015-04-22 07:34:56 +10:00
self.userId = userId
self.logMsg("Set userId: %s" % userId, 2)
def setServer(self, server):
# Reserved for UserClient only
2015-04-22 07:34:56 +10:00
self.server = server
self.logMsg("Set server: %s" % server, 2)
def setToken(self, token):
# Reserved for UserClient only
2015-04-22 07:34:56 +10:00
self.token = token
self.logMsg("Set token: %s" % token, 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)
2015-04-22 07:34:56 +10:00
def postCapabilities(self, deviceId):
2015-05-03 12:28:24 +10:00
# Post settings to session
url = "{server}/mediabrowser/Sessions/Capabilities/Full"
2015-05-03 12:28:24 +10:00
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"
)
2015-05-03 12:28:24 +10:00
}
2015-04-22 07:34:56 +10:00
self.logMsg("Capabilities URL: %s" % url, 2)
self.logMsg("PostData: %s" % data, 2)
2015-04-22 07:34:56 +10:00
try:
self.downloadUrl(url, postBody=data, type="POST")
self.logMsg("Posted capabilities to %s" % self.server, 1)
except:
self.logMsg("Posted capabilities failed.")
2015-04-22 07:34:56 +10:00
# Attempt at getting sessionId
url = "{server}/mediabrowser/Sessions?DeviceId=%s&format=json" % deviceId
try:
result = self.downloadUrl(url)
self.logMsg("Session: %s" % result, 2)
sessionId = result[0][u'Id']
self.logMsg("SessionId: %s" % sessionId)
self.WINDOW.setProperty("sessionId%s" % self.username, sessionId)
except:
self.logMsg("Failed to retrieve sessionId.", 1)
else:
# Post any permanent additional users
additionalUsers = utils.settings('additionalUsers').split(',')
self.logMsg("List of permanent users that should be added to the session: %s" % str(additionalUsers), 1)
# Get the user list from server to get the userId
url = "{server}/mediabrowser/Users?format=json"
result = self.downloadUrl(url)
if result:
for user in result:
username = user['Name'].lower()
userId = user['Id']
for additional in additionalUsers:
addUser = additional.decode('utf-8').lower()
if username in addUser:
url = "{server}/mediabrowser/Sessions/%s/Users/%s" % (sessionId, userId)
postdata = {}
self.downloadUrl(url, postBody=postdata, type="POST")
#xbmcgui.Dialog().notification("Success!", "%s added to viewing session" % username, time=1000)
2015-04-22 07:34:56 +10:00
def startSession(self):
self.deviceId = self.clientInfo.getMachineId()
2015-04-22 07:34:56 +10:00
# User is identified from this point
# Attach authenticated header to the session
verify = None
cert = None
header = self.getHeader()
# If user enabled host certificate verification
try:
verify = self.sslverify
cert = self.sslclient
except:
self.logMsg("Could not load SSL settings.", 1)
2015-04-22 07:34:56 +10:00
# Start session
self.s = requests.Session()
self.s.headers = header
self.s.verify = verify
self.s.cert = cert
2015-04-22 07:34:56 +10:00
# 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)
2015-03-14 08:24:59 +11:00
def stopSession(self):
try:
self.s.close()
except:
self.logMsg("Requests session could not be terminated.", 1)
2015-04-22 07:34:56 +10:00
def getHeader(self, authenticate=True):
clientInfo = self.clientInfo
deviceName = clientInfo.getDeviceName()
deviceId = clientInfo.getMachineId()
2015-03-14 08:24:59 +11:00
version = clientInfo.getVersion()
2015-04-22 07:34:56 +10:00
if not authenticate:
# If user is not authenticated
auth = 'MediaBrowser Client="Kodi", Device="%s", DeviceId="%s", Version="%s"' % (deviceName, deviceId, version)
header = {'Content-type': 'application/json', 'Accept-encoding': 'gzip', 'Accept-Charset': 'UTF-8,*', 'Authorization': auth}
2015-04-22 07:34:56 +10:00
self.logMsg("Header: %s" % header, 2)
return header
2015-03-14 08:24:59 +11:00
else:
2015-04-22 07:34:56 +10:00
userId = self.userId
token = self.token
# Attached to the requests session
auth = 'MediaBrowser UserId="%s", Client="Kodi", Device="%s", DeviceId="%s", Version="%s"' % (userId, deviceName, deviceId, version)
header = {'Content-type': 'application/json', 'Accept-encoding': 'gzip', 'Accept-Charset': 'UTF-8,*', 'Authorization': auth, 'X-MediaBrowser-Token': token}
2015-04-22 07:34:56 +10:00
self.logMsg("Header: %s" % header, 2)
return header
2015-04-22 07:34:56 +10:00
def downloadUrl(self, url, postBody=None, type="GET", authenticate=True):
2015-04-22 07:34:56 +10:00
self.logMsg("=== ENTER downloadUrl ===", 2)
WINDOW = self.WINDOW
2015-04-22 07:34:56 +10:00
timeout = self.timeout
default_link = ""
2015-05-03 15:27:43 +10:00
try:
2015-05-03 15:27:43 +10:00
# If user is authenticated
if (authenticate):
# Get requests session
try:
s = self.s
# Replace for the real values and append api_key
url = url.replace("{server}", self.server, 1)
url = url.replace("{UserId}", self.userId, 1)
2015-05-03 15:27:43 +10:00
self.logMsg("URL: %s" % url, 2)
2015-05-03 15:27:43 +10:00
# Prepare request
if type == "GET":
r = s.get(url, json=postBody, timeout=timeout)
2015-05-03 15:27:43 +10:00
elif type == "POST":
r = s.post(url, json=postBody, timeout=timeout)
elif type == "DELETE":
r = s.delete(url, json=postBody, timeout=timeout)
except AttributeError:
2015-05-03 15:27:43 +10:00
# Get user information
self.username = WINDOW.getProperty('currUser')
self.userId = WINDOW.getProperty('userId%s' % self.username)
self.server = WINDOW.getProperty('server%s' % self.username)
self.token = WINDOW.getProperty('accessToken%s' % self.username)
2015-05-03 15:27:43 +10:00
header = self.getHeader()
verifyssl = False
cert = None
# IF user enables ssl verification
try:
if utils.settings('sslverify') == "true":
verifyssl = True
if utils.settings('sslcert') != "None":
cert = utils.settings('sslcert')
except:
self.logMsg("Could not load SSL settings.", 1)
pass
2015-05-03 15:27:43 +10:00
# Replace for the real values and append api_key
url = url.replace("{server}", self.server, 1)
url = url.replace("{UserId}", self.userId, 1)
2015-05-03 15:27:43 +10:00
self.logMsg("URL: %s" % url, 2)
2015-05-03 15:27:43 +10:00
# Prepare request
if type == "GET":
r = requests.get(url, json=postBody, headers=header, timeout=timeout, cert=cert, verify=verifyssl)
2015-05-03 15:27:43 +10:00
elif type == "POST":
r = requests.post(url, json=postBody, headers=header, timeout=timeout, cert=cert, verify=verifyssl)
2015-05-03 15:27:43 +10:00
elif type == "DELETE":
r = requests.delete(url, json=postBody, headers=header, timeout=timeout, cert=cert, verify=verifyssl)
2015-05-03 15:27:43 +10:00
# If user is not authenticated
elif not authenticate:
self.logMsg("URL: %s" % url, 2)
2015-05-03 15:27:43 +10:00
header = self.getHeader(authenticate=False)
verifyssl = False
2015-05-03 15:27:43 +10:00
# If user enables ssl verification
try:
2015-05-03 15:27:43 +10:00
verifyssl = self.sslverify
except AttributeError:
pass
2015-05-03 15:27:43 +10:00
# Prepare request
if type == "GET":
r = requests.get(url, json=postBody, headers=header, timeout=timeout, verify=verifyssl)
elif type == "POST":
r = requests.post(url, json=postBody, headers=header, timeout=timeout, verify=verifyssl)
# Process the response
2015-04-22 07:34:56 +10:00
if r.status_code == 204:
2015-05-03 15:27:43 +10:00
# No body in the response
self.logMsg("====== 204 Success ======", 2)
return default_link
2015-05-03 15:27:43 +10:00
2015-04-22 07:34:56 +10:00
elif r.status_code == requests.codes.ok:
2015-03-14 08:24:59 +11:00
try:
2015-04-22 07:34:56 +10:00
# UTF-8 - JSON object
r = r.json()
self.logMsg("====== 200 Success ======", 2)
self.logMsg("Response: %s" % r, 2)
2015-04-22 07:34:56 +10:00
return r
except:
if r.headers.get('content-type') == "text/html":
pass
else:
self.logMsg("Unable to convert the response for: %s" % url, 1)
2015-05-03 15:27:43 +10:00
else:
r.raise_for_status()
return default_link
# TO REVIEW EXCEPTIONS
2015-04-22 07:34:56 +10:00
except requests.exceptions.ConnectionError as e:
2015-05-03 15:27:43 +10:00
# Make the addon aware of status
if WINDOW.getProperty("Server_online") != "false":
2015-05-03 15:27:43 +10:00
self.logMsg("Server unreachable at: %s" % url, 0)
self.logMsg(e, 2)
WINDOW.setProperty("Server_online", "false")
pass
2015-03-14 08:24:59 +11:00
2015-04-22 07:34:56 +10:00
except requests.exceptions.ConnectTimeout as e:
self.logMsg("Server timeout at: %s" % url, 0)
self.logMsg(e, 1)
2015-04-22 07:34:56 +10:00
except requests.exceptions.HTTPError as e:
if r.status_code == 401:
2015-04-22 07:34:56 +10:00
# Unauthorized
status = WINDOW.getProperty("Server_status")
if 'x-application-error-code' in r.headers:
if r.headers['X-Application-Error-Code'] == "ParentalControl":
# Parental control - access restricted
WINDOW.setProperty("Server_status", "restricted")
xbmcgui.Dialog().notification("Emby server", "Access restricted.", xbmcgui.NOTIFICATION_ERROR, time=5000)
return False
2015-08-31 11:17:09 +10:00
elif r.headers['X-Application-Error-Code'] == "UnauthorizedAccessException":
# User tried to do something his emby account doesn't allow - admin restricted in some way
2015-08-31 11:17:09 +10:00
pass
elif (status == "401") or (status == "Auth"):
pass
else:
# Tell UserClient token has been revoked.
WINDOW.setProperty("Server_status", "401")
2015-04-22 07:34:56 +10:00
self.logMsg("HTTP Error: %s" % e, 0)
xbmcgui.Dialog().notification("Error connecting", "Unauthorized.", xbmcgui.NOTIFICATION_ERROR)
return 401
2015-04-22 07:34:56 +10:00
elif (r.status_code == 301) or (r.status_code == 302):
# Redirects
pass
elif r.status_code == 400:
# Bad requests
2015-03-14 08:24:59 +11:00
pass
except requests.exceptions.SSLError as e:
self.logMsg("Invalid SSL certificate for: %s" % url, 0)
self.logMsg(e, 1)
2015-04-22 07:34:56 +10:00
except requests.exceptions.RequestException as e:
self.logMsg("Unknown error connecting to: %s" % url, 0)
self.logMsg(e, 1)
2015-04-22 07:34:56 +10:00
return default_link