Added Requests

This commit is contained in:
angelblue05 2015-04-21 16:34:56 -05:00
parent ebe03057b0
commit 079fd4e8d9

View file

@ -1,341 +1,225 @@
import xbmc import xbmc
import xbmcgui import xbmcgui
import xbmcaddon import xbmcaddon
import urllib
import urllib2 import requests
import httplib import json
import hashlib import logging
import StringIO
import gzip import Utils as utils
import sys
import inspect
import json as json
from random import randrange
from uuid import uuid4 as uuid4
from ClientInformation import ClientInformation from ClientInformation import ClientInformation
import encodings
import time # Disable requests logging
import traceback logging.getLogger("requests").setLevel(logging.WARNING)
class DownloadUtils(): class DownloadUtils():
WINDOW = xbmcgui.Window(10000) # Borg - multiple instances, shared state
logLevel = 0 _shared_state = {}
addonSettings = None
getString = None
LogCalls = False
TrackLog = ""
TotalUrlCalls = 0
def __init__(self, *args):
addonId = ClientInformation().getAddonId()
self.addonSettings = xbmcaddon.Addon(id=addonId)
self.addon = xbmcaddon.Addon(id=addonId)
self.getString = self.addonSettings.getLocalizedString
level = self.addonSettings.getSetting('logLevel')
self.logLevel = 0
if(level != None and level != ""):
self.logLevel = int(level)
if(self.logLevel == 2):
self.LogCalls = True
def logMsg(self, msg, level = 1):
if(self.logLevel >= level):
try:
xbmc.log("emby DownloadUtils -> " + str(msg))
except UnicodeEncodeError:
try:
xbmc.log("emby DownloadUtils -> " + str(msg.encode('utf-8')))
except: pass
def getServer(self, prefix=True):
WINDOW = self.WINDOW
username = WINDOW.getProperty("currUser")
if prefix:
server = WINDOW.getProperty("server%s" % username)
else:
server = WINDOW.getProperty("server_%s" % username)
return server
def getUserId(self, suppress=True):
WINDOW = xbmcgui.Window( 10000 )
self.addonSettings = xbmcaddon.Addon(id='plugin.video.emby')
port = self.addonSettings.getSetting('port')
host = self.addonSettings.getSetting('ipaddress')
userName = self.addonSettings.getSetting('username')
userid = WINDOW.getProperty("userid" + userName)
if(userid != None and userid != ""):
self.logMsg("DownloadUtils -> Returning saved (WINDOW) UserID : " + userid + "UserName: " + userName,2)
return userid
userid = self.addonSettings.getSetting("userid" + userName)
if(userid != None and userid != ""):
WINDOW.setProperty("userid" + userName, userid)
self.logMsg("DownloadUtils -> Returning saved (SETTING) UserID : " + userid + "UserName: " + userName,2)
return userid
self.logMsg("Looking for user name: " + userName)
authOk = self.authenticate()
if(authOk == ""):
if(suppress == False):
xbmcgui.Dialog().ok(self.getString(30044), self.getString(30044))
return ""
userid = WINDOW.getProperty("userid" + userName)
if(userid == "" and suppress == False):
xbmcgui.Dialog().ok(self.getString(30045),self.getString(30045))
self.logMsg("userid : " + userid)
self.postcapabilities()
return userid
def postcapabilities(self):
self.logMsg("postcapabilities called")
# Set Capabilities
server = self.getServer()
clientInfo = ClientInformation() clientInfo = ClientInformation()
machineId = clientInfo.getMachineId()
# get session id addonName = clientInfo.getAddonName()
url = "%s/mediabrowser/Sessions?DeviceId=%s&format=json" % (server, machineId) addonId = clientInfo.getAddonId()
self.logMsg("Session URL : " + url); addon = xbmcaddon.Addon(id=addonId)
jsonData = self.downloadUrl(url) WINDOW = xbmcgui.Window(10000)
self.logMsg("Session JsonData : " + jsonData)
result = json.loads(jsonData)
self.logMsg("Session JsonData : " + str(result))
sessionId = result[0].get("Id")
self.logMsg("Session Id : " + str(sessionId))
# post capability data # Requests session
#playableMediaTypes = "Audio,Video,Photo" s = None
timeout = 30
def __init__(self):
self.__dict__ = self._shared_state
self.className = self.__class__.__name__
def logMsg(self, msg, lvl=1):
utils.logMsg("%s %s" % (self.addonName, self.className), str(msg), int(lvl))
def setUsername(self, username):
# Reserved for UserClient only
self.username = username
self.logMsg("Set username: %s" % username, 1)
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 postCapabilities(self, deviceId):
# Get sessionId
url = "{server}/mediabrowser/Sessions?DeviceId=%s&format=json" % deviceId
result = self.downloadUrl(url)
# sessionId result
self.logMsg("Session result: %s" % result, 1)
self.sessionId = result[0][u'Id']
# Settings for capabilities
playableMediaTypes = "Audio,Video" playableMediaTypes = "Audio,Video"
#supportedCommands = "Play,Playstate,DisplayContent,GoHome,SendString,GoToSettings,DisplayMessage,PlayNext"
supportedCommands = "Play,Playstate,SendString,DisplayMessage,PlayNext" supportedCommands = "Play,Playstate,SendString,DisplayMessage,PlayNext"
url = "%s/mediabrowser/Sessions/Capabilities?Id=%s&PlayableMediaTypes=%s&SupportedCommands=%s&SupportsMediaControl=True" % (server, sessionId, playableMediaTypes, supportedCommands) # Post settings to sessionId
postData = {} url = "{server}/mediabrowser/Sessions/Capabilities?Id=%s&PlayableMediaTypes=%s&SupportedCommands=%s&SupportsMediaControl=True" % (self.sessionId, playableMediaTypes, supportedCommands)
#postData["Id"] = sessionId; data = {}
#postData["PlayableMediaTypes"] = "Video"; self.logMsg("Capabilities URL: %s" % url, 2)
#postData["SupportedCommands"] = "MoveUp"; self.logMsg("PostData: %s" % data, 2)
stringdata = json.dumps(postData)
self.logMsg("Capabilities URL : " + url);
self.logMsg("Capabilities Data : " + stringdata)
self.downloadUrl(url, postBody=stringdata, type="POST") self.downloadUrl(url, postBody=data, type="POST")
self.logMsg("Posted capabilities to sessionId: %s" % self.sessionId, 1)
def startSession(self):
self.deviceId = self.clientInfo.getMachineId()
# User is identified from this point
# Attach authenticated header to the session
cert = None
header = self.getHeader()
if self.addon.getSetting('sslcert') != "None":
# If user uses HTTPS and has a custom client certificate
cert = self.addon.getSetting('sslcert')
# Start session
self.s = requests.Session()
self.s.headers.update(header)
self.s.cert = cert
# 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)
self.postCapabilities(self.deviceId)
def imageUrl(self, id, type, index, width, height): def imageUrl(self, id, type, index, width, height):
# To move to API.py
return "%s/mediabrowser/Items/%s/Images/%s?MaxWidth=%s&MaxHeight=%s&Index=%s" % (self.server, id, type, width, height, index)
server = self.getServer() def getHeader(self, authenticate=True):
return "%s/mediabrowser/Items/%s/Images/%s?MaxWidth=%s&MaxHeight=%s&Index=%s" % (server, id, type, width, height, index) clientInfo = self.clientInfo
def getAuthHeader(self, authenticate=True): deviceName = clientInfo.getDeviceName()
clientInfo = ClientInformation() deviceId = clientInfo.getMachineId()
txt_mac = clientInfo.getMachineId()
version = clientInfo.getVersion() version = clientInfo.getVersion()
deviceName = self.addonSettings.getSetting('deviceName') if not authenticate:
deviceName = deviceName.replace("\"", "_") # If user is not authenticated
username = self.WINDOW.getProperty("currUser") auth = 'MediaBrowser Client="Kodi", Device="%s", DeviceId="%s", Version="%s"' % (deviceName, deviceId, version)
header = {"Accept-encoding": "gzip", "Accept-Charset": "UTF-8,*", "Authorization": auth}
self.logMsg("Header: %s" % header, 2)
return header
if(authenticate == False):
authString = "MediaBrowser Client=\"Kodi\",Device=\"" + deviceName + "\",DeviceId=\"" + txt_mac + "\",Version=\"" + version + "\""
headers = {"Accept-encoding": "gzip", "Accept-Charset" : "UTF-8,*", "Authorization" : authString}
return headers
else: else:
userid = self.getUserId() userId = self.userId
authString = "MediaBrowser UserId=\"" + userid + "\",Client=\"Kodi\",Device=\"" + deviceName + "\",DeviceId=\"" + txt_mac + "\",Version=\"" + version + "\"" token = self.token
headers = {"Accept-encoding": "gzip", "Accept-Charset" : "UTF-8,*", "Authorization" : authString} # Attached to the requests session
auth = 'MediaBrowser UserId="%s", Client="Kodi", Device="%s", DeviceId="%s", Version="%s"' % (userId, deviceName, deviceId, version)
header = {"Accept-encoding": "gzip", "Accept-Charset": "UTF-8,*", "Authorization": auth, "X-MediaBrowser-Token": token}
authToken = self.WINDOW.getProperty("accessToken%s" % username) self.logMsg("Header: %s" % header, 2)
if(authToken != ""): return header
headers["X-MediaBrowser-Token"] = authToken
self.logMsg("Authentication Header : " + str(headers),2) def downloadUrl(self, url, postBody=None, type="GET", authenticate=True):
return headers
def downloadUrl(self, url, suppress=False, postBody=None, type="GET", popup=0, authenticate=True ): self.logMsg("=== ENTER downloadUrl ===", 2)
self.logMsg("== ENTER: getURL ==",2)
if(authenticate == True and suppress == True): WINDOW = self.WINDOW
token = self.authenticate(retreive=False) timeout = self.timeout
if(token == ""): default_link = ""
self.logMsg("No auth info set and suppress is true so returning no data!")
return ""
self.TotalUrlCalls = self.TotalUrlCalls + 1 # If user is authenticated
if(self.LogCalls): if (authenticate):
stackString = "" # Get requests session
for f in inspect.stack(): s = self.s
stackString = stackString + "\r - " + str(f) # Replace for the real values and append api_key
self.TrackLog = self.TrackLog + "HTTP_API_CALL : " + url + stackString + "\r" url = url.replace("{server}", self.server, 1)
url = url.replace("{UserId}", self.userId, 1)
url = "%s&api_key=%s" % (url, self.token)
link = "" self.logMsg("URL: %s" % url, 1)
https = None # Prepare request
if type == "GET":
r = s.get(url, params=postBody, timeout=timeout)
elif type == "POST":
r = s.post(url, params=postBody, timeout=timeout)
elif type == "DELETE":
r = s.delete(url, params=postBody, timeout=timeout)
# If user is not authenticated
elif not authenticate:
self.logMsg("URL: %s" % url, 1)
header = self.getHeader(authenticate=False)
# Prepare request
if type == "GET":
r = requests.get(url, params=postBody, headers=header, timeout=timeout, verify=False)
elif type == "POST":
r = requests.post(url, params=postBody, headers=header, timeout=timeout)
# Process the response
try: try:
if url[0:5] == "https": r.raise_for_status()
serversplit = 2
urlsplit = 3
elif url[0:4] == "http":
serversplit = 2
urlsplit = 3
else:
serversplit = 0
urlsplit = 1
https = self.addonSettings.getSetting('https') if r.status_code == 204:
# No response in body
server = url.split('/')[serversplit] self.logMsg("====== 204 Success ======", 1)
urlPath = "/"+"/".join(url.split('/')[urlsplit:]) return default_link
# Response code 200
self.logMsg("DOWNLOAD_URL = " + url,2) elif r.status_code == requests.codes.ok:
self.logMsg("server = " + str(server),2)
self.logMsg("urlPath = " + str(urlPath),2)
if(server[0:1] == ":" or server[-1:] == ":"):
self.logMsg("No server host or port set in url")
return ""
head = self.getAuthHeader(authenticate)
self.logMsg("HEADERS : " + str(head), level=2)
if (https == 'false'):
#xbmc.log("Https disabled.")
conn = httplib.HTTPConnection(server, timeout=30)
elif (https == 'true'):
#xbmc.log("Https enabled.")
conn = httplib.HTTPSConnection(server, timeout=30)
# make the connection and send the request
if(postBody != None):
head["Content-Type"] = "application/x-www-form-urlencoded"
head["Content-Length"] = str(len(postBody))
self.logMsg("POST DATA : " + postBody,2)
conn.request(method=type, url=urlPath, body=postBody, headers=head)
else:
conn.request(method=type, url=urlPath, headers=head)
# get the response
tries = 0
while tries <= 4:
try: try:
data = conn.getresponse() # UTF-8 - JSON object
break r = r.json()
self.logMsg("====== 200 Success ======", 1)
return r
except: except:
# TODO: we need to work out which errors we can just quit trying immediately self.logMsg("Unable to convert the response for: %s" % url, 1)
if(xbmc.abortRequested == True):
return ""
xbmc.sleep(100)
if(xbmc.abortRequested == True):
return ""
tries += 1
if tries == 5:
data = conn.getresponse()
self.logMsg("GET URL HEADERS : " + str(data.getheaders()), level=2) return default_link
# process the response # TO REVIEW EXCEPTIONS
contentType = "none" except requests.exceptions.ConnectionError as e:
if int(data.status) == 200: self.logMsg("Server unreachable at: %s" % url, 0)
retData = data.read() self.logMsg(e, 1)
contentType = data.getheader('content-encoding')
self.logMsg("Data Len Before : " + str(len(retData)), level=2)
if(contentType == "gzip"):
retData = StringIO.StringIO(retData)
gzipper = gzip.GzipFile(fileobj=retData)
link = gzipper.read()
else:
link = retData
self.logMsg("Data Len After : " + str(len(link)), level=2)
self.logMsg("====== 200 returned =======", level=2)
self.logMsg("Content-Type : " + str(contentType), level=2)
self.logMsg(link, level=2)
self.logMsg("====== 200 finished ======", level=2)
elif ( int(data.status) == 301 ) or ( int(data.status) == 302 ): except requests.exceptions.ConnectTimeout as e:
try: self.logMsg("Server timeout at: %s" % url, 0)
conn.close() self.logMsg(e, 1)
except:
pass
return data.getheader('Location')
elif int(data.status) == 401: except requests.exceptions.HTTPError as e:
WINDOW = xbmcgui.Window(10000)
if r.status_code == 401:
# Unauthorized
status = WINDOW.getProperty("Server_status") status = WINDOW.getProperty("Server_status")
# Prevent multiple re-auth
if (status == "401") or (status == "Auth"): if (status == "401") or (status == "Auth"):
pass pass
else: else:
# Tell UserClient token has been revoked. # Tell UserClient token has been revoked.
WINDOW.setProperty("Server_status", "401") WINDOW.setProperty("Server_status", "401")
error = "HTTP response error: " + str(data.status) + " " + str(data.reason) self.logMsg("HTTP Error: %s" % e, 0)
xbmc.log(error)
#xbmcgui.Dialog().ok(self.getString(30135),"Reason: %s" % data.reason) #self.getString(30044),
try: elif (r.status_code == 301) or (r.status_code == 302):
conn.close() # Redirects
except:
pass pass
return "" elif r.status_code == 400:
# Bad requests
elif int(data.status) >= 400:
error = "HTTP response error: " + str(data.status) + " " + str(data.reason)
xbmc.log(error)
stack = self.FormatException()
self.logMsg(stack)
if suppress is False:
if popup == 0:
xbmc.executebuiltin("XBMC.Notification(URL error: "+ str(data.reason) +",)")
else:
xbmcgui.Dialog().ok(self.getString(30135),server)
try:
conn.close()
except:
pass
return ""
else:
link = ""
except Exception, msg:
error = "Unable to connect to " + str(server) + " : " + str(msg)
xbmc.log(error)
stack = self.FormatException()
self.logMsg(stack)
if suppress is False:
if popup == 0:
xbmc.executebuiltin("XBMC.Notification(: Connection Error: Error connecting to server,)")
else:
xbmcgui.Dialog().ok(self.getString(30204), str(msg))
pass
else:
try:
conn.close()
except:
pass pass
return link except requests.exceptions.RequestException as e:
self.logMsg("Unknown error connecting to: %s" % url, 0)
self.logMsg(e, 1)
def FormatException(self): return default_link
exception_list = traceback.format_stack()
exception_list = exception_list[:-2]
exception_list.extend(traceback.format_tb(sys.exc_info()[2]))
exception_list.extend(traceback.format_exception_only(sys.exc_info()[0], sys.exc_info()[1]))
exception_str = "Traceback (most recent call last):\n"
exception_str += "".join(exception_list)
# Removing the last \n
exception_str = exception_str[:-1]
return exception_str
def __del__(self):
return
# xbmc.log("\rURL_REQUEST_REPORT : Total Calls : " + str(self.TotalUrlCalls) + "\r" + self.TrackLog)