diff --git a/jellyfin_kodi/objects/actions.py b/jellyfin_kodi/objects/actions.py index 0e313219..86a21cbf 100644 --- a/jellyfin_kodi/objects/actions.py +++ b/jellyfin_kodi/objects/actions.py @@ -80,10 +80,14 @@ class Actions(object): self.set_playlist(item, listitem, db_id, transcode) - self.stack[0][1].setPath(self.stack[0][0]) + # Using playlist approach for Cinema mode + if len(self.stack) > 1: + self._play_stack() + else: + self.stack[0][1].setPath(self.stack[0][0]) - if len(sys.argv) > 1: - xbmcplugin.setResolvedUrl(int(sys.argv[1]), True, self.stack[0][1]) + if len(sys.argv) > 1: + xbmcplugin.setResolvedUrl(int(sys.argv[1]), True, self.stack[0][1]) def set_playlist(self, item, listitem, db_id=None, transcode=False): """Verify seektime, set intros, set main item and set additional parts. @@ -149,6 +153,55 @@ class Actions(object): window("jellyfin.skip.%s" % intro["Id"], value="true") + def _play_stack(self): + """Play multiple items from the stack using a playlist. + + This is used for Cinema Mode (intros + movie) and multi-part videos. + Unlike setResolvedUrl which can only handle a single file, this method + creates a Kodi playlist and adds all items from the stack. + + The key challenge is that when called from a plugin:// URL context, Kodi + expects setResolvedUrl() to provide the playback URL. But setResolvedUrl + can only handle a single item. To work around this, we: + 1. First cancel the plugin resolution with setResolvedUrl(False) + 2. Then build and start our playlist independently + """ + playlist = xbmc.PlayList(xbmc.PLAYLIST_VIDEO) + player = xbmc.Player() + + # Stop any current playback first and wait for it to fully stop + if player.isPlaying(): + player.stop() + # Wait for playback to fully stop (up to 3 seconds) + for _ in range(30): + xbmc.sleep(100) + if not player.isPlaying(): + break + + # Clear the playlist before cancelling plugin resolution + playlist.clear() + + # Cancel the plugin resolution - this must happen after stopping playback + # to prevent race conditions with the previous playback session + if len(sys.argv) > 1: + xbmcplugin.setResolvedUrl(int(sys.argv[1]), False, xbmcgui.ListItem()) + + xbmc.executebuiltin("ActivateWindow(busydialognocancel)") + + # Small delay to ensure plugin resolution is fully cancelled + xbmc.sleep(100) + + # Add all items from the stack to the playlist + for index, (path, listitem) in enumerate(self.stack): + listitem.setPath(path) + playlist.add(path, listitem, index) + LOG.info("[ stack/%s ] %s", index, path[:60]) + + xbmc.executebuiltin("Dialog.Close(busydialognocancel)") + + # Start playback from position 0 (the first intro) + player.play(playlist, startpos=0) + def _set_additional_parts(self, item_id): """Create listitems and add them to the stack of playlist.""" parts = self.api_client.get_additional_parts(item_id)