From 581b3d25367a6b4b245a817cd55556fdd73ada96 Mon Sep 17 00:00:00 2001 From: croneter Date: Fri, 5 Nov 2021 07:17:19 +0100 Subject: [PATCH 1/4] Don't track the old playstate anymore --- resources/lib/app/playstate.py | 6 ------ resources/lib/kodimonitor.py | 3 --- 2 files changed, 9 deletions(-) diff --git a/resources/lib/app/playstate.py b/resources/lib/app/playstate.py index 1f2c7ff4..21909b71 100644 --- a/resources/lib/app/playstate.py +++ b/resources/lib/app/playstate.py @@ -44,12 +44,6 @@ class PlayState(object): 1: {}, 2: {} } - # The LAST playstate once playback is finished - self.old_player_states = { - 0: {}, - 1: {}, - 2: {} - } self.played_info = {} # Currently playing PKC item, a PlaylistItem() diff --git a/resources/lib/kodimonitor.py b/resources/lib/kodimonitor.py index 0d3bbe5f..89296c33 100644 --- a/resources/lib/kodimonitor.py +++ b/resources/lib/kodimonitor.py @@ -34,7 +34,6 @@ class KodiMonitor(xbmc.Monitor): xbmc.Monitor.__init__(self) for playerid in app.PLAYSTATE.player_states: app.PLAYSTATE.player_states[playerid] = copy.deepcopy(app.PLAYSTATE.template) - app.PLAYSTATE.old_player_states[playerid] = copy.deepcopy(app.PLAYSTATE.template) LOG.info("Kodi monitor started.") def onScanStarted(self, library): @@ -418,8 +417,6 @@ def _playback_cleanup(ended=False): app.CONN.plex_transient_token = None for playerid in app.PLAYSTATE.active_players: status = app.PLAYSTATE.player_states[playerid] - # Remember the last played item later - app.PLAYSTATE.old_player_states[playerid] = copy.deepcopy(status) # Stop transcoding if status['playmethod'] == v.PLAYBACK_METHOD_TRANSCODE: LOG.debug('Tell the PMS to stop transcoding') From 1d82fbc0fb430e38dea90fea6033f560df6c8aa2 Mon Sep 17 00:00:00 2001 From: croneter Date: Fri, 5 Nov 2021 17:38:37 +0100 Subject: [PATCH 2/4] Tell the PMS and Plex Companion about stream changes --- resources/lib/kodimonitor.py | 44 +++--- resources/lib/playlist_func.py | 179 +++++++++++----------- resources/lib/plex_companion/playstate.py | 10 ++ 3 files changed, 122 insertions(+), 111 deletions(-) diff --git a/resources/lib/kodimonitor.py b/resources/lib/kodimonitor.py index 89296c33..c7107453 100644 --- a/resources/lib/kodimonitor.py +++ b/resources/lib/kodimonitor.py @@ -343,6 +343,7 @@ class KodiMonitor(xbmc.Monitor): # Mechanik for Plex skip intro feature if utils.settings('enableSkipIntro') == 'true': status['intro_markers'] = item.api.intro_markers() + item.playerid = playerid # Remember the currently playing item app.PLAYSTATE.item = item # Remember that this player has been active @@ -364,19 +365,9 @@ class KodiMonitor(xbmc.Monitor): if not app.SYNC.direct_paths: _notify_upnext(item) - # We need to switch to the Plex streams ONCE upon playback start if playerid == v.KODI_VIDEO_PLAYER_ID: - # The Kodi player takes forever to initialize all streams - # Especially subtitles, apparently. No way to tell when Kodi - # is done :-( - if app.APP.monitor.waitForAbort(5): - return - item.init_kodi_streams() - item.switch_to_plex_stream('video') - if utils.settings('audioStreamPick') == '0': - item.switch_to_plex_stream('audio') - if utils.settings('subtitleStreamPick') == '0': - item.switch_to_plex_stream('subtitle') + task = InitVideoStreams(item) + backgroundthread.BGThreader.addTask(task) def _on_av_change(self, data): """ @@ -386,19 +377,8 @@ class KodiMonitor(xbmc.Monitor): Example data as returned by Kodi: {'item': {'id': 5, 'type': 'movie'}, 'player': {'playerid': 1, 'speed': 1}} - - PICKING UP CHANGES ON SUBTITLES IS CURRENTLY BROKEN ON THE KODI SIDE! - Kodi subs will never change. Also see json_rpc.py """ - playerid = data['player']['playerid'] - if not playerid == v.KODI_VIDEO_PLAYER_ID: - # We're just messing with Kodi's videoplayer - return - item = app.PLAYSTATE.item - if item is None: - # Player might've quit - return - item.on_av_change(playerid) + pass def _playback_cleanup(ended=False): @@ -682,3 +662,19 @@ def _videolibrary_onupdate(data): PF.scrobble(db_item['plex_id'], 'watched') else: PF.scrobble(db_item['plex_id'], 'unwatched') + + +class InitVideoStreams(backgroundthread.Task): + """ + The Kodi player takes forever to initialize all streams Especially + subtitles, apparently. No way to tell when Kodi is done :-( + """ + + def __init__(self, item): + self.item = item + super().__init__() + + def run(self): + if app.APP.monitor.waitForAbort(5): + return + self.item.init_streams() diff --git a/resources/lib/playlist_func.py b/resources/lib/playlist_func.py index b472bb7b..41a92ff5 100644 --- a/resources/lib/playlist_func.py +++ b/resources/lib/playlist_func.py @@ -55,6 +55,7 @@ class PlaylistItem(object): self.playmethod = None self.playcount = None self.offset = None + self.playerid = None # Transcoding quality, if needed self.quality = None # If Plex video consists of several parts; part number @@ -72,11 +73,12 @@ class PlaylistItem(object): self._audio_streams = None self._subtitle_streams = None # Which Kodi streams are active? - self.current_kodi_video_stream = None - self.current_kodi_audio_stream = None - # False means "deactivated", None means "we do not have a Kodi - # equivalent for this Plex subtitle" - self.current_kodi_sub_stream = None + self._current_kodi_video_stream = None + self._current_kodi_audio_stream = None + # Kodi subs can be turned on/off additionally! + self._current_kodi_sub_stream = None + self._current_kodi_sub_stream_enabled = None + self.streams_initialized = False @property def plex_id(self): @@ -110,6 +112,48 @@ class PlaylistItem(object): self._process_streams() return self._subtitle_streams + @property + def current_kodi_video_stream(self): + return self._current_kodi_video_stream + + @current_kodi_video_stream.setter + def current_kodi_video_stream(self, value): + if value != self._current_kodi_video_stream: + self.on_kodi_video_stream_change(value) + self._current_kodi_video_stream = value + + @property + def current_kodi_audio_stream(self): + return self._current_kodi_audio_stream + + @current_kodi_audio_stream.setter + def current_kodi_audio_stream(self, value): + if value != self._current_kodi_audio_stream: + self.on_kodi_audio_stream_change(value) + self._current_kodi_audio_stream = value + + @property + def current_kodi_sub_stream_enabled(self): + return self._current_kodi_sub_stream_enabled + + @current_kodi_sub_stream_enabled.setter + def current_kodi_sub_stream_enabled(self, value): + if value != self._current_kodi_sub_stream_enabled: + self.on_kodi_subtitle_stream_change(self.current_kodi_sub_stream, + value) + self._current_kodi_sub_stream_enabled = value + + @property + def current_kodi_sub_stream(self): + return self._current_kodi_sub_stream + + @current_kodi_sub_stream.setter + def current_kodi_sub_stream(self, value): + if value != self._current_kodi_sub_stream: + self.on_kodi_subtitle_stream_change(value, + self.current_kodi_sub_stream_enabled) + self._current_kodi_sub_stream = value + @property def current_plex_video_stream(self): return self.plex_stream_index(self.current_kodi_video_stream, 'video') @@ -170,8 +214,7 @@ class PlaylistItem(object): elif stream_type == 'video': return self.video_streams - @staticmethod - def _current_index(stream_type): + def _current_index(self, stream_type): """ Kodi might tell us the wrong index for any stream after playback start Get the correct one! @@ -186,7 +229,7 @@ class PlaylistItem(object): # Really annoying: Kodi might return wrong results directly after # playback startup, e.g. a Kodi audio index of 1953718901 (!) try: - index = function(v.KODI_VIDEO_PLAYER_ID) + index = function(self.playerid) except TypeError: # No sensible reply yet pass @@ -200,31 +243,44 @@ class PlaylistItem(object): raise RuntimeError('Kodi did not tell us the correct index for %s' % stream_type) - def init_kodi_streams(self): + def init_streams(self): """ Initializes all streams after Kodi has started playing this video """ - self.current_kodi_video_stream = self._current_index('video') - self.current_kodi_audio_stream = self._current_index('audio') - self.current_kodi_sub_stream = False if not js.get_subtitle_enabled(v.KODI_VIDEO_PLAYER_ID) \ - else self._current_index('subtitle') + self.init_kodi_streams() + self.switch_to_plex_stream('video') + if utils.settings('audioStreamPick') == '0': + self.switch_to_plex_stream('audio') + if utils.settings('subtitleStreamPick') == '0': + self.switch_to_plex_stream('subtitle') + self.streams_initialized = True + + def init_kodi_streams(self): + self._current_kodi_video_stream = self._current_index('video') + self._current_kodi_audio_stream = self._current_index('audio') + self._current_kodi_sub_stream_enabled = js.get_subtitle_enabled(self.playerid) + self._current_kodi_sub_stream = self._current_index('subtitle') def plex_stream_index(self, kodi_stream_index, stream_type): """ Pass in the kodi_stream_index [int] in order to receive the Plex stream index [int]. stream_type: 'video', 'audio', 'subtitle' - Returns None if unsuccessful """ if stream_type == 'audio': return int(self.audio_streams[kodi_stream_index].get('id')) elif stream_type == 'video': return int(self.video_streams[kodi_stream_index].get('id')) elif stream_type == 'subtitle': - try: - return int(self.subtitle_streams[kodi_stream_index].get('id')) - except (IndexError, TypeError): - pass + if self.current_kodi_sub_stream_enabled: + try: + return int(self.subtitle_streams[kodi_stream_index].get('id')) + except (IndexError, TypeError): + # A subtitle that is not available on the Plex side + # deactivating subs + return 0 + else: + return 0 def kodi_stream_index(self, plex_stream_index, stream_type): """ @@ -263,16 +319,14 @@ class PlaylistItem(object): except (IndexError, TypeError): LOG.debug('Kodi subtitle change detected to a sub %s that is ' 'NOT available on the Plex side', kodi_stream_index) - self.current_kodi_sub_stream = None - return - LOG.debug('Kodi subtitle change detected: telling Plex about ' - 'switch to index %s, Plex stream id %s', - kodi_stream_index, plex_stream_index) - self.current_kodi_sub_stream = kodi_stream_index + plex_stream_index = 0 + else: + LOG.debug('Kodi subtitle change detected: telling Plex about ' + 'switch to index %s, Plex stream id %s', + kodi_stream_index, plex_stream_index) else: plex_stream_index = 0 LOG.debug('Kodi subtitle has been deactivated, telling Plex') - self.current_kodi_sub_stream = False PF.change_subtitle(plex_stream_index, self.api.part_id()) def on_kodi_audio_stream_change(self, kodi_stream_index): @@ -284,7 +338,6 @@ class PlaylistItem(object): LOG.debug('Changing Plex audio stream to %s, Kodi index %s', plex_stream_index, kodi_stream_index) PF.change_audio_stream(plex_stream_index, self.api.part_id()) - self.current_kodi_audio_stream = kodi_stream_index def on_kodi_video_stream_change(self, kodi_stream_index): """ @@ -295,24 +348,17 @@ class PlaylistItem(object): LOG.debug('Changing Plex video stream to %s, Kodi index %s', plex_stream_index, kodi_stream_index) PF.change_video_stream(plex_stream_index, self.api.part_id()) - self.current_kodi_video_stream = kodi_stream_index - def switch_to_plex_streams(self): - self.switch_to_plex_stream('video') - self.switch_to_plex_stream('audio') - self.switch_to_plex_stream('subtitle') - - @staticmethod - def _set_kodi_stream_if_different(kodi_index, typus): + def _set_kodi_stream_if_different(self, kodi_index, typus): if typus == 'video': - current = js.get_current_video_stream_index(v.KODI_VIDEO_PLAYER_ID) + current = js.get_current_video_stream_index(self.playerid) if current != kodi_index: LOG.debug('Switching video stream') app.APP.player.setVideoStream(kodi_index) else: LOG.debug('Not switching video stream (no change)') elif typus == 'audio': - current = js.get_current_audio_stream_index(v.KODI_VIDEO_PLAYER_ID) + current = js.get_current_audio_stream_index(self.playerid) if current != kodi_index: LOG.debug('Switching audio stream') app.APP.player.setAudioStream(kodi_index) @@ -326,7 +372,7 @@ class PlaylistItem(object): LOG.debug('Deactivating Kodi subtitles because the PMS ' 'told us to not show any subtitles') app.APP.player.showSubtitles(False) - self.current_kodi_sub_stream = False + self._current_kodi_sub_stream_enabled = False return LOG.debug('The PMS wants to display %s stream with Plex id %s and ' 'languageTag %s', typus, plex_index, language_tag) @@ -351,53 +397,12 @@ class PlaylistItem(object): elif typus == 'video': self._set_kodi_stream_if_different(kodi_index, 'video') if typus == 'audio': - self.current_kodi_audio_stream = kodi_index + self._current_kodi_audio_stream = kodi_index elif typus == 'subtitle': - self.current_kodi_sub_stream = kodi_index + self._current_kodi_sub_stream_enabled = True + self._current_kodi_sub_stream = kodi_index elif typus == 'video': - self.current_kodi_video_stream = kodi_index - - def on_av_change(self, playerid): - """ - Call this method if Kodi reports an "AV-Change" - (event "Player.OnAVChange") - """ - i = 0 - while i < 20: - # Really annoying: Kodi might return wrong results directly after - # playback startup, e.g. a Kodi audio index of 1953718901 (!) - kodi_video_stream = js.get_current_video_stream_index(playerid) - kodi_audio_stream = js.get_current_audio_stream_index(playerid) - if kodi_video_stream < len(self.video_streams) and kodi_audio_stream < len(self.audio_streams): - # Correct result! - break - i += 1 - if app.APP.monitor.waitForAbort(0.1): - # Need to quit PKC - return - else: - LOG.error('Could not get sensible Kodi indices! kodi_video_stream ' - '%s, kodi_audio_stream %s', - kodi_video_stream, kodi_audio_stream) - return - kodi_video_stream = self._current_index('video') - kodi_audio_stream = self._current_index('audio') - sub_enabled = js.get_subtitle_enabled(playerid) - kodi_sub_stream = self._current_index('subtitle') - # Audio - if kodi_audio_stream != self.current_kodi_audio_stream: - self.on_kodi_audio_stream_change(kodi_audio_stream) - # Video - if kodi_video_stream != self.current_kodi_video_stream: - self.on_kodi_video_stream_change(kodi_audio_stream) - # Subtitles - CURRENTLY BROKEN ON THE KODI SIDE! - # current_kodi_sub_stream may also be zero - subs_off = (None, False) - if ((sub_enabled and self.current_kodi_sub_stream in subs_off) - or (not sub_enabled and self.current_kodi_sub_stream not in subs_off) - or (kodi_sub_stream is not None - and kodi_sub_stream != self.current_kodi_sub_stream)): - self.on_kodi_subtitle_stream_change(kodi_sub_stream, sub_enabled) + self._current_kodi_video_stream = kodi_index def on_plex_stream_change(self, video_stream_id=None, audio_stream_id=None, subtitle_stream_id=None): @@ -412,7 +417,7 @@ class PlaylistItem(object): 'the video stream!', video_stream_id) return self._set_kodi_stream_if_different(kodi_index, 'video') - self.current_kodi_video_stream = kodi_index + self._current_kodi_video_stream = kodi_index if audio_stream_id is not None: try: kodi_index = self.kodi_stream_index(audio_stream_id, 'audio') @@ -421,24 +426,24 @@ class PlaylistItem(object): 'the video stream!', audio_stream_id) return self._set_kodi_stream_if_different(kodi_index, 'audio') - self.current_kodi_audio_stream = kodi_index + self._current_kodi_audio_stream = kodi_index if subtitle_stream_id is not None: if subtitle_stream_id == 0: app.APP.player.showSubtitles(False) - kodi_index = False + self._current_kodi_sub_stream_enabled = False else: try: kodi_index = self.kodi_stream_index(subtitle_stream_id, 'subtitle') except ValueError: - kodi_index = None LOG.debug('The PMS wanted to change subs, but we could not' ' match the sub with id %s to a Kodi sub', subtitle_stream_id) else: app.APP.player.setSubtitleStream(kodi_index) app.APP.player.showSubtitles(True) - self.current_kodi_sub_stream = kodi_index + self._current_kodi_sub_stream_enabled = True + self._current_kodi_sub_stream = kodi_index def playlist_item_from_kodi(kodi_item): diff --git a/resources/lib/plex_companion/playstate.py b/resources/lib/plex_companion/playstate.py index 16ba8417..b1872bff 100644 --- a/resources/lib/plex_companion/playstate.py +++ b/resources/lib/plex_companion/playstate.py @@ -66,6 +66,9 @@ def timeline_dict(playerid, typus): 'type': typus, 'state': 'stopped' } + if typus == v.PLEX_PLAYLIST_TYPE_VIDEO and not item.streams_initialized: + # Not ready yet to send updates + raise TypeError() protocol, url, port = split_server_uri(app.CONN.server) status = 'paused' if int(info['speed']) == 0 else 'playing' duration = timing.kodi_time_to_millis(info['totaltime']) @@ -115,6 +118,13 @@ def timeline_dict(playerid, typus): answ['token'] = playqueue.plex_transient_token # Process audio and subtitle streams if typus == v.PLEX_PLAYLIST_TYPE_VIDEO: + item.current_kodi_video_stream = info['currentvideostream']['index'] + item.current_kodi_audio_stream = info['currentaudiostream']['index'] + item.current_kodi_sub_stream_enabled = info['subtitleenabled'] + try: + item.current_kodi_sub_stream = info['currentsubtitle']['index'] + except KeyError: + item.current_kodi_sub_stream = None answ['videoStreamID'] = str(item.current_plex_video_stream) answ['audioStreamID'] = str(item.current_plex_audio_stream) # Mind the zero - meaning subs are deactivated From f2e2be6da7a21d7e89c0bad1d9922c1e2f4e2aaa Mon Sep 17 00:00:00 2001 From: croneter Date: Fri, 5 Nov 2021 17:58:16 +0100 Subject: [PATCH 3/4] Adjust some Companion headers --- resources/lib/plex_companion/common.py | 2 +- resources/lib/variables.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/resources/lib/plex_companion/common.py b/resources/lib/plex_companion/common.py index 14f6e4c1..a957a785 100644 --- a/resources/lib/plex_companion/common.py +++ b/resources/lib/plex_companion/common.py @@ -25,7 +25,7 @@ def proxy_headers(): def proxy_params(): params = { 'deviceClass': 'pc', - 'protocolCapabilities': 'timeline,playback,navigation,playqueues', + 'protocolCapabilities': 'timeline,playback,navigation,mirror,playqueues', 'protocolVersion': 3 } if app.ACCOUNT.pms_token: diff --git a/resources/lib/variables.py b/resources/lib/variables.py index e457a5bd..5219af5a 100644 --- a/resources/lib/variables.py +++ b/resources/lib/variables.py @@ -445,9 +445,9 @@ CONTENT_FROM_PLEX_TYPE = { # Plex profile for transcoding and direct streaming # Uses the empty Generic.xml at Plex Media Server/Resources/Profiles for any # Playback decisions -PLATFORM = 'Generic' +PLATFORM = 'Kodi' # Version seems to be irrelevant for the generic platform -PLATFORM_VERSION = '1.0.0' +PLATFORM_VERSION = KODILONGVERSION # Overrides (replace=true) any existing entries in generic.xml STREAMING_HEADERS = { 'X-Plex-Client-Profile-Extra': From 5714d4fb0a7424e0dc6af66ece1951fd862127fd Mon Sep 17 00:00:00 2001 From: croneter Date: Fri, 5 Nov 2021 19:12:41 +0100 Subject: [PATCH 4/4] Fix stream init if quickly changing to the next video --- resources/lib/playlist_func.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/resources/lib/playlist_func.py b/resources/lib/playlist_func.py index 41a92ff5..e23e81ab 100644 --- a/resources/lib/playlist_func.py +++ b/resources/lib/playlist_func.py @@ -247,6 +247,9 @@ class PlaylistItem(object): """ Initializes all streams after Kodi has started playing this video """ + if not app.PLAYSTATE.item == self: + # Already stopped playback or skipped to the next one + return self.init_kodi_streams() self.switch_to_plex_stream('video') if utils.settings('audioStreamPick') == '0':