jellyfin-kodi/resources/lib/downloadutils.py

359 lines
12 KiB
Python
Raw Normal View History

2015-12-24 20:07:00 +00:00
# -*- coding: utf-8 -*-
##################################################################################################
import json
import requests
import logging
import xbmc
import xbmcgui
import clientinfo
from utils import window, settings
2015-12-24 20:07:00 +00:00
##################################################################################################
# Disable requests logging
2016-06-18 03:03:28 +00:00
from requests.packages.urllib3.exceptions import InsecureRequestWarning, InsecurePlatformWarning
2015-12-24 20:07:00 +00:00
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
2016-06-18 03:03:28 +00:00
requests.packages.urllib3.disable_warnings(InsecurePlatformWarning)
log = logging.getLogger("EMBY."+__name__)
2015-12-24 20:07:00 +00:00
##################################################################################################
class DownloadUtils():
2016-03-31 03:37:37 +00:00
2015-12-24 20:07:00 +00:00
# Borg - multiple instances, shared state
_shared_state = {}
clientInfo = clientinfo.ClientInfo()
# Requests session
s = None
default_timeout = 30
2015-12-24 20:07:00 +00:00
def __init__(self):
2016-06-18 03:03:28 +00:00
self.__dict__ = self._shared_state
2015-12-24 20:07:00 +00:00
def setUsername(self, username):
# Reserved for userclient only
self.username = username
log.debug("Set username: %s" % username)
2015-12-24 20:07:00 +00:00
def setUserId(self, userId):
# Reserved for userclient only
self.userId = userId
log.debug("Set userId: %s" % userId)
2015-12-24 20:07:00 +00:00
def setServer(self, server):
# Reserved for userclient only
self.server = server
log.debug("Set server: %s" % server)
2015-12-24 20:07:00 +00:00
def setToken(self, token):
# Reserved for userclient only
self.token = token
log.debug("Set token: %s" % token)
2015-12-24 20:07:00 +00:00
def setSSL(self, ssl, sslclient):
# Reserved for userclient only
self.sslverify = ssl
self.sslclient = sslclient
log.debug("Verify SSL host certificate: %s" % ssl)
log.debug("SSL client side certificate: %s" % sslclient)
2015-12-24 20:07:00 +00:00
def postCapabilities(self, deviceId):
# Post settings to session
url = "{server}/emby/Sessions/Capabilities/Full?format=json"
data = {
2016-03-31 03:37:37 +00:00
2015-12-24 20:07:00 +00:00
'PlayableMediaTypes': "Audio,Video",
'SupportsMediaControl': True,
'SupportedCommands': (
2016-03-31 03:37:37 +00:00
2015-12-24 20:07:00 +00:00
"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"
)
}
log.debug("Capabilities URL: %s" % url)
log.debug("Postdata: %s" % data)
2015-12-24 20:07:00 +00:00
2016-04-04 21:21:05 +00:00
self.downloadUrl(url, postBody=data, action_type="POST")
log.debug("Posted capabilities to %s" % self.server)
2015-12-24 20:07:00 +00:00
# Attempt at getting sessionId
url = "{server}/emby/Sessions?DeviceId=%s&format=json" % deviceId
result = self.downloadUrl(url)
try:
sessionId = result[0]['Id']
2016-03-31 03:37:37 +00:00
2015-12-24 20:07:00 +00:00
except (KeyError, TypeError):
log.info("Failed to retrieve sessionId.")
2016-03-31 03:37:37 +00:00
2015-12-24 20:07:00 +00:00
else:
log.debug("Session: %s" % result)
log.info("SessionId: %s" % sessionId)
2016-06-18 03:03:28 +00:00
window('emby_sessionId', value=sessionId)
2016-03-31 03:37:37 +00:00
2015-12-24 20:07:00 +00:00
# Post any permanent additional users
2016-06-18 03:03:28 +00:00
additionalUsers = settings('additionalUsers')
2015-12-24 20:07:00 +00:00
if additionalUsers:
2016-03-31 03:37:37 +00:00
2015-12-24 20:07:00 +00:00
additionalUsers = additionalUsers.split(',')
log.info("List of permanent users added to the session: %s" % additionalUsers)
2015-12-24 20:07:00 +00:00
# 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)
)
2016-04-04 21:21:05 +00:00
self.downloadUrl(url, postBody={}, action_type="POST")
2015-12-24 20:07:00 +00:00
def startSession(self):
self.deviceId = self.clientInfo.getDeviceId()
# User is identified from this point
# Attach authenticated header to the session
2016-02-25 04:45:25 +00:00
verify = False
2015-12-24 20:07:00 +00:00
header = self.getHeader()
# If user enabled host certificate verification
try:
verify = self.sslverify
2016-02-25 04:45:25 +00:00
if self.sslclient is not None:
verify = self.sslclient
2015-12-24 20:07:00 +00:00
except:
log.info("Could not load SSL settings.")
2016-03-31 03:37:37 +00:00
2015-12-24 20:07:00 +00:00
# Start session
self.s = requests.Session()
self.s.headers = header
self.s.verify = verify
# 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))
log.info("Requests session started on: %s" % self.server)
2015-12-24 20:07:00 +00:00
def stopSession(self):
try:
self.s.close()
except Exception:
log.warn("Requests session could not be terminated.")
2015-12-24 20:07:00 +00:00
def getHeader(self, authenticate=True):
2016-03-31 22:42:49 +00:00
deviceName = self.clientInfo.getDeviceName()
2016-06-18 03:03:28 +00:00
deviceName = deviceName.encode('utf-8')
2016-03-31 22:42:49 +00:00
deviceId = self.clientInfo.getDeviceId()
version = self.clientInfo.getVersion()
2015-12-24 20:07:00 +00:00
if authenticate:
2015-12-24 20:07:00 +00:00
auth = (
'MediaBrowser UserId="%s", Client="Kodi", Device="%s", DeviceId="%s", Version="%s"'
% (self.userId, deviceName, deviceId, version))
2015-12-24 20:07:00 +00:00
header = {
'Content-type': 'application/json',
'Accept-encoding': 'gzip',
'Accept-Charset': 'UTF-8,*',
'Authorization': auth,
'X-MediaBrowser-Token': self.token
2016-03-31 03:37:37 +00:00
}
2015-12-24 20:07:00 +00:00
else:
# If user is not authenticated
2015-12-24 20:07:00 +00:00
auth = (
'MediaBrowser Client="Kodi", Device="%s", DeviceId="%s", Version="%s"'
% (deviceName, deviceId, version))
2015-12-24 20:07:00 +00:00
header = {
'Content-type': 'application/json',
'Accept-encoding': 'gzip',
'Accept-Charset': 'UTF-8,*',
'Authorization': auth
2016-03-31 03:37:37 +00:00
}
2015-12-24 20:07:00 +00:00
return header
2016-06-18 03:03:28 +00:00
def downloadUrl(self, url, postBody=None, action_type="GET", parameters=None,
authenticate=True):
2016-03-31 03:37:37 +00:00
log.debug("===== ENTER downloadUrl =====")
session = requests
2016-07-16 00:02:30 +00:00
kwargs = {}
2015-12-24 20:07:00 +00:00
default_link = ""
try:
2016-06-18 03:03:28 +00:00
if authenticate:
2016-03-31 03:37:37 +00:00
if self.s is not None:
session = self.s
else:
2015-12-24 20:07:00 +00:00
# request session does not exists
# Get user information
2016-06-18 03:03:28 +00:00
self.userId = window('emby_currUser')
self.server = window('emby_server%s' % self.userId)
self.token = window('emby_accessToken%s' % self.userId)
2015-12-24 20:07:00 +00:00
verifyssl = False
# IF user enables ssl verification
2016-06-18 03:03:28 +00:00
if settings('sslverify') == "true":
2015-12-24 20:07:00 +00:00
verifyssl = True
2016-06-18 03:03:28 +00:00
if settings('sslcert') != "None":
verifyssl = settings('sslcert')
2015-12-24 20:07:00 +00:00
2016-07-16 00:02:30 +00:00
kwargs.update({
'verify': verifyssl,
'headers': self.getHeader()
})
2015-12-24 20:07:00 +00:00
# Replace for the real values
url = url.replace("{server}", self.server)
url = url.replace("{UserId}", self.userId)
2015-12-24 20:07:00 +00:00
else: # User is not authenticated
2015-12-24 20:07:00 +00:00
# If user enables ssl verification
try:
verifyssl = self.sslverify
2016-02-25 04:45:25 +00:00
if self.sslclient is not None:
verifyssl = self.sslclient
2015-12-24 20:07:00 +00:00
except AttributeError:
verifyssl = False
2016-07-16 00:02:30 +00:00
kwargs.update({
'verify': verifyssl,
'headers': self.getHeader(authenticate=False)
})
##### PREPARE REQUEST #####
kwargs.update({
'url': url,
'timeout': self.default_timeout,
2016-07-16 00:02:30 +00:00
'json': postBody,
'params': parameters
})
2016-03-31 03:37:37 +00:00
2015-12-24 20:07:00 +00:00
##### THE RESPONSE #####
log.debug(kwargs)
r = self._requests(action_type, session, **kwargs)
2015-12-24 20:07:00 +00:00
if r.status_code == 204:
# No body in the response
log.debug("====== 204 Success ======")
# Read response to release connection
r.content
2015-12-24 20:07:00 +00:00
elif r.status_code == requests.codes.ok:
2016-03-31 03:37:37 +00:00
try:
# UNICODE - JSON object
2015-12-24 20:07:00 +00:00
r = r.json()
log.debug("====== 200 Success ======")
log.debug("Response: %s" % r)
2015-12-24 20:07:00 +00:00
return r
except:
if r.headers.get('content-type') != "text/html":
log.info("Unable to convert the response for: %s" % url)
else: # Bad status code
log.error("=== Bad status response: %s ===" % r.status_code)
2015-12-24 20:07:00 +00:00
r.raise_for_status()
2016-03-31 03:37:37 +00:00
2015-12-24 20:07:00 +00:00
##### EXCEPTIONS #####
except requests.exceptions.ConnectionError as e:
# Make the addon aware of status
2016-06-18 03:03:28 +00:00
if window('emby_online') != "false":
log.warn("Server unreachable at: %s" % url)
2016-06-18 03:03:28 +00:00
window('emby_online', value="false")
2015-12-24 20:07:00 +00:00
except requests.exceptions.ConnectTimeout as e:
log.warn("Server timeout at: %s" % url)
2015-12-24 20:07:00 +00:00
except requests.exceptions.HTTPError as e:
if r.status_code == 401:
# Unauthorized
2016-06-18 03:03:28 +00:00
status = window('emby_serverStatus')
2015-12-24 20:07:00 +00:00
if 'X-Application-Error-Code' in r.headers:
# Emby server errors
if r.headers['X-Application-Error-Code'] == "ParentalControl":
# Parental control - access restricted
2016-06-18 03:03:28 +00:00
window('emby_serverStatus', value="restricted")
2015-12-24 20:07:00 +00:00
xbmcgui.Dialog().notification(
heading="Emby server",
message="Access restricted.",
icon=xbmcgui.NOTIFICATION_ERROR,
time=5000)
return False
2016-03-31 03:37:37 +00:00
2015-12-24 20:07:00 +00:00
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.
2016-06-18 03:03:28 +00:00
window('emby_serverStatus', value="401")
log.warn("HTTP Error: %s" % e)
2015-12-24 20:07:00 +00:00
xbmcgui.Dialog().notification(
heading="Error connecting",
message="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:
log.warn("Invalid SSL certificate for: %s" % url)
2015-12-24 20:07:00 +00:00
except requests.exceptions.RequestException as e:
log.warn("Unknown error connecting to: %s" % url)
2015-12-24 20:07:00 +00:00
return default_link
def _requests(self, action, session=requests, **kwargs):
if action == "GET":
r = session.get(**kwargs)
elif action == "POST":
r = session.post(**kwargs)
elif action == "DELETE":
r = session.delete(**kwargs)
return r