diff --git a/resources/lib/PlexCompanion.py b/resources/lib/PlexCompanion.py
index 1a2b8f3d..9c4281f4 100644
--- a/resources/lib/PlexCompanion.py
+++ b/resources/lib/PlexCompanion.py
@@ -7,22 +7,25 @@ import xbmc
import clientinfo
import utils
-from plexbmchelper import listener, plexgdm, subscribers
-from plexbmchelper.settings import settings
+from plexbmchelper import listener, plexgdm, subscribers, functions, \
+ httppersist
@utils.logging
+@utils.ThreadMethodsAdditionalSuspend('emby_serverStatus')
@utils.ThreadMethods
class PlexCompanion(threading.Thread):
def __init__(self):
- self.port = int(utils.settings('companionPort'))
ci = clientinfo.ClientInfo()
self.clientId = ci.getDeviceId()
self.deviceName = ci.getDeviceName()
- self.logMsg("----===## Starting PlexBMC Helper ##===----", 1)
+
+ self.port = int(utils.settings('companionPort'))
+ self.logMsg("----===## Starting PlexCompanion ##===----", 1)
# Start GDM for server/client discovery
- self.client = plexgdm.plexgdm(debug=settings['gdm_debug'])
+ self.client = plexgdm.plexgdm(
+ debug=utils.settings('companionGDMDebugging'))
self.client.clientDetails(self.clientId, # UUID
self.deviceName, # clientName
self.port,
@@ -34,23 +37,38 @@ class PlexCompanion(threading.Thread):
threading.Thread.__init__(self)
def run(self):
+ # Cache for quicker while loops
+ log = self.logMsg
+ client = self.client
+ threadStopped = self.threadStopped
+ threadSuspended = self.threadSuspended
start_count = 0
- window = utils.window
+
+ # Start up instances
+ requestMgr = httppersist.RequestMgr()
+ jsonClass = functions.jsonClass(requestMgr)
+ subscriptionManager = subscribers.SubscriptionManager(
+ jsonClass, requestMgr)
+
+ # Start up httpd
while True:
try:
httpd = listener.ThreadedHTTPServer(
+ client,
+ subscriptionManager,
+ jsonClass,
('', self.port),
listener.MyHandler)
httpd.timeout = 0.95
break
except:
- self.logMsg("Unable to start PlexCompanion. Traceback:", -1)
- self.logMsg(traceback.print_exc(), -1)
+ log("Unable to start PlexCompanion. Traceback:", -1)
+ log(traceback.print_exc(), -1)
xbmc.sleep(3000)
if start_count == 3:
- self.logMsg("Error: Unable to start web helper.", -1)
+ log("Error: Unable to start web helper.", -1)
httpd = False
break
@@ -59,15 +77,15 @@ class PlexCompanion(threading.Thread):
if not httpd:
return
- self.client.start_all()
+ client.start_all()
+
message_count = 0
- is_running = False
- while not self.threadStopped():
+ while not threadStopped():
# If we are not authorized, sleep
# Otherwise, we trigger a download which leads to a
# re-authorizations
- while self.threadSuspended() or window('emby_serverStatus'):
- if self.threadStopped():
+ while threadSuspended():
+ if threadStopped():
break
xbmc.sleep(1000)
try:
@@ -76,31 +94,29 @@ class PlexCompanion(threading.Thread):
message_count += 1
if message_count > 100:
- if self.client.check_client_registration():
- self.logMsg("Client is still registered", 1)
+ if client.check_client_registration():
+ log("Client is still registered", 1)
else:
- self.logMsg("Client is no longer registered", 1)
- self.logMsg("Plex Companion still running on port %s"
- % self.port, 1)
+ log("Client is no longer registered", 1)
+ log("Plex Companion still running on port %s"
+ % self.port, 1)
message_count = 0
- if not is_running:
- self.logMsg("Plex Companion has started", 0)
- is_running = True
+ # Get and set servers
+ subscriptionManager.serverlist = client.getServerList()
- subscribers.subMgr.notify()
- settings['serverList'] = self.client.getServerList()
+ subscriptionManager.notify()
xbmc.sleep(50)
except:
- self.logMsg("Error in loop, continuing anyway", 1)
- self.logMsg(traceback.format_exc(), 1)
+ log("Error in loop, continuing anyway", 1)
+ log(traceback.format_exc(), 1)
xbmc.sleep(50)
- self.client.stop_all()
+ client.stop_all()
try:
httpd.socket.shutdown(socket.SHUT_RDWR)
except:
pass
finally:
httpd.socket.close()
- self.logMsg("----===## Plex Companion stopped ##===----", 0)
+ log("----===## Plex Companion stopped ##===----", 0)
diff --git a/resources/lib/plexbmchelper/functions.py b/resources/lib/plexbmchelper/functions.py
index 07ee7b72..2b00b109 100644
--- a/resources/lib/plexbmchelper/functions.py
+++ b/resources/lib/plexbmchelper/functions.py
@@ -1,26 +1,35 @@
import base64
-import inspect
import json
import string
-import traceback
import xbmc
-from settings import settings
-from httppersist import requests
+import settings
+from utils import logMsg
+
def xbmc_photo():
return "photo"
+
+
def xbmc_video():
return "video"
+
+
def xbmc_audio():
return "audio"
+
def plex_photo():
return "photo"
+
+
def plex_video():
return "video"
+
+
def plex_audio():
return "music"
+
def xbmc_type(plex_type):
if plex_type == plex_photo():
return xbmc_photo()
@@ -28,7 +37,8 @@ def xbmc_type(plex_type):
return xbmc_video()
elif plex_type == plex_audio():
return xbmc_audio()
-
+
+
def plex_type(xbmc_type):
if xbmc_type == xbmc_photo():
return plex_photo()
@@ -37,6 +47,7 @@ def plex_type(xbmc_type):
elif xbmc_type == xbmc_audio():
return plex_audio()
+
def getPlatform():
if xbmc.getCondVisibility('system.platform.osx'):
return "MacOSX"
@@ -50,140 +61,23 @@ def getPlatform():
return "RaspberryPi"
elif xbmc.getCondVisibility('system.platform.linux'):
return "Linux"
- elif xbmc.getCondVisibility('system.platform.android'):
+ elif xbmc.getCondVisibility('system.platform.android'):
return "Android"
return "Unknown"
-
-def printDebug( msg, functionname=True ):
- if settings['debug']:
- if functionname is False:
- print str(msg)
- else:
- print "PleXBMC Helper -> " + inspect.stack()[1][3] + ": " + str(msg)
-
-""" communicate with XBMC """
-def jsonrpc(action, arguments = {}):
- """ put some JSON together for the JSON-RPC APIv6 """
- if action.lower() == "sendkey":
- request=json.dumps({ "jsonrpc" : "2.0" , "method" : "Input.SendText", "params" : { "text" : self.arguments[0], "done" : False }} )
- elif action.lower() == "ping":
- request=json.dumps({ "jsonrpc" : "2.0",
- "id" : 1 ,
- "method" : "JSONRPC.Ping" })
- elif action.lower() == "playmedia":
- xbmc.Player().play("plugin://plugin.video.plexkodiconnect/"
- "?mode=companion&arguments=%s"
- % arguments)
- return True
- elif arguments:
- request=json.dumps({ "id" : 1,
- "jsonrpc" : "2.0",
- "method" : action,
- "params" : arguments})
- else:
- request=json.dumps({ "id" : 1,
- "jsonrpc" : "2.0",
- "method" : action})
-
- printDebug("Sending request to XBMC without network stack: %s" % request)
- result = parseJSONRPC(xbmc.executeJSONRPC(request))
- if not result and settings['webserver_enabled']:
- # xbmc.executeJSONRPC appears to fail on the login screen, but going
- # through the network stack works, so let's try the request again
- result = parseJSONRPC(requests.post(
- "127.0.0.1",
- settings['port'],
- "/jsonrpc",
- request,
- { 'Content-Type' : 'application/json',
- 'Authorization' : 'Basic ' + string.strip(base64.encodestring(settings['user'] + ':' + settings['passwd'])) }))
-
- return result
-
-
-
-def parseJSONRPC(jsonraw):
- if not jsonraw:
- printDebug("Empty response from XBMC")
- return {}
- else:
- printDebug("Response from XBMC: %s" % jsonraw)
- parsed=json.loads(jsonraw)
- if parsed.get('error', False):
- print "XBMC returned an error: %s" % parsed.get('error')
- return parsed.get('result', {})
def getXMLHeader():
return ''+"\r\n"
+
def getOKMsg():
return getXMLHeader() + ''
-def getPlexHeaders():
- h = {
- "Content-type": "application/x-www-form-urlencoded",
- "Access-Control-Allow-Origin": "*",
- "X-Plex-Version": settings['version'],
- "X-Plex-Client-Identifier": settings['uuid'],
- "X-Plex-Provides": "player",
- "X-Plex-Product": "PlexKodiConnect",
- "X-Plex-Device-Name": settings['client_name'],
- "X-Plex-Platform": "XBMC",
- "X-Plex-Model": getPlatform(),
- "X-Plex-Device": "PC",
- }
- if settings['myplex_user']:
- h["X-Plex-Username"] = settings['myplex_user']
- return h
-
-def getServerByHost(host):
- list = settings['serverList']
- if len(list) == 1:
- return list[0]
- for server in list:
- if server.get('serverName') in host or server.get('server') in host:
- return server
- return {}
-
-def getPlayers():
- info = jsonrpc("Player.GetActivePlayers") or []
- ret = {}
- for player in info:
- player['playerid'] = int(player['playerid'])
- ret[player['type']] = player
- return ret
-
-def getPlayerIds():
- ret = []
- for player in getPlayers().values():
- ret.append(player['playerid'])
- return ret
-
-def getVideoPlayerId(players = False):
- if players is None:
- players = getPlayers()
- return players.get(xbmc_video(), {}).get('playerid', None)
-
-def getAudioPlayerId(players = False):
- if players is None:
- players = getPlayers()
- return players.get(xbmc_audio(), {}).get('playerid', None)
-
-def getPhotoPlayerId(players = False):
- if players is None:
- players = getPlayers()
- return players.get(xbmc_photo(), {}).get('playerid', None)
-
-def getVolume():
- answ = jsonrpc('Application.GetProperties', { "properties": [ "volume", 'muted' ] })
- vol = str(answ.get('volume', 100))
- mute = ("0", "1")[answ.get('muted', False)]
- return (vol, mute)
def timeToMillis(time):
return (time['hours']*3600 + time['minutes']*60 + time['seconds'])*1000 + time['milliseconds']
+
def millisToTime(t):
millis = int(t)
seconds = millis / 1000
@@ -194,5 +88,118 @@ def millisToTime(t):
millis = millis % 1000
return {'hours':hours,'minutes':minutes,'seconds':seconds,'milliseconds':millis}
+
def textFromXml(element):
- return element.firstChild.data
\ No newline at end of file
+ return element.firstChild.data
+
+
+class jsonClass():
+
+ def __init__(self, requestMgr):
+ self.settings = settings.getSettings()
+ self.requestMgr = requestMgr
+
+ def jsonrpc(self, action, arguments={}):
+ """ put some JSON together for the JSON-RPC APIv6 """
+ if action.lower() == "sendkey":
+ request=json.dumps({ "jsonrpc" : "2.0" , "method" : "Input.SendText", "params" : { "text" : arguments[0], "done" : False }} )
+ elif action.lower() == "ping":
+ request=json.dumps({ "jsonrpc" : "2.0",
+ "id" : 1 ,
+ "method" : "JSONRPC.Ping" })
+ elif action.lower() == "playmedia":
+ xbmc.Player().play("plugin://plugin.video.plexkodiconnect/"
+ "?mode=companion&arguments=%s"
+ % arguments)
+ return True
+ elif arguments:
+ request=json.dumps({ "id" : 1,
+ "jsonrpc" : "2.0",
+ "method" : action,
+ "params" : arguments})
+ else:
+ request=json.dumps({ "id" : 1,
+ "jsonrpc" : "2.0",
+ "method" : action})
+
+ logMsg("PlexCompanion",
+ "Sending request to XBMC without network stack: %s"
+ % request, 2)
+ result = self.parseJSONRPC(xbmc.executeJSONRPC(request))
+
+ if not result and self.settings['webserver_enabled']:
+ # xbmc.executeJSONRPC appears to fail on the login screen, but going
+ # through the network stack works, so let's try the request again
+ result = self.parseJSONRPC(self.requestMgr.post(
+ "127.0.0.1",
+ self.settings['port'],
+ "/jsonrpc",
+ request,
+ { 'Content-Type' : 'application/json',
+ 'Authorization' : 'Basic ' + string.strip(base64.encodestring(self.settings['user'] + ':' + self.settings['passwd'])) }))
+
+ return result
+
+ def getPlexHeaders(self):
+ h = {
+ "Content-type": "application/x-www-form-urlencoded",
+ "Access-Control-Allow-Origin": "*",
+ "X-Plex-Version": self.settings['version'],
+ "X-Plex-Client-Identifier": self.settings['uuid'],
+ "X-Plex-Provides": "player",
+ "X-Plex-Product": "PlexKodiConnect",
+ "X-Plex-Device-Name": self.settings['client_name'],
+ "X-Plex-Platform": "Kodi",
+ "X-Plex-Model": getPlatform(),
+ "X-Plex-Device": "PC",
+ }
+ if self.settings['myplex_user']:
+ h["X-Plex-Username"] = self.settings['myplex_user']
+ return h
+
+ def parseJSONRPC(self, jsonraw):
+ if not jsonraw:
+ logMsg("PlexCompanion", "Empty response from XBMC", 1)
+ return {}
+ else:
+ logMsg("PlexCompanion", "Response from XBMC: %s" % jsonraw, 2)
+ parsed=json.loads(jsonraw)
+ if parsed.get('error', False):
+ logMsg("PlexCompanion", "XBMC returned an error: %s"
+ % parsed.get('error'), -1)
+ return parsed.get('result', {})
+
+ def getPlayers(self):
+ info = self.jsonrpc("Player.GetActivePlayers") or []
+ ret = {}
+ for player in info:
+ player['playerid'] = int(player['playerid'])
+ ret[player['type']] = player
+ return ret
+
+ def getPlayerIds(self):
+ ret = []
+ for player in self.getPlayers().values():
+ ret.append(player['playerid'])
+ return ret
+
+ def getVideoPlayerId(self, players=False):
+ if players is None:
+ players = self.getPlayers()
+ return players.get(xbmc_video(), {}).get('playerid', None)
+
+ def getAudioPlayerId(self, players=False):
+ if players is None:
+ players = self.getPlayers()
+ return players.get(xbmc_audio(), {}).get('playerid', None)
+
+ def getPhotoPlayerId(self, players=False):
+ if players is None:
+ players = self.getPlayers()
+ return players.get(xbmc_photo(), {}).get('playerid', None)
+
+ def getVolume(self):
+ answ = self.jsonrpc('Application.GetProperties', { "properties": [ "volume", 'muted' ] })
+ vol = str(answ.get('volume', 100))
+ mute = ("0", "1")[answ.get('muted', False)]
+ return (vol, mute)
diff --git a/resources/lib/plexbmchelper/httppersist.py b/resources/lib/plexbmchelper/httppersist.py
index 8d8857f9..01e08a14 100644
--- a/resources/lib/plexbmchelper/httppersist.py
+++ b/resources/lib/plexbmchelper/httppersist.py
@@ -1,7 +1,12 @@
import httplib
import traceback
import string
+import errno
+from socket import error as socket_error
+from utils import logging
+
+@logging
class RequestMgr:
def __init__(self):
self.conns = {}
@@ -9,24 +14,24 @@ class RequestMgr:
def getConnection(self, protocol, host, port):
conn = self.conns.get(protocol+host+str(port), False)
if not conn:
- if protocol=="https":
+ if protocol == "https":
conn = httplib.HTTPSConnection(host, port)
else:
conn = httplib.HTTPConnection(host, port)
self.conns[protocol+host+str(port)] = conn
return conn
-
+
def closeConnection(self, protocol, host, port):
conn = self.conns.get(protocol+host+str(port), False)
if conn:
conn.close()
self.conns.pop(protocol+host+str(port), None)
-
+
def dumpConnections(self):
for conn in self.conns.values():
conn.close()
self.conns = {}
-
+
def post(self, host, port, path, body, header={}, protocol="http"):
conn = None
try:
@@ -35,27 +40,33 @@ class RequestMgr:
conn.request("POST", path, body, header)
data = conn.getresponse()
if int(data.status) >= 400:
- print "HTTP response error: " + str(data.status)
- # this should return false, but I'm hacking it since iOS returns 404 no matter what
+ self.logMsg("HTTP response error: %s" % str(data.status), -1)
+ # this should return false, but I'm hacking it since iOS
+ # returns 404 no matter what
return data.read() or True
- else:
+ else:
return data.read() or True
- except:
- print "Unable to connect to %s\nReason:" % host
- traceback.print_exc()
+ except socket_error as serr:
+ # Ignore remote close and connection refused (e.g. shutdown PKC)
+ if serr.errno in (errno.WSAECONNABORTED, errno.WSAECONNREFUSED):
+ pass
+ else:
+ self.logMsg("Unable to connect to %s\nReason:" % host, -1)
+ self.logMsg(traceback.print_exc(), -1)
self.conns.pop(protocol+host+str(port), None)
if conn:
conn.close()
return False
-
- def getwithparams(self, host, port, path, params, header={}, protocol="http"):
+
+ def getwithparams(self, host, port, path, params, header={},
+ protocol="http"):
newpath = path + '?'
pairs = []
for key in params:
pairs.append(str(key)+'='+str(params[key]))
newpath += string.join(pairs, '&')
return self.get(host, port, newpath, header, protocol)
-
+
def get(self, host, port, path, header={}, protocol="http"):
try:
conn = self.getConnection(protocol, host, port)
@@ -63,14 +74,13 @@ class RequestMgr:
conn.request("GET", path, headers=header)
data = conn.getresponse()
if int(data.status) >= 400:
- print "HTTP response error: " + str(data.status)
+ self.logMsg("HTTP response error: %s" % str(data.status), -1)
return False
- else:
+ else:
return data.read() or True
except:
- print "Unable to connect to %s\nReason: %s" % (host, traceback.print_exc())
+ self.logMsg("Unable to connect to %s\nReason:" % host, -1)
+ self.logMsg(traceback.print_exc(), -1)
self.conns.pop(protocol+host+str(port), None)
conn.close()
return False
-
-requests = RequestMgr()
diff --git a/resources/lib/plexbmchelper/listener.py b/resources/lib/plexbmchelper/listener.py
index 2e53dcfb..b573f4c8 100644
--- a/resources/lib/plexbmchelper/listener.py
+++ b/resources/lib/plexbmchelper/listener.py
@@ -4,38 +4,56 @@ import xbmc
from SocketServer import ThreadingMixIn
from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler
from urlparse import urlparse, parse_qs
-from settings import settings
+import settings
from functions import *
-from subscribers import subMgr
+from utils import logging
+
+@logging
class MyHandler(BaseHTTPRequestHandler):
protocol_version = 'HTTP/1.1'
- def log_message(s, format, *args):
- # I have my own logging, suppressing BaseHTTPRequestHandler's
- #printDebug(format % args)
- return True
+
+ def __init__(self, *args, **kwargs):
+ self.serverlist = []
+ self.settings = settings.getSettings()
+ BaseHTTPRequestHandler.__init__(self, *args, **kwargs)
+
+ def getServerByHost(self, host):
+ if len(self.serverlist) == 1:
+ return self.serverlist[0]
+ for server in self.serverlist:
+ if (server.get('serverName') in host or
+ server.get('server') in host):
+ return server
+ return {}
+
def do_HEAD(s):
- printDebug( "Serving HEAD request..." )
+ s.logMsg("Serving HEAD request...", 2)
s.answer_request(0)
def do_GET(s):
- printDebug( "Serving GET request..." )
+ s.logMsg("Serving GET request...", 2)
s.answer_request(1)
def do_OPTIONS(s):
s.send_response(200)
s.send_header('Content-Length', '0')
- s.send_header('X-Plex-Client-Identifier', settings['uuid'])
+ s.send_header('X-Plex-Client-Identifier', s.settings['uuid'])
s.send_header('Content-Type', 'text/plain')
s.send_header('Connection', 'close')
s.send_header('Access-Control-Max-Age', '1209600')
s.send_header('Access-Control-Allow-Origin', '*')
- s.send_header('Access-Control-Allow-Methods', 'POST, GET, OPTIONS, DELETE, PUT, HEAD')
- s.send_header('Access-Control-Allow-Headers', 'x-plex-version, x-plex-platform-version, x-plex-username, x-plex-client-identifier, x-plex-target-client-identifier, x-plex-device-name, x-plex-platform, x-plex-product, accept, x-plex-device')
+ s.send_header('Access-Control-Allow-Methods',
+ 'POST, GET, OPTIONS, DELETE, PUT, HEAD')
+ s.send_header('Access-Control-Allow-Headers',
+ 'x-plex-version, x-plex-platform-version, '
+ 'x-plex-username, x-plex-client-identifier, '
+ 'x-plex-target-client-identifier, x-plex-device-name, '
+ 'x-plex-platform, x-plex-product, accept, x-plex-device')
s.end_headers()
s.wfile.close()
- def response(s, body, headers = {}, code = 200):
+ def response(s, body, headers={}, code=200):
try:
s.send_response(code)
for key in headers:
@@ -49,73 +67,76 @@ class MyHandler(BaseHTTPRequestHandler):
pass
def answer_request(s, sendData):
+ s.serverlist = s.server.client.getServerList()
+ s.subMgr = s.server.subscriptionManager
+ s.js = s.server.jsonClass
try:
- request_path=s.path[1:]
- request_path=re.sub(r"\?.*","",request_path)
+ request_path = s.path[1:]
+ request_path = re.sub(r"\?.*", "", request_path)
url = urlparse(s.path)
paramarrays = parse_qs(url.query)
params = {}
for key in paramarrays:
params[key] = paramarrays[key][0]
- printDebug ( "request path is: [%s]" % ( request_path,) )
- printDebug ( "params are: %s" % params )
- subMgr.updateCommandID(s.headers.get('X-Plex-Client-Identifier', s.client_address[0]), params.get('commandID', False))
+ s.logMsg("request path is: [%s]" % (request_path,), 2)
+ s.logMsg("params are: %s" % params, 2)
+ s.subMgr.updateCommandID(s.headers.get('X-Plex-Client-Identifier', s.client_address[0]), params.get('commandID', False))
if request_path=="version":
- s.response("PleXBMC Helper Remote Redirector: Running\r\nVersion: %s" % settings['version'])
+ s.response("PleXBMC Helper Remote Redirector: Running\r\nVersion: %s" % s.settings['version'])
elif request_path=="verify":
- result=jsonrpc("ping")
+ result=s.js.jsonrpc("ping")
s.response("XBMC JSON connection test:\r\n"+result)
elif "resources" == request_path:
resp = getXMLHeader()
resp += ""
resp += ""
resp += ""
- printDebug("crafted resources response: %s" % resp)
- s.response(resp, getPlexHeaders())
+ s.logMsg("crafted resources response: %s" % resp, 2)
+ s.response(resp, s.js.getPlexHeaders())
elif "/subscribe" in request_path:
- s.response(getOKMsg(), getPlexHeaders())
+ s.response(getOKMsg(), s.js.getPlexHeaders())
protocol = params.get('protocol', False)
host = s.client_address[0]
port = params.get('port', False)
uuid = s.headers.get('X-Plex-Client-Identifier', "")
commandID = params.get('commandID', 0)
- subMgr.addSubscriber(protocol, host, port, uuid, commandID)
+ s.subMgr.addSubscriber(protocol, host, port, uuid, commandID)
elif "/poll" in request_path:
if params.get('wait', False) == '1':
xbmc.sleep(950)
commandID = params.get('commandID', 0)
- s.response(re.sub(r"INSERTCOMMANDID", str(commandID), subMgr.msg(getPlayers())), {
- 'X-Plex-Client-Identifier': settings['uuid'],
+ s.response(re.sub(r"INSERTCOMMANDID", str(commandID), s.subMgr.msg(s.js.getPlayers())), {
+ 'X-Plex-Client-Identifier': s.settings['uuid'],
'Access-Control-Expose-Headers': 'X-Plex-Client-Identifier',
'Access-Control-Allow-Origin': '*',
'Content-Type': 'text/xml'
})
elif "/unsubscribe" in request_path:
- s.response(getOKMsg(), getPlexHeaders())
+ s.response(getOKMsg(), s.js.getPlexHeaders())
uuid = s.headers.get('X-Plex-Client-Identifier', False) or s.client_address[0]
- subMgr.removeSubscriber(uuid)
+ s.subMgr.removeSubscriber(uuid)
elif request_path == "player/playback/setParameters":
- s.response(getOKMsg(), getPlexHeaders())
+ s.response(getOKMsg(), s.js.getPlexHeaders())
if 'volume' in params:
volume = int(params['volume'])
- printDebug("adjusting the volume to %s%%" % volume)
- jsonrpc("Application.SetVolume", {"volume": volume})
+ s.logMsg("adjusting the volume to %s%%" % volume, 2)
+ s.js.jsonrpc("Application.SetVolume", {"volume": volume})
elif "/playMedia" in request_path:
- s.response(getOKMsg(), getPlexHeaders())
+ s.response(getOKMsg(), s.js.getPlexHeaders())
offset = params.get('viewOffset', params.get('offset', "0"))
protocol = params.get('protocol', "http")
address = params.get('address', s.client_address[0])
- server = getServerByHost(address)
+ server = s.getServerByHost(address)
port = params.get('port', server.get('port', '32400'))
try:
containerKey = urlparse(params.get('containerKey')).path
@@ -127,74 +148,88 @@ class MyHandler(BaseHTTPRequestHandler):
except IndexError:
playQueueID = ''
- jsonrpc("playmedia", params)
- subMgr.lastkey = params['key']
- subMgr.containerKey = containerKey
- subMgr.playQueueID = playQueueID
- subMgr.server = server.get('server', 'localhost')
- subMgr.port = port
- subMgr.protocol = protocol
- subMgr.notify()
+ s.js.jsonrpc("playmedia", params)
+ s.subMgr.lastkey = params['key']
+ s.subMgr.containerKey = containerKey
+ s.subMgr.playQueueID = playQueueID
+ s.subMgr.server = server.get('server', 'localhost')
+ s.subMgr.port = port
+ s.subMgr.protocol = protocol
+ s.subMgr.notify()
elif request_path == "player/playback/play":
- s.response(getOKMsg(), getPlexHeaders())
- for playerid in getPlayerIds():
- jsonrpc("Player.PlayPause", {"playerid" : playerid, "play": True})
+ s.response(getOKMsg(), s.js.getPlexHeaders())
+ for playerid in s.js.getPlayerIds():
+ s.js.jsonrpc("Player.PlayPause", {"playerid" : playerid, "play": True})
elif request_path == "player/playback/pause":
- s.response(getOKMsg(), getPlexHeaders())
- for playerid in getPlayerIds():
- jsonrpc("Player.PlayPause", {"playerid" : playerid, "play": False})
+ s.response(getOKMsg(), s.js.getPlexHeaders())
+ for playerid in s.js.getPlayerIds():
+ s.js.jsonrpc("Player.PlayPause", {"playerid" : playerid, "play": False})
elif request_path == "player/playback/stop":
- s.response(getOKMsg(), getPlexHeaders())
- for playerid in getPlayerIds():
- jsonrpc("Player.Stop", {"playerid" : playerid})
+ s.response(getOKMsg(), s.js.getPlexHeaders())
+ for playerid in s.js.getPlayerIds():
+ s.js.jsonrpc("Player.Stop", {"playerid" : playerid})
elif request_path == "player/playback/seekTo":
- s.response(getOKMsg(), getPlexHeaders())
- for playerid in getPlayerIds():
- jsonrpc("Player.Seek", {"playerid":playerid, "value":millisToTime(params.get('offset', 0))})
- subMgr.notify()
+ s.response(getOKMsg(), s.js.getPlexHeaders())
+ for playerid in s.js.getPlayerIds():
+ s.js.jsonrpc("Player.Seek", {"playerid":playerid, "value":millisToTime(params.get('offset', 0))})
+ s.subMgr.notify()
elif request_path == "player/playback/stepForward":
- s.response(getOKMsg(), getPlexHeaders())
- for playerid in getPlayerIds():
- jsonrpc("Player.Seek", {"playerid":playerid, "value":"smallforward"})
- subMgr.notify()
+ s.response(getOKMsg(), s.js.getPlexHeaders())
+ for playerid in s.js.getPlayerIds():
+ s.js.jsonrpc("Player.Seek", {"playerid":playerid, "value":"smallforward"})
+ s.subMgr.notify()
elif request_path == "player/playback/stepBack":
- s.response(getOKMsg(), getPlexHeaders())
- for playerid in getPlayerIds():
- jsonrpc("Player.Seek", {"playerid":playerid, "value":"smallbackward"})
- subMgr.notify()
+ s.response(getOKMsg(), s.js.getPlexHeaders())
+ for playerid in s.js.getPlayerIds():
+ s.js.jsonrpc("Player.Seek", {"playerid":playerid, "value":"smallbackward"})
+ s.subMgr.notify()
elif request_path == "player/playback/skipNext":
- s.response(getOKMsg(), getPlexHeaders())
- for playerid in getPlayerIds():
- jsonrpc("Player.Seek", {"playerid":playerid, "value":"bigforward"})
- subMgr.notify()
+ s.response(getOKMsg(), s.js.getPlexHeaders())
+ for playerid in s.js.getPlayerIds():
+ s.js.jsonrpc("Player.Seek", {"playerid":playerid, "value":"bigforward"})
+ s.subMgr.notify()
elif request_path == "player/playback/skipPrevious":
- s.response(getOKMsg(), getPlexHeaders())
- for playerid in getPlayerIds():
- jsonrpc("Player.Seek", {"playerid":playerid, "value":"bigbackward"})
- subMgr.notify()
+ s.response(getOKMsg(), s.js.getPlexHeaders())
+ for playerid in s.js.getPlayerIds():
+ s.js.jsonrpc("Player.Seek", {"playerid":playerid, "value":"bigbackward"})
+ s.subMgr.notify()
elif request_path == "player/navigation/moveUp":
- s.response(getOKMsg(), getPlexHeaders())
- jsonrpc("Input.Up")
+ s.response(getOKMsg(), s.js.getPlexHeaders())
+ s.js.jsonrpc("Input.Up")
elif request_path == "player/navigation/moveDown":
- s.response(getOKMsg(), getPlexHeaders())
- jsonrpc("Input.Down")
+ s.response(getOKMsg(), s.js.getPlexHeaders())
+ s.js.jsonrpc("Input.Down")
elif request_path == "player/navigation/moveLeft":
- s.response(getOKMsg(), getPlexHeaders())
- jsonrpc("Input.Left")
+ s.response(getOKMsg(), s.js.getPlexHeaders())
+ s.js.jsonrpc("Input.Left")
elif request_path == "player/navigation/moveRight":
- s.response(getOKMsg(), getPlexHeaders())
- jsonrpc("Input.Right")
+ s.response(getOKMsg(), s.js.getPlexHeaders())
+ s.js.jsonrpc("Input.Right")
elif request_path == "player/navigation/select":
- s.response(getOKMsg(), getPlexHeaders())
- jsonrpc("Input.Select")
+ s.response(getOKMsg(), s.js.getPlexHeaders())
+ s.js.jsonrpc("Input.Select")
elif request_path == "player/navigation/home":
- s.response(getOKMsg(), getPlexHeaders())
- jsonrpc("Input.Home")
+ s.response(getOKMsg(), s.js.getPlexHeaders())
+ s.js.jsonrpc("Input.Home")
elif request_path == "player/navigation/back":
- s.response(getOKMsg(), getPlexHeaders())
- jsonrpc("Input.Back")
+ s.response(getOKMsg(), s.js.getPlexHeaders())
+ s.js.jsonrpc("Input.Back")
except:
traceback.print_exc()
-
+
+
class ThreadedHTTPServer(ThreadingMixIn, HTTPServer):
- daemon_threads = True
\ No newline at end of file
+ daemon_threads = True
+
+ def __init__(self, client, subscriptionManager, jsonClass,
+ *args, **kwargs):
+ """
+ client: Class handle to plexgdm.plexgdm. We can thus ask for an up-to-
+ date serverlist without instantiating anything
+
+ same for SubscriptionManager and jsonClass
+ """
+ self.client = client
+ self.subscriptionManager = subscriptionManager
+ self.jsonClass = jsonClass
+ HTTPServer.__init__(self, *args, **kwargs)
diff --git a/resources/lib/plexbmchelper/plexgdm.py b/resources/lib/plexbmchelper/plexgdm.py
index f78626e5..7f441a32 100644
--- a/resources/lib/plexbmchelper/plexgdm.py
+++ b/resources/lib/plexbmchelper/plexgdm.py
@@ -333,7 +333,7 @@ class plexgdm:
if discovery_count > self.discovery_interval:
self.discover()
discovery_count=0
- xbmc.sleep(1000)
+ xbmc.sleep(500)
def start_discovery(self, daemon = False):
if not self._discovery_is_running:
diff --git a/resources/lib/plexbmchelper/settings.py b/resources/lib/plexbmchelper/settings.py
index e722b565..fe494c7d 100644
--- a/resources/lib/plexbmchelper/settings.py
+++ b/resources/lib/plexbmchelper/settings.py
@@ -1,48 +1,46 @@
-import xbmc
import xbmcaddon
import utils
-settings = {}
-
-guisettingsXML = utils.guisettingsXML()
-
def getGUI(name):
+ guisettingsXML = utils.guisettingsXML()
try:
ans = list(guisettingsXML.iter(name))[0].text
if ans is None:
ans = ''
- return ans
except:
- return ""
+ ans = ''
+ return ans
-addon = xbmcaddon.Addon()
-plexbmc = xbmcaddon.Addon('plugin.video.plexkodiconnect')
-settings['debug'] = utils.settings('companionDebugging')
-settings['gdm_debug'] = utils.settings('companionGDMDebugging')
+def getSettings():
+ settings = {}
+ addon = xbmcaddon.Addon()
+ plexbmc = xbmcaddon.Addon('plugin.video.plexkodiconnect')
-# Transform 'true' into True because of the way Kodi's file settings work
-kodiSettingsList = ['debug', 'gdm_debug']
-for entry in kodiSettingsList:
- if settings[entry] == 'true':
- settings[entry] = True
- else:
- settings[entry] = False
+ settings['debug'] = utils.settings('companionDebugging')
+ settings['gdm_debug'] = utils.settings('companionGDMDebugging')
-settings['client_name'] = plexbmc.getSetting('deviceName')
+ # Transform 'true' into True because of the way Kodi's file settings work
+ kodiSettingsList = ['debug', 'gdm_debug']
+ for entry in kodiSettingsList:
+ if settings[entry] == 'true':
+ settings[entry] = True
+ else:
+ settings[entry] = False
-# XBMC web server settings
-xbmc.sleep(5000)
-settings['webserver_enabled'] = (getGUI('webserver') == "true")
-settings['port'] = int(getGUI('webserverport'))
-settings['user'] = getGUI('webserverusername')
-settings['passwd'] = getGUI('webserverpassword')
+ settings['client_name'] = plexbmc.getSetting('deviceName')
-settings['uuid'] = plexbmc.getSetting('plex_client_Id')
+ # XBMC web server settings
+ settings['webserver_enabled'] = (getGUI('webserver') == "true")
+ settings['port'] = int(getGUI('webserverport'))
+ settings['user'] = getGUI('webserverusername')
+ settings['passwd'] = getGUI('webserverpassword')
-settings['version'] = plexbmc.getAddonInfo('version')
-settings['plexbmc_version'] = plexbmc.getAddonInfo('version')
-settings['myplex_user'] = plexbmc.getSetting('username')
-settings['serverList'] = []
-settings['myport'] = addon.getSetting('companionPort')
+ settings['uuid'] = plexbmc.getSetting('plex_client_Id')
+
+ settings['version'] = plexbmc.getAddonInfo('version')
+ settings['plexbmc_version'] = plexbmc.getAddonInfo('version')
+ settings['myplex_user'] = plexbmc.getSetting('username')
+ settings['myport'] = addon.getSetting('companionPort')
+ return settings
diff --git a/resources/lib/plexbmchelper/subscribers.py b/resources/lib/plexbmchelper/subscribers.py
index c85238c7..b742a21d 100644
--- a/resources/lib/plexbmchelper/subscribers.py
+++ b/resources/lib/plexbmchelper/subscribers.py
@@ -1,18 +1,17 @@
import re
import threading
-# from xml.dom.minidom import parseString
from functions import *
-from settings import settings
-from httppersist import requests
from xbmc import Player
-# import xbmcgui
import downloadutils
-from utils import window
+from utils import window, logging
import PlexFunctions as pf
+
+@logging
class SubscriptionManager:
- def __init__(self):
+ def __init__(self, jsonClass, RequestMgr):
+ self.serverlist = []
self.subscribers = {}
self.info = {}
self.lastkey = ""
@@ -27,8 +26,20 @@ class SubscriptionManager:
self.download = downloadutils.DownloadUtils()
self.xbmcplayer = Player()
+ self.js = jsonClass
+ self.RequestMgr = RequestMgr
+
+ def getServerByHost(self, host):
+ if len(self.serverlist) == 1:
+ return self.serverlist[0]
+ for server in self.serverlist:
+ if (server.get('serverName') in host or
+ server.get('server') in host):
+ return server
+ return {}
+
def getVolume(self):
- self.volume, self.mute = getVolume()
+ self.volume, self.mute = self.js.getVolume()
def msg(self, players):
msg = getXMLHeader()
@@ -45,9 +56,9 @@ class SubscriptionManager:
else:
self.mainlocation = "navigation"
msg += ' location="%s">' % self.mainlocation
- msg += self.getTimelineXML(getAudioPlayerId(players), plex_audio())
- msg += self.getTimelineXML(getPhotoPlayerId(players), plex_photo())
- msg += self.getTimelineXML(getVideoPlayerId(players), plex_video())
+ msg += self.getTimelineXML(self.js.getAudioPlayerId(players), plex_audio())
+ msg += self.getTimelineXML(self.js.getPhotoPlayerId(players), plex_photo())
+ msg += self.getTimelineXML(self.js.getVideoPlayerId(players), plex_video())
msg += "\r\n"
return msg
@@ -88,7 +99,7 @@ class SubscriptionManager:
ret += ' location="%s"' % (self.mainlocation)
ret += ' key="%s"' % (self.lastkey)
ret += ' ratingKey="%s"' % (self.lastratingkey)
- serv = getServerByHost(self.server)
+ serv = self.getServerByHost(self.server)
if info.get('playQueueID'):
self.containerKey = "/playQueues/%s" % info.get('playQueueID')
ret += ' playQueueID="%s"' % info.get('playQueueID')
@@ -124,7 +135,7 @@ class SubscriptionManager:
def notify(self, event = False):
self.cleanup()
- players = getPlayers()
+ players = self.js.getPlayers()
# fetch the message, subscribers or not, since the server
# will need the info anyway
msg = self.msg(players)
@@ -152,32 +163,39 @@ class SubscriptionManager:
params['state'] = info['state']
params['time'] = info['time']
params['duration'] = info['duration']
- serv = getServerByHost(self.server)
+ serv = self.getServerByHost(self.server)
url = serv.get('protocol', 'http') + '://' \
+ serv.get('server', 'localhost') + ':' \
+ serv.get('port', '32400') + "/:/timeline"
self.download.downloadUrl(url, type="GET", parameters=params)
# requests.getwithparams(serv.get('server', 'localhost'), serv.get('port', 32400), "/:/timeline", params, getPlexHeaders(), serv.get('protocol', 'http'))
- printDebug("params: %s" % params)
- printDebug("players: %s" % players)
- printDebug("sent server notification with state = %s" % params['state'])
+ self.logMsg("params: %s" % params, 2)
+ self.logMsg("players: %s" % players, 2)
+ self.logMsg("sent server notification with state = %s"
+ % params['state'], 2)
def controllable(self):
return "volume,shuffle,repeat,audioStream,videoStream,subtitleStream,skipPrevious,skipNext,seekTo,stepBack,stepForward,stop,playPause"
-
+
def addSubscriber(self, protocol, host, port, uuid, commandID):
- sub = Subscriber(protocol, host, port, uuid, commandID)
+ sub = Subscriber(protocol,
+ host,
+ port,
+ uuid,
+ commandID,
+ self,
+ self.RequestMgr)
with threading.RLock():
self.subscribers[sub.uuid] = sub
return sub
-
+
def removeSubscriber(self, uuid):
with threading.RLock():
for sub in self.subscribers.values():
if sub.uuid == uuid or sub.host == uuid:
sub.cleanup()
del self.subscribers[sub.uuid]
-
+
def cleanup(self):
with threading.RLock():
for sub in self.subscribers.values():
@@ -189,8 +207,8 @@ class SubscriptionManager:
info = {}
try:
# get info from the player
- props = jsonrpc("Player.GetProperties", {"playerid": playerid, "properties": ["time", "totaltime", "speed", "shuffled", "repeat"]})
- printDebug(jsonrpc("Player.GetItem", {"playerid": playerid, "properties": ["file", "showlink", "episode", "season"]}))
+ props = self.js.jsonrpc("Player.GetProperties", {"playerid": playerid, "properties": ["time", "totaltime", "speed", "shuffled", "repeat"]})
+ self.logMsg(self.js.jsonrpc("Player.GetItem", {"playerid": playerid, "properties": ["file", "showlink", "episode", "season"]}), 2)
info['time'] = timeToMillis(props['time'])
info['duration'] = timeToMillis(props['totaltime'])
info['state'] = ("paused", "playing")[int(props['speed'])]
@@ -214,8 +232,11 @@ class SubscriptionManager:
return info
+
+@logging
class Subscriber:
- def __init__(self, protocol, host, port, uuid, commandID):
+ def __init__(self, protocol, host, port, uuid, commandID,
+ subMgr, RequestMgr):
self.protocol = protocol or "http"
self.host = host
self.port = port or 32400
@@ -224,12 +245,18 @@ class Subscriber:
self.navlocationsent = False
self.age = 0
self.download = downloadutils.DownloadUtils()
+ self.subMgr = subMgr
+ self.RequestMgr = RequestMgr
+
def __eq__(self, other):
return self.uuid == other.uuid
+
def tostr(self):
return "uuid=%s,commandID=%i" % (self.uuid, self.commandID)
+
def cleanup(self):
- requests.closeConnection(self.protocol, self.host, self.port)
+ self.RequestMgr.closeConnection(self.protocol, self.host, self.port)
+
def send_update(self, msg, is_nav):
self.age += 1
if not is_nav:
@@ -239,7 +266,8 @@ class Subscriber:
else:
self.navlocationsent = True
msg = re.sub(r"INSERTCOMMANDID", str(self.commandID), msg)
- printDebug("sending xml to subscriber %s: %s" % (self.tostr(), msg))
+ self.logMsg("sending xml to subscriber %s: %s"
+ % (self.tostr(), msg), 2)
url = self.protocol + '://' + self.host + ':' + self.port \
+ "/:/timeline"
t = threading.Thread(target=self.threadedSend, args=(url, msg))
@@ -255,6 +283,4 @@ class Subscriber:
postBody=msg,
type="POSTXML")
if response in [False, None, 401]:
- subMgr.removeSubscriber(self.uuid)
-
-subMgr = SubscriptionManager()
+ self.subMgr.removeSubscriber(self.uuid)
diff --git a/service.py b/service.py
index 68465035..6749450e 100644
--- a/service.py
+++ b/service.py
@@ -322,8 +322,7 @@ class Service():
delay = int(utils.settings('startupDelay'))
xbmc.log("Delaying Plex startup by: %s sec..." % delay)
-# Plex: add 10 seconds just for good measure
-if delay and xbmc.Monitor().waitForAbort(delay+10):
+if delay and xbmc.Monitor().waitForAbort(delay):
# Start the service
xbmc.log("Abort requested while waiting. Emby for kodi not started.")
else: