2016-01-15 22:12:52 +11:00
|
|
|
import base64
|
|
|
|
import json
|
|
|
|
import string
|
2016-04-07 02:23:51 +10:00
|
|
|
|
2016-01-15 22:12:52 +11:00
|
|
|
import xbmc
|
2016-04-07 02:23:51 +10:00
|
|
|
|
2016-04-05 18:57:30 +10:00
|
|
|
from utils import logging
|
2016-04-03 01:46:23 +11:00
|
|
|
|
2016-01-15 22:12:52 +11:00
|
|
|
|
|
|
|
def xbmc_photo():
|
|
|
|
return "photo"
|
2016-04-03 01:46:23 +11:00
|
|
|
|
|
|
|
|
2016-01-15 22:12:52 +11:00
|
|
|
def xbmc_video():
|
|
|
|
return "video"
|
2016-04-03 01:46:23 +11:00
|
|
|
|
|
|
|
|
2016-01-15 22:12:52 +11:00
|
|
|
def xbmc_audio():
|
|
|
|
return "audio"
|
|
|
|
|
2016-04-03 01:46:23 +11:00
|
|
|
|
2016-01-15 22:12:52 +11:00
|
|
|
def plex_photo():
|
|
|
|
return "photo"
|
2016-04-03 01:46:23 +11:00
|
|
|
|
|
|
|
|
2016-01-15 22:12:52 +11:00
|
|
|
def plex_video():
|
|
|
|
return "video"
|
2016-04-03 01:46:23 +11:00
|
|
|
|
|
|
|
|
2016-01-15 22:12:52 +11:00
|
|
|
def plex_audio():
|
|
|
|
return "music"
|
|
|
|
|
2016-04-03 01:46:23 +11:00
|
|
|
|
2016-01-15 22:12:52 +11:00
|
|
|
def xbmc_type(plex_type):
|
|
|
|
if plex_type == plex_photo():
|
|
|
|
return xbmc_photo()
|
|
|
|
elif plex_type == plex_video():
|
|
|
|
return xbmc_video()
|
|
|
|
elif plex_type == plex_audio():
|
|
|
|
return xbmc_audio()
|
2016-04-03 01:46:23 +11:00
|
|
|
|
|
|
|
|
2016-01-15 22:12:52 +11:00
|
|
|
def plex_type(xbmc_type):
|
|
|
|
if xbmc_type == xbmc_photo():
|
|
|
|
return plex_photo()
|
|
|
|
elif xbmc_type == xbmc_video():
|
|
|
|
return plex_video()
|
|
|
|
elif xbmc_type == xbmc_audio():
|
|
|
|
return plex_audio()
|
|
|
|
|
2016-04-03 01:46:23 +11:00
|
|
|
|
2016-01-15 22:12:52 +11:00
|
|
|
def getXMLHeader():
|
2016-04-07 02:23:51 +10:00
|
|
|
return '<?xml version="1.0" encoding="utf-8" ?>\r\n'
|
2016-01-15 22:12:52 +11:00
|
|
|
|
2016-04-03 01:46:23 +11:00
|
|
|
|
2016-01-15 22:12:52 +11:00
|
|
|
def getOKMsg():
|
|
|
|
return getXMLHeader() + '<Response code="200" status="OK" />'
|
|
|
|
|
|
|
|
|
|
|
|
def timeToMillis(time):
|
|
|
|
return (time['hours']*3600 + time['minutes']*60 + time['seconds'])*1000 + time['milliseconds']
|
|
|
|
|
2016-04-03 01:46:23 +11:00
|
|
|
|
2016-01-15 22:12:52 +11:00
|
|
|
def millisToTime(t):
|
|
|
|
millis = int(t)
|
|
|
|
seconds = millis / 1000
|
|
|
|
minutes = seconds / 60
|
|
|
|
hours = minutes / 60
|
|
|
|
seconds = seconds % 60
|
|
|
|
minutes = minutes % 60
|
|
|
|
millis = millis % 1000
|
|
|
|
return {'hours':hours,'minutes':minutes,'seconds':seconds,'milliseconds':millis}
|
|
|
|
|
2016-04-03 01:46:23 +11:00
|
|
|
|
2016-01-15 22:12:52 +11:00
|
|
|
def textFromXml(element):
|
2016-04-03 01:46:23 +11:00
|
|
|
return element.firstChild.data
|
|
|
|
|
|
|
|
|
2016-04-05 18:57:30 +10:00
|
|
|
@logging
|
2016-04-03 01:46:23 +11:00
|
|
|
class jsonClass():
|
|
|
|
|
2016-04-05 18:57:30 +10:00
|
|
|
def __init__(self, requestMgr, settings):
|
|
|
|
self.settings = settings
|
2016-04-03 01:46:23 +11:00
|
|
|
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})
|
|
|
|
|
2016-04-05 18:57:30 +10:00
|
|
|
self.logMsg("Sending request to XBMC without network stack: %s"
|
|
|
|
% request, 2)
|
2016-04-03 01:46:23 +11:00
|
|
|
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",
|
2016-04-07 02:23:51 +10:00
|
|
|
"X-Plex-Model": self.settings['platform'],
|
2016-04-03 01:46:23 +11:00
|
|
|
"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:
|
2016-04-05 18:57:30 +10:00
|
|
|
self.logMsg("Empty response from XBMC", 1)
|
2016-04-03 01:46:23 +11:00
|
|
|
return {}
|
|
|
|
else:
|
2016-04-05 18:57:30 +10:00
|
|
|
self.logMsg("Response from XBMC: %s" % jsonraw, 2)
|
2016-04-03 01:46:23 +11:00
|
|
|
parsed=json.loads(jsonraw)
|
|
|
|
if parsed.get('error', False):
|
2016-04-05 18:57:30 +10:00
|
|
|
self.logMsg("XBMC returned an error: %s" % parsed.get('error'), -1)
|
2016-04-03 01:46:23 +11:00
|
|
|
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)
|