PlexKodiConnect/resources/lib/DownloadUtils.py

348 lines
13 KiB
Python
Raw Normal View History

2015-03-14 08:24:59 +11:00
import xbmc
import xbmcgui
import xbmcaddon
2015-04-22 07:34:56 +10:00
import requests
import json
import logging
import Utils as utils
2015-03-14 08:24:59 +11:00
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-03-14 08:24:59 +11:00
2015-04-22 07:34:56 +10:00
addonName = clientInfo.getAddonName()
addon = xbmcaddon.Addon()
2015-04-03 21:13:01 +11:00
WINDOW = xbmcgui.Window(10000)
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
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
self.userId = userId
self.logMsg("Set userId: %s" % userId, 2)
def setServer(self, server):
# Reserved for UserClient only
self.server = server
self.logMsg("Set server: %s" % server, 2)
def setToken(self, token):
# Reserved for UserClient only
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"
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-05-03 12:28:24 +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()
# 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-03-14 08:24:59 +11:00
2015-04-22 07:34:56 +10:00
self.logMsg("Header: %s" % header, 2)
return header
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)
self.logMsg("URL: %s" % url, 2)
# Prepare request
if type == "GET":
r = s.get(url, json=postBody, timeout=timeout)
elif type == "POST":
r = s.post(url, json=postBody, timeout=timeout)
elif type == "DELETE":
r = s.delete(url, json=postBody, timeout=timeout)
except AttributeError:
# 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)
header = self.getHeader()
verifyssl = False
cert = None
# IF user enables ssl verification
try:
if utils.settings('sslverify') == "true":
2015-05-03 15:27:43 +10:00
verifyssl = True
if utils.settings('sslcert') != "None":
cert = utils.settings('sslcert')
2015-05-03 15:27:43 +10:00
except:
self.logMsg("Could not load SSL settings.", 1)
pass
# Replace for the real values and append api_key
url = url.replace("{server}", self.server, 1)
url = url.replace("{UserId}", self.userId, 1)
self.logMsg("URL: %s" % url, 2)
# Prepare request
if type == "GET":
r = requests.get(url, json=postBody, headers=header, timeout=timeout, cert=cert, verify=verifyssl)
elif type == "POST":
r = requests.post(url, json=postBody, headers=header, timeout=timeout, cert=cert, verify=verifyssl)
elif type == "DELETE":
r = requests.delete(url, json=postBody, headers=header, timeout=timeout, cert=cert, verify=verifyssl)
# If user is not authenticated
elif not authenticate:
2015-05-03 15:27:43 +10:00
self.logMsg("URL: %s" % url, 2)
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":
2015-05-03 15:27:43 +10:00
r = requests.get(url, json=postBody, headers=header, timeout=timeout, verify=verifyssl)
elif type == "POST":
2015-05-03 15:27:43 +10:00
r = requests.post(url, json=postBody, headers=header, timeout=timeout, verify=verifyssl)
2015-05-03 15:27:43 +10:00
# 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)
2015-04-22 07:34:56 +10:00
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()
2015-04-22 07:34:56 +10:00
return default_link
# TO REVIEW EXCEPTIONS
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":
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
2015-04-03 21:13:01 +11:00
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")
2015-08-31 11:17:09 +10:00
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
pass
2015-08-31 11:17:09 +10:00
elif (status == "401") or (status == "Auth"):
2015-03-14 08:24:59 +11:00
pass
2015-04-03 21:13:01 +11:00
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)
2015-08-31 11:17:09 +10:00
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