From 38a2d88e008414369937b0e04f41a81f7f7023b7 Mon Sep 17 00:00:00 2001 From: MoojMidge <56883549+MoojMidge@users.noreply.github.com> Date: Fri, 13 Dec 2024 06:08:01 +1100 Subject: [PATCH] Workaround Kodi crashes during forced playlist playback --- .../player/xbmc/xbmc_playlist_player.py | 47 ++++++++++++------- .../kodion/plugin/xbmc/xbmc_plugin.py | 28 +++++------ .../youtube_plugin/youtube/helper/yt_play.py | 35 +++++++++----- 3 files changed, 66 insertions(+), 44 deletions(-) diff --git a/resources/lib/youtube_plugin/kodion/player/xbmc/xbmc_playlist_player.py b/resources/lib/youtube_plugin/kodion/player/xbmc/xbmc_playlist_player.py index fee3305e7..3cb1aa0e5 100644 --- a/resources/lib/youtube_plugin/kodion/player/xbmc/xbmc_playlist_player.py +++ b/resources/lib/youtube_plugin/kodion/player/xbmc/xbmc_playlist_player.py @@ -197,44 +197,50 @@ def add_items(self, items, loads=False): return len(items) - def play_playlist_item(self, position, resume=False): + def play_playlist_item(self, position, resume=False, defer=False): """ Function to play item in playlist from a specified position, where the first item in the playlist is position 1 """ + context = self._context + playlist_id = self._playlist.getPlayListId() + if position == 'next': position, _ = self.get_position(offset=1) - context = self._context if not position: context.log_warning('Unable to play from playlist position: {0}' .format(position)) return - context.log_debug('Playing from playlist position: {0}' - .format(position)) + context.log_debug('Playing from playlist: {id}, position: {position}' + .format(id=playlist_id, + position=position)) if not resume: - self._player.play(self._playlist, startpos=position - 1) - return + command = 'Playlist.PlayOffset({type},{position})'.format( + type=self.PLAYLIST_MAP.get(playlist_id) or 'video', + position=position - 1, + ) + if defer: + return ''.join(('command://', command)) + return self._context.execute(command) + # JSON Player.Open can be too slow but is needed if resuming is enabled jsonrpc(method='Player.Open', - params={'item': {'playlistid': self._playlist.getPlayListId(), + params={'item': {'playlistid': playlist_id, # Convert 1 indexed to 0 indexed position 'position': position - 1}}, options={'resume': True}, no_response=True) - def play(self, playlist_index=-1): + def play(self, playlist_index=-1, defer=False): """ - We call the player in this way, because 'Player.play(...)' will call the addon again while the instance is - running. This is somehow shitty, because we couldn't release any resources and in our case we couldn't release - the cache. So this is the solution to prevent a locked database (sqlite). + We call the player in this way, because 'Player.play(...)' will call the + addon again while the instance is running. This is somehow shitty, + because we couldn't release any resources and in our case we couldn't + release the cache. So this is the solution to prevent a locked database + (sqlite) and Kodi crashing. """ - playlist_type = self.PLAYLIST_MAP.get(self._playlist.getPlayListId()) - self._context.execute( - 'Playlist.PlayOffset({type},{position})' - .format(type=playlist_type or 'video', position=playlist_index) - ) """ playlist = None @@ -249,6 +255,15 @@ def play(self, playlist_index=-1): xbmc.Player().play(item=playlist) """ + playlist_type = self.PLAYLIST_MAP.get(self._playlist.getPlayListId()) + command = 'Playlist.PlayOffset({type},{position})'.format( + type=playlist_type or 'video', + position=playlist_index, + ) + if defer: + return ''.join(('command://', command)) + return self._context.execute(command) + def get_position(self, offset=0): """ Function to get current playlist position and number of remaining diff --git a/resources/lib/youtube_plugin/kodion/plugin/xbmc/xbmc_plugin.py b/resources/lib/youtube_plugin/kodion/plugin/xbmc/xbmc_plugin.py index a20f6a6ea..ec1fd00d7 100644 --- a/resources/lib/youtube_plugin/kodion/plugin/xbmc/xbmc_plugin.py +++ b/resources/lib/youtube_plugin/kodion/plugin/xbmc/xbmc_plugin.py @@ -141,11 +141,11 @@ def run(self, provider, context, focused=None): if max_wait_time < 0: context.log_error('Multiple busy dialogs active' ' - Unable to restart playback') + command = playlist_player.play_playlist_item(position, + defer=True) result, post_run_action = self.uri_action( context, - 'command://Playlist.PlayOffset({type},{position})' - .format(type='video', - position=(position - 1)), + command, ) succeeded = False continue @@ -229,24 +229,20 @@ def run(self, provider, context, focused=None): result = options.get(force_resolve) if result and result.__class__.__name__ in self._PLAY_ITEM_MAP: - uri = result.get_uri() - - if result.playable: + if options.get(provider.RESULT_FORCE_PLAY) or not result.playable: + result, post_run_action = self.uri_action( + context, + result.get_uri() + ) + else: item = self._PLAY_ITEM_MAP[result.__class__.__name__]( context, result, show_fanart=show_fanart, ) - uri = result.get_uri() - if options.get(provider.RESULT_FORCE_PLAY): - playlist_player = context.get_playlist_player() - playlist_player.play_item(item=uri, listitem=item) - else: - xbmcplugin.setResolvedUrl( - handle, succeeded=True, listitem=item - ) - else: - result, post_run_action = self.uri_action(context, uri) + xbmcplugin.setResolvedUrl( + handle, succeeded=True, listitem=item + ) if item_count: context.apply_content() diff --git a/resources/lib/youtube_plugin/youtube/helper/yt_play.py b/resources/lib/youtube_plugin/youtube/helper/yt_play.py index 6c57ae43d..7282ecda9 100644 --- a/resources/lib/youtube_plugin/youtube/helper/yt_play.py +++ b/resources/lib/youtube_plugin/youtube/helper/yt_play.py @@ -426,11 +426,11 @@ def process_items_for_playlist(context, elif play_from == 'end': play_from = -1 if isinstance(play_from, int): - playlist_position = play_from + position = play_from elif isinstance(play_from, string_type): - playlist_position = None + position = None else: - playlist_position = False + position = False # add videos to playlist num_items = 0 @@ -438,25 +438,36 @@ def process_items_for_playlist(context, if not item.playable: continue playlist_player.add(item) - if playlist_position is None and item.video_id == play_from: - playlist_position = num_items num_items += 1 + if position is None and item.video_id == play_from: + position = num_items if not num_items: return False if isinstance(play_from, int): if num_items >= play_from > 0: - playlist_position = play_from - 1 + position = play_from elif play_from < 0: - playlist_position = num_items + play_from + position = num_items + play_from else: - playlist_position = 0 - elif not playlist_position: - playlist_position = 0 + position = 1 + elif not position: + position = 1 if action == 'queue': return items if action == 'play': - playlist_player.play_playlist_item(playlist_position + 1) - return items[playlist_position] + ui = context.get_ui() + max_wait_time = position + while ui.busy_dialog_active() or playlist_player.size() < position: + max_wait_time -= 1 + if max_wait_time < 0: + command = playlist_player.play_playlist_item(position, + defer=True) + return UriItem(command) + context.sleep(1) + else: + playlist_player.play_playlist_item(position) + return + return items[position - 1]