From b1d59e65bec8c55b5bdc60035a8e09d8ac121b27 Mon Sep 17 00:00:00 2001 From: croneter Date: Mon, 5 Aug 2019 18:21:54 +0200 Subject: [PATCH 1/4] Fix Plex Companion device restarting playback when reconnecting to PKC --- resources/lib/playlist_func.py | 15 +++++++++++ resources/lib/plex_companion.py | 44 ++++++++++++++++++++++++++++----- 2 files changed, 53 insertions(+), 6 deletions(-) diff --git a/resources/lib/playlist_func.py b/resources/lib/playlist_func.py index d32912f8..47ce5670 100644 --- a/resources/lib/playlist_func.py +++ b/resources/lib/playlist_func.py @@ -129,6 +129,21 @@ class Playqueue_Object(object): self.kodi_playlist_playback = False LOG.debug('Playlist cleared: %s', self) + def position_from_plex_id(self, plex_id): + """ + Returns the position [int] for the very first item with plex_id [int] + (Plex seems uncapable of adding the same element multiple times to a + playqueue or playlist) + + Raises KeyError if not found + """ + for position, item in enumerate(self.items): + if item.plex_id == plex_id: + break + else: + raise KeyError('Did not find plex_id %s in %s', plex_id, self) + return position + class Playlist_Item(object): """ diff --git a/resources/lib/plex_companion.py b/resources/lib/plex_companion.py index dbdea11e..5571aef7 100644 --- a/resources/lib/plex_companion.py +++ b/resources/lib/plex_companion.py @@ -55,7 +55,16 @@ def update_playqueue_from_PMS(playqueue, except AttributeError: LOG.error('Could now download playqueue %s', playqueue_id) return - playqueue.clear() + if playqueue.id == playqueue_id: + # This seems to be happening ONLY if a Plex Companion device + # reconnects and Kodi is already playing something - silly, really + # For all other cases, a new playqueue is generated by Plex + LOG.debug('Update for existing playqueue detected') + new = False + else: + new = True + playqueue.clear() + # Get new metadata for the playqueue first try: PL.get_playlist_details_from_xml(playqueue, xml) except PL.PlaylistError: @@ -63,10 +72,33 @@ def update_playqueue_from_PMS(playqueue, return playqueue.repeat = 0 if not repeat else int(repeat) playqueue.plex_transient_token = transient_token - playback.play_xml(playqueue, - xml, - offset=offset, - start_plex_id=start_plex_id) + if new: + playback.play_xml(playqueue, + xml, + offset=offset, + start_plex_id=start_plex_id) + return + # Updates to playqueues could potentially become a bit more ugly... + if app.APP.is_playing: + try: + playerid = js.get_player_ids()[0] + except IndexError: + LOG.error('Unexpectately could not get Kodi player id') + return + if app.PLAYSTATE.player_states[playerid]['plex_id'] == start_plex_id: + # Nothing to do - let's not seek to avoid jumps in playback + return + pos = playqueue.position_from_plex_id(start_plex_id) + LOG.debug('Skipping to position %s for %s', pos, playqueue) + js.skipto(pos) + if offset: + js.seek_to(offset) + return + # Need to initiate playback again using our existing playqueue + app.APP.player.play(playqueue.kodi_pl, + None, + False, + playqueue.position_from_plex_id(start_plex_id)) class PlexCompanion(backgroundthread.KillableThread): @@ -165,7 +197,7 @@ class PlexCompanion(backgroundthread.KillableThread): update_playqueue_from_PMS(playqueue, playqueue_id=container_key, repeat=query.get('repeat'), - offset=data.get('offset'), + offset=utils.cast(int, data.get('offset')), transient_token=data.get('token'), start_plex_id=key) From f5026b637d939b4665d971cd3823fabac2b37238 Mon Sep 17 00:00:00 2001 From: croneter Date: Thu, 8 Aug 2019 19:54:12 +0200 Subject: [PATCH 2/4] Fix rare AttributeError when Kodi exits --- resources/lib/playlist_func.py | 8 ++++++-- resources/lib/plex_companion.py | 5 ++--- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/resources/lib/playlist_func.py b/resources/lib/playlist_func.py index d32912f8..6f011025 100644 --- a/resources/lib/playlist_func.py +++ b/resources/lib/playlist_func.py @@ -487,6 +487,8 @@ def update_playlist_from_PMS(playlist, playlist_id=None, xml=None): need to fetch a new playqueue If an xml is passed in, the playlist will be overwritten with its info + + Raises PlaylistError if something went wront """ if xml is None: xml = get_PMS_playlist(playlist, playlist_id) @@ -523,6 +525,8 @@ def init_plex_playqueue(playlist, plex_id=None, kodi_item=None): xml = DU().downloadUrl(url="{server}/%ss" % playlist.kind, action_type="POST", parameters=params) + if xml in (None, 401): + raise PlaylistError('Did not receive a valid xml from the PMS') get_playlist_details_from_xml(playlist, xml) # Need to get the details for the playlist item item = playlist_item_from_xml(xml[0]) @@ -706,7 +710,7 @@ def get_PMS_playlist(playlist, playlist_id=None): Fetches the PMS playlist/playqueue as an XML. Pass in playlist_id if we need to fetch a new playlist - Returns None if something went wrong + Raises PlaylistError if something went wrong """ playlist_id = playlist_id if playlist_id else playlist.id if playlist.kind == 'playList': @@ -716,7 +720,7 @@ def get_PMS_playlist(playlist, playlist_id=None): try: xml.attrib except AttributeError: - xml = None + raise PlaylistError('Did not get a valid xml') return xml diff --git a/resources/lib/plex_companion.py b/resources/lib/plex_companion.py index dbdea11e..ac400129 100644 --- a/resources/lib/plex_companion.py +++ b/resources/lib/plex_companion.py @@ -49,10 +49,9 @@ def update_playqueue_from_PMS(playqueue, if transient_token is None: transient_token = playqueue.plex_transient_token with app.APP.lock_playqueues: - xml = PL.get_PMS_playlist(playqueue, playqueue_id) try: - xml.attrib - except AttributeError: + xml = PL.get_PMS_playlist(playqueue, playqueue_id) + except PL.PlaylistError: LOG.error('Could now download playqueue %s', playqueue_id) return playqueue.clear() From b7f13a8842235803502390c2d65f270e7f09de81 Mon Sep 17 00:00:00 2001 From: croneter Date: Fri, 9 Aug 2019 08:16:17 +0200 Subject: [PATCH 3/4] Fix extras not playing when path substitution is enabled --- resources/lib/variables.py | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/resources/lib/variables.py b/resources/lib/variables.py index 2656d58f..b21ac258 100644 --- a/resources/lib/variables.py +++ b/resources/lib/variables.py @@ -362,7 +362,7 @@ KODI_PLAYLIST_TYPE_FROM_KODI_TYPE = { REMAP_TYPE_FROM_PLEXTYPE = { PLEX_TYPE_MOVIE: 'movie', - PLEX_TYPE_CLIP: 'clip', + PLEX_TYPE_CLIP: 'movie', PLEX_TYPE_SHOW: 'tv', PLEX_TYPE_SEASON: 'tv', PLEX_TYPE_EPISODE: 'tv', @@ -400,20 +400,6 @@ TRANSLATION_FROM_PLEXTYPE = { PLEX_TYPE_PHOTO: 1, } -REMAP_TYPE_FROM_PLEXTYPE = { - 'movie': 'movie', - 'show': 'tv', - 'season': 'tv', - 'episode': 'tv', - 'artist': 'music', - 'album': 'music', - 'song': 'music', - 'track': 'music', - 'clip': 'clip', - 'photo': 'photo' -} - - PLEX_TYPE_FROM_WEBSOCKET = { 1: PLEX_TYPE_MOVIE, 2: PLEX_TYPE_SHOW, From 0d7a1b3a9f1828b550d508848c2b3e07801bbcf8 Mon Sep 17 00:00:00 2001 From: croneter Date: Fri, 9 Aug 2019 13:40:18 +0200 Subject: [PATCH 4/4] Change way item is added to Plex playqueue by using PMS machine identifier --- resources/lib/playback.py | 9 ++------- resources/lib/playlist_func.py | 25 ++++++++----------------- resources/lib/plex_functions.py | 9 ++++----- 3 files changed, 14 insertions(+), 29 deletions(-) diff --git a/resources/lib/playback.py b/resources/lib/playback.py index 441d71b6..8bbc706c 100644 --- a/resources/lib/playback.py +++ b/resources/lib/playback.py @@ -234,14 +234,9 @@ def _playback_init(plex_id, plex_type, playqueue, pos): playqueue.clear() if plex_type != v.PLEX_TYPE_CLIP: # Post to the PMS to create a playqueue - in any case due to Companion - section_uuid = xml.attrib.get('librarySectionUUID') - xml = PF.init_plex_playqueue(plex_id, - section_uuid, - mediatype=plex_type, - trailers=trailers) + xml = PF.init_plex_playqueue(plex_id, plex_type, trailers=trailers) if xml is None: - LOG.error('Could not get a playqueue xml for plex id %s, UUID %s', - plex_id, section_uuid) + LOG.error('Could not get a playqueue xml for plex id %s', plex_id) # "Play error" utils.dialog('notification', utils.lang(29999), diff --git a/resources/lib/playlist_func.py b/resources/lib/playlist_func.py index d32912f8..68d7cc6a 100644 --- a/resources/lib/playlist_func.py +++ b/resources/lib/playlist_func.py @@ -137,11 +137,10 @@ class Playlist_Item(object): id = None [int] Plex playlist/playqueue id, e.g. playQueueItemID plex_id = None [int] Plex unique item id, "ratingKey" plex_type = None [str] Plex type, e.g. 'movie', 'clip' - plex_uuid = None [str] Plex librarySectionUUID kodi_id = None [int] Kodi unique kodi id (unique only within type!) kodi_type = None [str] Kodi type: 'movie' file = None [str] Path to the item's file. STRING!! - uri = None [str] Weird Plex uri path involving plex_uuid. STRING! + uri = None [str] PMS path to item; will be auto-set with plex_id guid = None [str] Weird Plex guid xml = None [etree] XML from PMS, 1 lvl below playmethod = None [str] either 'DirectPlay', 'DirectStream', 'Transcode' @@ -154,11 +153,10 @@ class Playlist_Item(object): self._id = None self._plex_id = None self.plex_type = None - self.plex_uuid = None self._kodi_id = None self.kodi_type = None self.file = None - self.uri = None + self._uri = None self.guid = None self.xml = None self.playmethod = None @@ -182,6 +180,12 @@ class Playlist_Item(object): if not isinstance(value, int) and value is not None: raise TypeError('Passed %s instead of int!' % type(value)) self._plex_id = value + self._uri = ('server://%s/com.plexapp.plugins.library/library/metadata/%s' % + (app.CONN.machine_identifier, value)) + + @property + def uri(self): + return self._uri @property def id(self): @@ -238,11 +242,9 @@ class Playlist_Item(object): "'id': {self.id}, " "'plex_id': {self.plex_id}, " "'plex_type': '{self.plex_type}', " - "'plex_uuid': '{self.plex_uuid}', " "'kodi_id': {self.kodi_id}, " "'kodi_type': '{self.kodi_type}', " "'file': '{self.file}', " - "'uri': '{self.uri}', " "'guid': '{self.guid}', " "'playmethod': '{self.playmethod}', " "'playcount': {self.playcount}, " @@ -328,7 +330,6 @@ def playlist_item_from_kodi(kodi_item): if db_item: item.plex_id = db_item['plex_id'] item.plex_type = db_item['plex_type'] - item.plex_uuid = db_item['plex_id'] # we dont need the uuid yet :-) item.file = kodi_item.get('file') if item.plex_id is None and item.file is not None: try: @@ -338,13 +339,6 @@ def playlist_item_from_kodi(kodi_item): query = dict(utils.parse_qsl(query)) item.plex_id = utils.cast(int, query.get('plex_id')) item.plex_type = query.get('itemType') - if item.plex_id is None and item.file is not None: - item.uri = ('library://whatever/item/%s' - % utils.quote(item.file, safe='')) - else: - # TO BE VERIFIED - PLEX DOESN'T LIKE PLAYLIST ADDS IN THIS MANNER - item.uri = ('library://%s/item/library%%2Fmetadata%%2F%s' % - (item.plex_uuid, item.plex_id)) LOG.debug('Made playlist item from Kodi: %s', item) return item @@ -408,9 +402,6 @@ def playlist_item_from_plex(plex_id): item.kodi_type = db_item['kodi_type'] else: raise KeyError('Could not find plex_id %s in database' % plex_id) - item.plex_uuid = plex_id - item.uri = ('library://%s/item/library%%2Fmetadata%%2F%s' % - (item.plex_uuid, plex_id)) LOG.debug('Made playlist item from plex: %s', item) return item diff --git a/resources/lib/plex_functions.py b/resources/lib/plex_functions.py index 1b7e8fbc..a89fbf16 100644 --- a/resources/lib/plex_functions.py +++ b/resources/lib/plex_functions.py @@ -820,16 +820,15 @@ def get_plex_sections(): return xml -def init_plex_playqueue(plex_id, librarySectionUUID, mediatype='movie', - trailers=False): +def init_plex_playqueue(plex_id, plex_type, trailers=False): """ Returns raw API metadata XML dump for a playlist with e.g. trailers. """ url = "{server}/playQueues" args = { - 'type': mediatype, - 'uri': ('library://{0}/item/%2Flibrary%2Fmetadata%2F{1}'.format( - librarySectionUUID, plex_id)), + 'type': plex_type, + 'uri': ('server://%s/com.plexapp.plugins.library/library/metadata/%s' % + (app.CONN.machine_identifier, plex_id)), 'includeChapters': '1', 'shuffle': '0', 'repeat': '0'