Plex Companion optimizations
This commit is contained in:
parent
2e5249ca4f
commit
18a9e77b33
2 changed files with 86 additions and 39 deletions
|
@ -105,12 +105,16 @@ class PlexCompanion(Thread):
|
||||||
api = API(xml[0])
|
api = API(xml[0])
|
||||||
playqueue = self.mgr.playqueue.get_playqueue_from_type(
|
playqueue = self.mgr.playqueue.get_playqueue_from_type(
|
||||||
v.KODI_PLAYLIST_TYPE_FROM_PLEX_TYPE[api.getType()])
|
v.KODI_PLAYLIST_TYPE_FROM_PLEX_TYPE[api.getType()])
|
||||||
self.mgr.playqueue.update_playqueue_from_PMS(
|
if container_key == playqueue.id:
|
||||||
playqueue,
|
LOG.info('Already know this playqueue - ignoring')
|
||||||
playqueue_id=container_key,
|
playqueue.transient_token = data.get('token')
|
||||||
repeat=query.get('repeat'),
|
else:
|
||||||
offset=data.get('offset'),
|
self.mgr.playqueue.update_playqueue_from_PMS(
|
||||||
transient_token=data.get('token'))
|
playqueue,
|
||||||
|
playqueue_id=container_key,
|
||||||
|
repeat=query.get('repeat'),
|
||||||
|
offset=data.get('offset'),
|
||||||
|
transient_token=data.get('token'))
|
||||||
|
|
||||||
@LOCKER.lockthis
|
@LOCKER.lockthis
|
||||||
def _process_streams(self, data):
|
def _process_streams(self, data):
|
||||||
|
|
|
@ -46,6 +46,34 @@ XML = ('%s<MediaContainer commandID="{command_id}" location="{location}">\n'
|
||||||
v.PLEX_PLAYLIST_TYPE_PHOTO)
|
v.PLEX_PLAYLIST_TYPE_PHOTO)
|
||||||
|
|
||||||
|
|
||||||
|
def headers_pms():
|
||||||
|
"""
|
||||||
|
Headers are different for Plex Companion - use these for PMS notifications
|
||||||
|
"""
|
||||||
|
return {
|
||||||
|
'Content-type': 'text/plain',
|
||||||
|
'Connection': 'Keep-Alive',
|
||||||
|
'Keep-Alive': 'timeout=20',
|
||||||
|
'X-Plex-Client-Identifier': v.PKC_MACHINE_IDENTIFIER,
|
||||||
|
'Access-Control-Expose-Headers': 'X-Plex-Client-Identifier',
|
||||||
|
'X-Plex-Protocol': "1.0"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def headers_companion_client():
|
||||||
|
"""
|
||||||
|
Headers are different for Plex Companion - use these for a Plex Companion
|
||||||
|
client
|
||||||
|
"""
|
||||||
|
return {
|
||||||
|
'Content-type': 'application/xml',
|
||||||
|
'Connection': 'Keep-Alive',
|
||||||
|
'X-Plex-Client-Identifier': v.PKC_MACHINE_IDENTIFIER,
|
||||||
|
'Accept-Encoding': 'gzip, deflate',
|
||||||
|
'Accept-Language': 'en,*'
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def update_player_info(playerid):
|
def update_player_info(playerid):
|
||||||
"""
|
"""
|
||||||
Updates all player info for playerid [int] in state.py.
|
Updates all player info for playerid [int] in state.py.
|
||||||
|
@ -67,6 +95,7 @@ class SubscriptionMgr(object):
|
||||||
self.protocol = "http"
|
self.protocol = "http"
|
||||||
self.port = ""
|
self.port = ""
|
||||||
self.isplaying = False
|
self.isplaying = False
|
||||||
|
self.last_timelines = {}
|
||||||
# In order to be able to signal a stop at the end
|
# In order to be able to signal a stop at the end
|
||||||
self.last_params = {}
|
self.last_params = {}
|
||||||
self.lastplayers = {}
|
self.lastplayers = {}
|
||||||
|
@ -75,19 +104,7 @@ class SubscriptionMgr(object):
|
||||||
self.playqueue = mgr.playqueue
|
self.playqueue = mgr.playqueue
|
||||||
self.request_mgr = request_mgr
|
self.request_mgr = request_mgr
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _headers():
|
|
||||||
"""
|
|
||||||
Headers are different for Plex Companion!
|
|
||||||
"""
|
|
||||||
return {
|
|
||||||
'Content-type': 'text/plain',
|
|
||||||
'Connection': 'Keep-Alive',
|
|
||||||
'Keep-Alive': 'timeout=20',
|
|
||||||
'X-Plex-Client-Identifier': v.PKC_MACHINE_IDENTIFIER,
|
|
||||||
'Access-Control-Expose-Headers': 'X-Plex-Client-Identifier',
|
|
||||||
'X-Plex-Protocol': "1.0"
|
|
||||||
}
|
|
||||||
|
|
||||||
def _server_by_host(self, host):
|
def _server_by_host(self, host):
|
||||||
if len(self.serverlist) == 1:
|
if len(self.serverlist) == 1:
|
||||||
|
@ -113,16 +130,29 @@ class SubscriptionMgr(object):
|
||||||
}
|
}
|
||||||
for typus in timelines:
|
for typus in timelines:
|
||||||
if players.get(v.KODI_PLAYLIST_TYPE_FROM_PLEX_PLAYLIST_TYPE[typus]) is None:
|
if players.get(v.KODI_PLAYLIST_TYPE_FROM_PLEX_PLAYLIST_TYPE[typus]) is None:
|
||||||
timeline = {
|
timeline = self._dict_to_xml({
|
||||||
'controllable': CONTROLLABLE[typus],
|
'controllable': CONTROLLABLE[typus],
|
||||||
'type': typus,
|
'type': typus,
|
||||||
'state': 'stopped'
|
'state': 'stopped'
|
||||||
}
|
})
|
||||||
else:
|
else:
|
||||||
timeline = self._timeline_dict(players[
|
try:
|
||||||
v.KODI_PLAYLIST_TYPE_FROM_PLEX_PLAYLIST_TYPE[typus]], typus)
|
timeline = self._dict_to_xml(self._timeline_dict(players[
|
||||||
timelines[typus] = self._dict_to_xml(timeline)
|
v.KODI_PLAYLIST_TYPE_FROM_PLEX_PLAYLIST_TYPE[typus]],
|
||||||
|
typus))
|
||||||
|
except RuntimeError:
|
||||||
|
try:
|
||||||
|
timeline = self.last_timelines[typus]
|
||||||
|
except KeyError:
|
||||||
|
# On startup
|
||||||
|
timeline = self._dict_to_xml({
|
||||||
|
'controllable': CONTROLLABLE[typus],
|
||||||
|
'type': typus,
|
||||||
|
'state': 'stopped'
|
||||||
|
})
|
||||||
|
timelines[typus] = timeline
|
||||||
location = 'fullScreenVideo' if self.isplaying else 'navigation'
|
location = 'fullScreenVideo' if self.isplaying else 'navigation'
|
||||||
|
self.last_timelines = dict(timelines)
|
||||||
timelines.update({'command_id': '{command_id}', 'location': location})
|
timelines.update({'command_id': '{command_id}', 'location': location})
|
||||||
return answ.format(**timelines)
|
return answ.format(**timelines)
|
||||||
|
|
||||||
|
@ -142,7 +172,7 @@ class SubscriptionMgr(object):
|
||||||
playqueue = self.playqueue.playqueues[playerid]
|
playqueue = self.playqueue.playqueues[playerid]
|
||||||
pos = info['position']
|
pos = info['position']
|
||||||
try:
|
try:
|
||||||
playqueue.items[pos]
|
item = playqueue.items[pos]
|
||||||
except IndexError:
|
except IndexError:
|
||||||
# E.g. for direct path playback for single item
|
# E.g. for direct path playback for single item
|
||||||
return {
|
return {
|
||||||
|
@ -150,6 +180,11 @@ class SubscriptionMgr(object):
|
||||||
'type': ptype,
|
'type': ptype,
|
||||||
'state': 'stopped'
|
'state': 'stopped'
|
||||||
}
|
}
|
||||||
|
self.isplaying = True
|
||||||
|
if item.plex_id != info['plex_id']:
|
||||||
|
# Kodi playqueue already progressed; need to wait until everything
|
||||||
|
# is loaded
|
||||||
|
raise RuntimeError
|
||||||
pbmc_server = window('pms_server')
|
pbmc_server = window('pms_server')
|
||||||
if pbmc_server:
|
if pbmc_server:
|
||||||
(self.protocol, self.server, self.port) = pbmc_server.split(':')
|
(self.protocol, self.server, self.port) = pbmc_server.split(':')
|
||||||
|
@ -180,10 +215,11 @@ class SubscriptionMgr(object):
|
||||||
'partCount': len(playqueue.items),
|
'partCount': len(playqueue.items),
|
||||||
'providerIdentifier': 'com.plexapp.plugins.library',
|
'providerIdentifier': 'com.plexapp.plugins.library',
|
||||||
}
|
}
|
||||||
|
# Get the plex id from the PKC playqueue not info, as Kodi jumps to next
|
||||||
if info['plex_id']:
|
# playqueue element way BEFORE kodi monitor onplayback is called
|
||||||
answ['key'] = '/library/metadata/%s' % info['plex_id']
|
if item.plex_id:
|
||||||
answ['ratingKey'] = info['plex_id']
|
answ['key'] = '/library/metadata/%s' % item.plex_id
|
||||||
|
answ['ratingKey'] = item.plex_id
|
||||||
# PlayQueue stuff
|
# PlayQueue stuff
|
||||||
if info['container_key']:
|
if info['container_key']:
|
||||||
answ['containerKey'] = info['container_key']
|
answ['containerKey'] = info['container_key']
|
||||||
|
@ -191,9 +227,9 @@ class SubscriptionMgr(object):
|
||||||
info['container_key'].startswith('/playQueues')):
|
info['container_key'].startswith('/playQueues')):
|
||||||
answ['playQueueID'] = playqueue.id
|
answ['playQueueID'] = playqueue.id
|
||||||
answ['playQueueVersion'] = playqueue.version
|
answ['playQueueVersion'] = playqueue.version
|
||||||
answ['playQueueItemID'] = playqueue.items[pos].id
|
answ['playQueueItemID'] = item.id
|
||||||
if playqueue.items[pos].guid:
|
if playqueue.items[pos].guid:
|
||||||
answ['guid'] = playqueue.items[pos].guid
|
answ['guid'] = item.guid
|
||||||
# Temp. token set?
|
# Temp. token set?
|
||||||
if state.PLEX_TRANSIENT_TOKEN:
|
if state.PLEX_TRANSIENT_TOKEN:
|
||||||
answ['token'] = state.PLEX_TRANSIENT_TOKEN
|
answ['token'] = state.PLEX_TRANSIENT_TOKEN
|
||||||
|
@ -222,7 +258,6 @@ class SubscriptionMgr(object):
|
||||||
if strm_id is not None:
|
if strm_id is not None:
|
||||||
# If None, then the subtitle is only present on Kodi side
|
# If None, then the subtitle is only present on Kodi side
|
||||||
answ['subtitleStreamID'] = strm_id
|
answ['subtitleStreamID'] = strm_id
|
||||||
self.isplaying = True
|
|
||||||
return answ
|
return answ
|
||||||
|
|
||||||
def signal_stop(self):
|
def signal_stop(self):
|
||||||
|
@ -268,6 +303,7 @@ class SubscriptionMgr(object):
|
||||||
# Update the PKC info with what's playing on the Kodi side
|
# Update the PKC info with what's playing on the Kodi side
|
||||||
for player in players.values():
|
for player in players.values():
|
||||||
update_player_info(player['playerid'])
|
update_player_info(player['playerid'])
|
||||||
|
self._notify_server(players)
|
||||||
if self.subscribers and state.PLAYBACK_INIT_DONE is True:
|
if self.subscribers and state.PLAYBACK_INIT_DONE is True:
|
||||||
msg = self.msg(players)
|
msg = self.msg(players)
|
||||||
if self.isplaying is True:
|
if self.isplaying is True:
|
||||||
|
@ -275,7 +311,6 @@ class SubscriptionMgr(object):
|
||||||
# drop out of the Plex Companion playback screen
|
# drop out of the Plex Companion playback screen
|
||||||
for subscriber in self.subscribers.values():
|
for subscriber in self.subscribers.values():
|
||||||
subscriber.send_update(msg, not players)
|
subscriber.send_update(msg, not players)
|
||||||
self._notify_server(players)
|
|
||||||
self.lastplayers = players
|
self.lastplayers = players
|
||||||
|
|
||||||
def _notify_server(self, players):
|
def _notify_server(self, players):
|
||||||
|
@ -293,26 +328,31 @@ class SubscriptionMgr(object):
|
||||||
|
|
||||||
def _get_pms_params(self, playerid):
|
def _get_pms_params(self, playerid):
|
||||||
info = state.PLAYER_STATES[playerid]
|
info = state.PLAYER_STATES[playerid]
|
||||||
|
playqueue = self.playqueue.playqueues[playerid]
|
||||||
|
try:
|
||||||
|
item = playqueue.items[info['position']]
|
||||||
|
except IndexError:
|
||||||
|
return self.last_params
|
||||||
status = 'paused' if info['speed'] == '0' else 'playing'
|
status = 'paused' if info['speed'] == '0' else 'playing'
|
||||||
params = {
|
params = {
|
||||||
'state': status,
|
'state': status,
|
||||||
'ratingKey': info['plex_id'],
|
'ratingKey': item.plex_id,
|
||||||
'key': '/library/metadata/%s' % info['plex_id'],
|
'key': '/library/metadata/%s' % item.plex_id,
|
||||||
'time': kodi_time_to_millis(info['time']),
|
'time': kodi_time_to_millis(info['time']),
|
||||||
'duration': kodi_time_to_millis(info['totaltime'])
|
'duration': kodi_time_to_millis(info['totaltime'])
|
||||||
}
|
}
|
||||||
if info['container_key'] is not None:
|
if info['container_key'] is not None:
|
||||||
params['containerKey'] = info['container_key']
|
params['containerKey'] = info['container_key']
|
||||||
if info['container_key'].startswith('/playQueues/'):
|
if info['container_key'].startswith('/playQueues/'):
|
||||||
playqueue = self.playqueue.playqueues[playerid]
|
|
||||||
params['playQueueVersion'] = playqueue.version
|
params['playQueueVersion'] = playqueue.version
|
||||||
params['playQueueItemID'] = playqueue.id
|
params['playQueueID'] = playqueue.id
|
||||||
|
params['playQueueItemID'] = item.id
|
||||||
self.last_params = params
|
self.last_params = params
|
||||||
return params
|
return params
|
||||||
|
|
||||||
def _send_pms_notification(self, playerid, params):
|
def _send_pms_notification(self, playerid, params):
|
||||||
serv = self._server_by_host(self.server)
|
serv = self._server_by_host(self.server)
|
||||||
xargs = self._headers()
|
xargs = headers_pms()
|
||||||
playqueue = self.playqueue.playqueues[playerid]
|
playqueue = self.playqueue.playqueues[playerid]
|
||||||
if state.PLEX_TRANSIENT_TOKEN:
|
if state.PLEX_TRANSIENT_TOKEN:
|
||||||
xargs['X-Plex-Token'] = state.PLEX_TRANSIENT_TOKEN
|
xargs['X-Plex-Token'] = state.PLEX_TRANSIENT_TOKEN
|
||||||
|
@ -409,6 +449,9 @@ class Subscriber(object):
|
||||||
Threaded POST request, because they stall due to PMS response missing
|
Threaded POST request, because they stall due to PMS response missing
|
||||||
the Content-Length header :-(
|
the Content-Length header :-(
|
||||||
"""
|
"""
|
||||||
response = DU().downloadUrl(url, postBody=msg, action_type="POST")
|
response = DU().downloadUrl(url,
|
||||||
|
postBody=msg,
|
||||||
|
action_type="POST",
|
||||||
|
headerOptions=headers_companion_client())
|
||||||
if response in (False, None, 401):
|
if response in (False, None, 401):
|
||||||
self.sub_mgr.remove_subscriber(self.uuid)
|
self.sub_mgr.remove_subscriber(self.uuid)
|
||||||
|
|
Loading…
Reference in a new issue