Major Plex Companion overhaul, part 1
This commit is contained in:
parent
c3b5054477
commit
b1e2791ca8
7 changed files with 165 additions and 146 deletions
|
@ -291,15 +291,6 @@ def init_plex_playqueue(itemid, librarySectionUUID, mediatype='movie',
|
|||
return xml
|
||||
|
||||
|
||||
def getPlexRepeat(kodiRepeat):
|
||||
plexRepeat = {
|
||||
'off': '0',
|
||||
'one': '1',
|
||||
'all': '2' # does this work?!?
|
||||
}
|
||||
return plexRepeat.get(kodiRepeat)
|
||||
|
||||
|
||||
def PMSHttpsEnabled(url):
|
||||
"""
|
||||
Returns True if the PMS can talk https, False otherwise.
|
||||
|
|
|
@ -360,6 +360,22 @@ def get_episodes(params):
|
|||
return ret
|
||||
|
||||
|
||||
def get_item(playerid):
|
||||
"""
|
||||
Returns the following for the currently playing item:
|
||||
{
|
||||
u'title': u'Okja',
|
||||
u'type': u'movie',
|
||||
u'id': 258,
|
||||
u'file': u'smb://...movie.mkv',
|
||||
u'label': u'Okja'
|
||||
}
|
||||
"""
|
||||
return jsonrpc('Player.GetItem').execute({
|
||||
'playerid': playerid,
|
||||
'properties': ['title', 'file']})['result']['item']
|
||||
|
||||
|
||||
def get_player_props(playerid):
|
||||
"""
|
||||
Returns a dict for the active Kodi player with the following values:
|
||||
|
@ -367,14 +383,14 @@ def get_player_props(playerid):
|
|||
'type' [str] the Kodi player type, e.g. 'video'
|
||||
'time' The current item's time in Kodi time
|
||||
'totaltime' The current item's total length in Kodi time
|
||||
'speed' [int] playback speed, defaults to 0
|
||||
'speed' [int] playback speed, 0 is paused, 1 is playing
|
||||
'shuffled' [bool] True if shuffled
|
||||
'repeat' [str] 'off', 'one', 'all'
|
||||
'position' [int] position in playlist (or -1)
|
||||
'playlistid' [int] the Kodi playlist id (or -1)
|
||||
}
|
||||
"""
|
||||
ret = jsonrpc('Player.GetProperties').execute({
|
||||
return jsonrpc('Player.GetProperties').execute({
|
||||
'playerid': playerid,
|
||||
'properties': ['type',
|
||||
'time',
|
||||
|
@ -383,8 +399,11 @@ def get_player_props(playerid):
|
|||
'shuffled',
|
||||
'repeat',
|
||||
'position',
|
||||
'playlistid']})
|
||||
return ret['result']
|
||||
'playlistid',
|
||||
'currentvideostream',
|
||||
'currentaudiostream',
|
||||
'subtitleenabled',
|
||||
'currentsubtitle']})['result']
|
||||
|
||||
|
||||
def current_audiostream(playerid):
|
||||
|
|
|
@ -13,7 +13,9 @@ from utils import window, settings, CatchExceptions, tryDecode, tryEncode, \
|
|||
from PlexFunctions import scrobble
|
||||
from kodidb_functions import get_kodiid_from_filename
|
||||
from PlexAPI import API
|
||||
import json_rpc as js
|
||||
import state
|
||||
import variables as v
|
||||
|
||||
###############################################################################
|
||||
|
||||
|
@ -178,69 +180,48 @@ class KodiMonitor(Monitor):
|
|||
|
||||
def PlayBackStart(self, data):
|
||||
"""
|
||||
Called whenever a playback is started
|
||||
Called whenever a playback is started. Example data:
|
||||
{
|
||||
u'item': {u'type': u'movie', u'title': u''},
|
||||
u'player': {u'playerid': 1, u'speed': 1}
|
||||
}
|
||||
"""
|
||||
# Get currently playing file - can take a while. Will be utf-8!
|
||||
try:
|
||||
currentFile = self.xbmcplayer.getPlayingFile()
|
||||
except:
|
||||
currentFile = None
|
||||
count = 0
|
||||
while currentFile is None:
|
||||
sleep(100)
|
||||
try:
|
||||
currentFile = self.xbmcplayer.getPlayingFile()
|
||||
except:
|
||||
pass
|
||||
if count == 50:
|
||||
log.info("No current File, cancel OnPlayBackStart...")
|
||||
return
|
||||
else:
|
||||
count += 1
|
||||
# Just to be on the safe side
|
||||
currentFile = tryDecode(currentFile)
|
||||
log.debug("Currently playing file is: %s" % currentFile)
|
||||
|
||||
log.debug('PlayBackStart called with: %s', data)
|
||||
# Get the type of media we're playing
|
||||
try:
|
||||
typus = data['item']['type']
|
||||
kodi_type = data['item']['type']
|
||||
playerid = data['player']['playerid']
|
||||
json_data = js.get_item(playerid)
|
||||
except (TypeError, KeyError):
|
||||
log.info("Item is invalid for PMS playstate update.")
|
||||
log.info('Aborting playback report - item is invalid for updates')
|
||||
return
|
||||
log.debug("Playing itemtype is (or appears to be): %s" % typus)
|
||||
|
||||
# Try to get a Kodi ID
|
||||
# If PKC was used - native paths, not direct paths
|
||||
plex_id = window('plex_%s.itemid' % tryEncode(currentFile))
|
||||
# Get rid of the '' if the window property was not set
|
||||
plex_id = None if not plex_id else plex_id
|
||||
kodiid = None
|
||||
if plex_id is None:
|
||||
log.debug('Did not get Plex id from window properties')
|
||||
try:
|
||||
kodiid = data['item']['id']
|
||||
except (TypeError, KeyError):
|
||||
log.debug('Did not get a Kodi id from Kodi, darn')
|
||||
# For direct paths, if we're not streaming something
|
||||
# When using Widgets, Kodi doesn't tell us shit so we need this hack
|
||||
if (kodiid is None and plex_id is None and typus != 'song'
|
||||
and not currentFile.startswith('http')):
|
||||
(kodiid, typus) = get_kodiid_from_filename(currentFile)
|
||||
if kodiid is None:
|
||||
return
|
||||
|
||||
if plex_id is None:
|
||||
# Get Plex' item id
|
||||
with plexdb.Get_Plex_DB() as plexcursor:
|
||||
plex_dbitem = plexcursor.getItem_byKodiId(kodiid, typus)
|
||||
try:
|
||||
plex_id = plex_dbitem[0]
|
||||
except TypeError:
|
||||
log.info("No Plex id returned for kodiid %s. Aborting playback"
|
||||
" report" % kodiid)
|
||||
return
|
||||
log.debug("Found Plex id %s for Kodi id %s for type %s"
|
||||
% (plex_id, kodiid, typus))
|
||||
try:
|
||||
kodi_id = json_data['id']
|
||||
kodi_type = json_data['type']
|
||||
except KeyError:
|
||||
log.info('Aborting playback report - no Kodi id for %s', json_data)
|
||||
return
|
||||
# Get Plex' item id
|
||||
with plexdb.Get_Plex_DB() as plex_db:
|
||||
plex_dbitem = plex_db.getItem_byKodiId(kodi_id, kodi_type)
|
||||
try:
|
||||
plex_id = plex_dbitem[0]
|
||||
plex_type = plex_dbitem[2]
|
||||
except TypeError:
|
||||
# No plex id, hence item not in the library. E.g. clips
|
||||
plex_id = None
|
||||
plex_type = None
|
||||
state.PLAYER_STATES[playerid].update(js.get_player_props(playerid))
|
||||
state.PLAYER_STATES[playerid]['file'] = json_data['file']
|
||||
state.PLAYER_STATES[playerid]['kodi_id'] = kodi_id
|
||||
state.PLAYER_STATES[playerid]['kodi_type'] = kodi_type
|
||||
state.PLAYER_STATES[playerid]['plex_id'] = plex_id
|
||||
state.PLAYER_STATES[playerid]['plex_type'] = plex_type
|
||||
log.debug('Set the player state %s', state.PLAYER_STATES[playerid])
|
||||
# Set other stuff like volume
|
||||
state.PLAYER_STATES[playerid]['volume'] = js.get_volume()
|
||||
state.PLAYER_STATES[playerid]['muted'] = js.get_muted()
|
||||
return
|
||||
|
||||
# Switch subtitle tracks if applicable
|
||||
subtitle = window('plex_%s.subtitle' % tryEncode(currentFile))
|
||||
|
|
|
@ -37,6 +37,7 @@ class PKC_Player(Player):
|
|||
Will be called when xbmc starts playing a file.
|
||||
Window values need to have been set in Kodimonitor.py
|
||||
"""
|
||||
return
|
||||
self.stopAll()
|
||||
|
||||
# Get current file (in utf-8!)
|
||||
|
|
|
@ -72,70 +72,62 @@ class SubscriptionManager:
|
|||
msg += self.getTimelineXML(players.get(v.KODI_TYPE_VIDEO),
|
||||
v.PLEX_TYPE_VIDEO)
|
||||
msg += "\n</MediaContainer>"
|
||||
log.debug('msg is: %s', msg)
|
||||
return msg
|
||||
|
||||
def getTimelineXML(self, player, ptype):
|
||||
if player is None:
|
||||
status = 'stopped'
|
||||
time = 0
|
||||
else:
|
||||
playerid = player['playerid']
|
||||
info = self.getPlayerProperties(playerid)
|
||||
info = state.PLAYER_STATES[playerid]
|
||||
# save this info off so the server update can use it too
|
||||
self.playerprops[playerid] = info
|
||||
status = info['state']
|
||||
time = info['time']
|
||||
# self.playerprops[playerid] = info
|
||||
status = ("paused", "playing")[info['speed']]
|
||||
ret = ('\n <Timeline state="%s" controllable="%s" type="%s" '
|
||||
'itemType="%s"' % (status, CONTROLLABLE[ptype], ptype, ptype))
|
||||
if player is None:
|
||||
ret += ' />'
|
||||
return ret
|
||||
|
||||
ret += ' time="%s"' % time
|
||||
ret += ' time="%s"' % kodi_time_to_millis(info['time'])
|
||||
ret += ' duration="%s"' % kodi_time_to_millis(info['totaltime'])
|
||||
ret += ' shuffle="%s"' % ("0", "1")[info['shuffled']]
|
||||
ret += ' repeat="%s"' % v.PLEX_REPEAT_FROM_KODI_REPEAT[info['repeat']]
|
||||
if ptype != v.KODI_TYPE_PHOTO:
|
||||
ret += ' volume="%s"' % info['volume']
|
||||
ret += ' mute="%s"' % ("0", "1")[info['muted']]
|
||||
pbmc_server = window('pms_server')
|
||||
server = self.getServerByHost(self.server)
|
||||
if pbmc_server:
|
||||
(self.protocol, self.server, self.port) = \
|
||||
pbmc_server.split(':')
|
||||
(self.protocol, self.server, self.port) = pbmc_server.split(':')
|
||||
self.server = self.server.replace('/', '')
|
||||
keyid = None
|
||||
count = 0
|
||||
while not keyid:
|
||||
if count > 30:
|
||||
break
|
||||
keyid = window('plex_currently_playing_itemid')
|
||||
sleep(100)
|
||||
count += 1
|
||||
if keyid:
|
||||
self.lastkey = "/library/metadata/%s" % keyid
|
||||
self.ratingkey = keyid
|
||||
ret += ' key="%s"' % self.lastkey
|
||||
ret += ' ratingKey="%s"' % self.ratingkey
|
||||
serv = self.getServerByHost(self.server)
|
||||
if info.get('playQueueID'):
|
||||
self.containerKey = "/playQueues/%s" % info.get('playQueueID')
|
||||
ret += ' playQueueID="%s"' % info.get('playQueueID')
|
||||
ret += ' playQueueVersion="%s"' % info.get('playQueueVersion')
|
||||
ret += ' playQueueItemID="%s"' % info.get('playQueueItemID')
|
||||
if info['plex_id']:
|
||||
self.lastkey = "/library/metadata/%s" % info['plex_id']
|
||||
self.ratingkey = info['plex_id']
|
||||
ret += ' key="/library/metadata/%s"' % info['plex_id']
|
||||
ret += ' ratingKey="%s"' % info['plex_id']
|
||||
# PlayQueue stuff
|
||||
playqueue = self.playqueue.playqueues[playerid]
|
||||
pos = info['position']
|
||||
try:
|
||||
ret += ' playQueueItemID="%s"' % playqueue.items[pos].ID or 'null'
|
||||
self.containerKey = "/playQueues/%s" % playqueue.ID or 'null'
|
||||
ret += ' playQueueID="%s"' % playqueue.ID or 'null'
|
||||
ret += ' playQueueVersion="%s"' % playqueue.version or 'null'
|
||||
ret += ' containerKey="%s"' % self.containerKey
|
||||
ret += ' guid="%s"' % info['guid']
|
||||
elif keyid:
|
||||
self.containerKey = self.lastkey
|
||||
ret += ' containerKey="%s"' % self.containerKey
|
||||
|
||||
ret += ' duration="%s"' % info['duration']
|
||||
ret += ' machineIdentifier="%s"' % serv.get('uuid', "")
|
||||
ret += ' protocol="%s"' % serv.get('protocol', "http")
|
||||
ret += ' address="%s"' % serv.get('server', self.server)
|
||||
ret += ' port="%s"' % serv.get('port', self.port)
|
||||
ret += ' volume="%s"' % info['volume']
|
||||
ret += ' shuffle="%s"' % info['shuffle']
|
||||
ret += ' mute="%s"' % info['mute']
|
||||
ret += ' repeat="%s"' % info['repeat']
|
||||
ret += ' itemType="%s"' % ptype
|
||||
ret += ' guid="%s"' % playqueue.items[pos].guid or 'null'
|
||||
except IndexError:
|
||||
pass
|
||||
ret += ' machineIdentifier="%s"' % server.get('uuid', "")
|
||||
ret += ' protocol="%s"' % server.get('protocol', 'http')
|
||||
ret += ' address="%s"' % server.get('server', self.server)
|
||||
ret += ' port="%s"' % server.get('port', self.port)
|
||||
# Temp. token set?
|
||||
if state.PLEX_TRANSIENT_TOKEN:
|
||||
ret += ' token="%s"' % state.PLEX_TRANSIENT_TOKEN
|
||||
elif info['plex_transient_token']:
|
||||
ret += ' token="%s"' % info['plex_transient_token']
|
||||
elif playqueue.plex_transient_token:
|
||||
ret += ' token="%s"' % playqueue.plex_transient_token
|
||||
# Might need an update in the future
|
||||
if ptype == 'video':
|
||||
ret += ' subtitleStreamID="-1"'
|
||||
|
@ -236,37 +228,26 @@ class SubscriptionManager:
|
|||
del self.subscribers[sub.uuid]
|
||||
|
||||
def getPlayerProperties(self, playerid):
|
||||
# Get the playqueue
|
||||
playqueue = self.playqueue.playqueues[playerid]
|
||||
# get info from the player
|
||||
props = state.PLAYER_STATES[playerid]
|
||||
info = {
|
||||
'time': kodi_time_to_millis(props['time']),
|
||||
'duration': kodi_time_to_millis(props['totaltime']),
|
||||
'state': ("paused", "playing")[int(props['speed'])],
|
||||
'shuffle': ("0", "1")[props.get('shuffled', False)],
|
||||
'repeat': v.PLEX_REPEAT_FROM_KODI_REPEAT[props.get('repeat')]
|
||||
}
|
||||
pos = props['position']
|
||||
try:
|
||||
# Get the playqueue
|
||||
playqueue = self.playqueue.playqueues[playerid]
|
||||
# get info from the player
|
||||
props = js.get_player_props(playerid)
|
||||
info = {
|
||||
'time': kodi_time_to_millis(props['time']),
|
||||
'duration': kodi_time_to_millis(props['totaltime']),
|
||||
'state': ("paused", "playing")[int(props['speed'])],
|
||||
'shuffle': ("0", "1")[props.get('shuffled', False)],
|
||||
'repeat': pf.getPlexRepeat(props.get('repeat')),
|
||||
}
|
||||
pos = props['position']
|
||||
try:
|
||||
info['playQueueItemID'] = playqueue.items[pos].ID or 'null'
|
||||
info['guid'] = playqueue.items[pos].guid or 'null'
|
||||
info['playQueueID'] = playqueue.ID or 'null'
|
||||
info['playQueueVersion'] = playqueue.version or 'null'
|
||||
info['itemType'] = playqueue.items[pos].plex_type or 'null'
|
||||
except:
|
||||
info['itemType'] = props.get('type') or 'null'
|
||||
info['playQueueItemID'] = playqueue.items[pos].ID or 'null'
|
||||
info['guid'] = playqueue.items[pos].guid or 'null'
|
||||
info['playQueueID'] = playqueue.ID or 'null'
|
||||
info['playQueueVersion'] = playqueue.version or 'null'
|
||||
info['itemType'] = playqueue.items[pos].plex_type or 'null'
|
||||
except:
|
||||
import traceback
|
||||
log.error("Traceback:\n%s" % traceback.format_exc())
|
||||
info = {
|
||||
'time': 0,
|
||||
'duration': 0,
|
||||
'state': 'stopped',
|
||||
'shuffle': False,
|
||||
'repeat': 0
|
||||
}
|
||||
info['itemType'] = props.get('type') or 'null'
|
||||
|
||||
# get the volume from the application
|
||||
info['volume'] = js.get_volume()
|
||||
|
@ -323,5 +304,6 @@ class Subscriber:
|
|||
response = self.doUtils(url,
|
||||
postBody=msg,
|
||||
action_type="POST")
|
||||
log.debug('response is: %s', response)
|
||||
if response in [False, None, 401]:
|
||||
self.subMgr.removeSubscriber(self.uuid)
|
||||
|
|
|
@ -75,8 +75,33 @@ PLEX_USER_ID = None
|
|||
# another user playing something! Token identifies user
|
||||
PLEX_TRANSIENT_TOKEN = None
|
||||
|
||||
# Kodi player states
|
||||
PLAYER_STATES = {}
|
||||
# Kodi player states - here, initial values are set
|
||||
PLAYER_STATES = {
|
||||
1: {
|
||||
'type': 'movie',
|
||||
'time': 0,
|
||||
'totaltime': 0,
|
||||
'speed': 0,
|
||||
'shuffled': False,
|
||||
'repeat': '0',
|
||||
'position': -1,
|
||||
'playlistid': -1,
|
||||
'currentvideostream': -1,
|
||||
'currentaudiostream': -1,
|
||||
'subtitleenabled': False,
|
||||
'currentsubtitle': -1,
|
||||
######
|
||||
'file': '',
|
||||
'kodi_id': None,
|
||||
'kodi_type': None,
|
||||
'plex_id': None,
|
||||
'plex_type': None,
|
||||
'volume': 100,
|
||||
'muted': False
|
||||
},
|
||||
2: {},
|
||||
3: {}
|
||||
}
|
||||
PLAYED_INFO = {}
|
||||
|
||||
# Kodi webserver details
|
||||
|
|
|
@ -214,6 +214,20 @@ KODI_PLAYLIST_TYPE_FROM_PLEX_TYPE = {
|
|||
}
|
||||
|
||||
|
||||
KODI_PLAYLIST_TYPE_FROM_KODI_TYPE = {
|
||||
KODI_TYPE_VIDEO: KODI_TYPE_VIDEO,
|
||||
KODI_TYPE_MOVIE: KODI_TYPE_VIDEO,
|
||||
KODI_TYPE_EPISODE: KODI_TYPE_VIDEO,
|
||||
KODI_TYPE_SEASON: KODI_TYPE_VIDEO,
|
||||
KODI_TYPE_SHOW: KODI_TYPE_VIDEO,
|
||||
KODI_TYPE_CLIP: KODI_TYPE_VIDEO,
|
||||
KODI_TYPE_ARTIST: KODI_TYPE_AUDIO,
|
||||
KODI_TYPE_ALBUM: KODI_TYPE_AUDIO,
|
||||
KODI_TYPE_SONG: KODI_TYPE_AUDIO,
|
||||
KODI_TYPE_AUDIO: KODI_TYPE_AUDIO,
|
||||
KODI_TYPE_PHOTO: KODI_TYPE_PHOTO
|
||||
}
|
||||
|
||||
REMAP_TYPE_FROM_PLEXTYPE = {
|
||||
PLEX_TYPE_MOVIE: 'movie',
|
||||
PLEX_TYPE_CLIP: 'clip',
|
||||
|
@ -371,3 +385,9 @@ SORT_METHODS_ALBUMS = (
|
|||
XML_HEADER = '<?xml version="1.0" encoding="UTF-8"?>\n'
|
||||
|
||||
COMPANION_OK_MESSAGE = XML_HEADER + '<Response code="200" status="OK" />'
|
||||
|
||||
PLEX_REPEAT_FROM_KODI_REPEAT = {
|
||||
'off': '0',
|
||||
'one': '1',
|
||||
'all': '2' # does this work?!?
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue