Major Plex Companion overhaul, part 1

This commit is contained in:
tomkat83 2017-12-10 19:01:22 +01:00
parent c3b5054477
commit b1e2791ca8
7 changed files with 165 additions and 146 deletions

View File

@ -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.

View File

@ -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):

View File

@ -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))

View File

@ -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!)

View File

@ -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)

View File

@ -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

View File

@ -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?!?
}