Fix Plex Companion headers & URL arguments

This commit is contained in:
tomkat83 2018-01-02 11:48:44 +01:00
parent 3a9f65d908
commit 14183cccca
2 changed files with 46 additions and 18 deletions

View file

@ -141,7 +141,8 @@ class DownloadUtils():
def downloadUrl(self, url, action_type="GET", postBody=None, def downloadUrl(self, url, action_type="GET", postBody=None,
parameters=None, authenticate=True, headerOptions=None, parameters=None, authenticate=True, headerOptions=None,
verifySSL=True, timeout=None, return_response=False): verifySSL=True, timeout=None, return_response=False,
headerOverride=None):
""" """
Override SSL check with verifySSL=False Override SSL check with verifySSL=False
@ -172,7 +173,10 @@ class DownloadUtils():
# User is not (yet) authenticated. Used to communicate with # User is not (yet) authenticated. Used to communicate with
# plex.tv and to check for PMS servers # plex.tv and to check for PMS servers
s = requests s = requests
headerOptions = self.getHeader(options=headerOptions) if not headerOverride:
headerOptions = self.getHeader(options=headerOptions)
else:
headerOptions = headerOverride
if settings('sslcert') != 'None': if settings('sslcert') != 'None':
kwargs['cert'] = settings('sslcert') kwargs['cert'] = settings('sslcert')

View file

@ -45,18 +45,31 @@ XML = ('%s<MediaContainer commandID="{command_id}" location="{location}">\n'
v.PLEX_PLAYLIST_TYPE_AUDIO, v.PLEX_PLAYLIST_TYPE_AUDIO,
v.PLEX_PLAYLIST_TYPE_PHOTO) v.PLEX_PLAYLIST_TYPE_PHOTO)
# Headers are different for Plex Companion - use these for PMS notifications
HEADERS_PMS = {
'Connection': 'Keep-Alive',
'Accept': 'text/plain, */*; q=0.01',
'Accept-Language': 'en',
'User-Agent': '%s %s (%s)' % (v.ADDON_NAME, v.ADDON_VERSION, v.PLATFORM),
'Accept-Encoding': 'gzip, deflate'
}
def headers_pms():
def params_pms():
""" """
Headers are different for Plex Companion - use these for PMS notifications Returns the url parameters for communicating with the PMS
""" """
return { return {
'Content-type': 'text/plain', # 'X-Plex-Client-Capabilities': ['protocols=shoutcast,http-video;videoDecoders=h264{profile:high&resolution:2160&level:52};audioDecoders=mp3,aac,dts{bitrate:800000&channels:2},ac3{bitrate:800000&channels:2}'],
'Connection': 'Keep-Alive',
'Keep-Alive': 'timeout=20',
'X-Plex-Client-Identifier': v.PKC_MACHINE_IDENTIFIER, 'X-Plex-Client-Identifier': v.PKC_MACHINE_IDENTIFIER,
'Access-Control-Expose-Headers': 'X-Plex-Client-Identifier', 'X-Plex-Device': v.PLATFORM,
'X-Plex-Protocol': "1.0" 'X-Plex-Device-Name': v.DEVICENAME,
# 'X-Plex-Device-Screen-Resolution': ['1916x1018,1920x1080'],
'X-Plex-Platform': v.PLATFORM,
'X-Plex-Product': v.ADDON_NAME,
'X-Plex-Version': v.ADDON_VERSION,
'hasMDE': '1',
# 'X-Plex-Session-Identifier': ['vinuvirm6m20iuw9c4cx1dcx'],
} }
@ -69,6 +82,12 @@ def headers_companion_client():
'Content-type': 'application/xml', 'Content-type': 'application/xml',
'Connection': 'Keep-Alive', 'Connection': 'Keep-Alive',
'X-Plex-Client-Identifier': v.PKC_MACHINE_IDENTIFIER, 'X-Plex-Client-Identifier': v.PKC_MACHINE_IDENTIFIER,
'X-Plex-Device': v.PLATFORM,
'X-Plex-Device-Name': v.DEVICENAME,
'X-Plex-Platform': v.PLATFORM,
'X-Plex-Product': v.ADDON_NAME,
'X-Plex-Version': v.ADDON_VERSION,
'X-Plex-Provides': 'client,controller,player,pubsub-player',
'Accept-Encoding': 'gzip, deflate', 'Accept-Encoding': 'gzip, deflate',
'Accept-Language': 'en,*' 'Accept-Language': 'en,*'
} }
@ -342,18 +361,19 @@ class SubscriptionMgr(object):
'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/'):
params['playQueueVersion'] = playqueue.version # params['playQueueVersion'] = playqueue.version
params['playQueueID'] = playqueue.id # params['playQueueID'] = playqueue.id
params['playQueueItemID'] = item.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 = headers_pms()
playqueue = self.playqueue.playqueues[playerid] playqueue = self.playqueue.playqueues[playerid]
xargs = params_pms()
xargs.update(params)
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
elif playqueue.plex_transient_token: elif playqueue.plex_transient_token:
@ -363,9 +383,12 @@ class SubscriptionMgr(object):
url = '%s://%s:%s/:/timeline' % (serv.get('protocol', 'http'), url = '%s://%s:%s/:/timeline' % (serv.get('protocol', 'http'),
serv.get('server', 'localhost'), serv.get('server', 'localhost'),
serv.get('port', '32400')) serv.get('port', '32400'))
DU().downloadUrl(url, parameters=params, headerOptions=xargs) DU().downloadUrl(url,
authenticate=False,
parameters=xargs,
headerOverride=HEADERS_PMS)
LOG.debug("Sent server notification with parameters: %s to %s", LOG.debug("Sent server notification with parameters: %s to %s",
params, url) xargs, url)
@LOCKER.lockthis @LOCKER.lockthis
def add_subscriber(self, protocol, host, port, uuid, command_id): def add_subscriber(self, protocol, host, port, uuid, command_id):
@ -439,12 +462,13 @@ class Subscriber(object):
def _threaded_send(self, url, msg): def _threaded_send(self, url, msg):
""" """
Threaded POST request, because they stall due to PMS response missing Threaded POST request, because they stall due to response missing
the Content-Length header :-( the Content-Length header :-(
""" """
response = DU().downloadUrl(url, response = DU().downloadUrl(url,
postBody=msg,
action_type="POST", action_type="POST",
headerOptions=headers_companion_client()) postBody=msg,
authenticate=False,
headerOverride=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)