From 46c749c547a0f9b777755b007aed2b6569df55d6 Mon Sep 17 00:00:00 2001 From: Andrew Rabert Date: Wed, 1 May 2024 11:46:14 -0400 Subject: [PATCH] Fix+enhance playback sequence of episodes in a series/season - Adds an option to a season's menu to `Play All From Here` (default behavior is to play only the specific season) - Playback will start on the first unplayed episode, or at the beginning of the season - After starting playback, navigation to prior episodes is immediately possible using the previous episode navigation button - Fix previous button not navigating to previous episode when starting on a non-first episode (ex. episode 1 when starting playback on episode 2) --- src/components/playback/playbackmanager.js | 68 +++++++++++----------- src/components/shortcuts.js | 2 +- 2 files changed, 35 insertions(+), 35 deletions(-) diff --git a/src/components/playback/playbackmanager.js b/src/components/playback/playbackmanager.js index 01d48b575e..14b23ad940 100644 --- a/src/components/playback/playbackmanager.js +++ b/src/components/playback/playbackmanager.js @@ -1858,7 +1858,7 @@ class PlaybackManager { }, queryOptions)); case 'Series': case 'Season': - return getSeriesOrSeasonPlaybackPromise(firstItem, options); + return getSeriesOrSeasonPlaybackPromise(firstItem, options, items); case 'Episode': return getEpisodePlaybackPromise(firstItem, options, items); } @@ -1922,41 +1922,44 @@ class PlaybackManager { return null; } - async function getSeriesOrSeasonPlaybackPromise(firstItem, options) { + async function getSeriesOrSeasonPlaybackPromise(firstItem, options, items) { const apiClient = ServerConnections.getApiClient(firstItem.ServerId); - const isSeason = firstItem.Type === 'Season'; + const startSeasonId = firstItem.Type === 'Season' ? items[options.startIndex || 0].Id : undefined; const episodesResult = await apiClient.getEpisodes(firstItem.SeriesId || firstItem.Id, { IsVirtualUnaired: false, IsMissing: false, - SeasonId: isSeason ? firstItem.Id : undefined, + SeasonId: (startSeasonId && items.length === 1) ? startSeasonId : undefined, SortBy: options.shuffle ? 'Random' : undefined, UserId: apiClient.getCurrentUserId(), Fields: ['Chapters', 'Trickplay'] }); - const originalResults = episodesResult.Items; - - let foundItem = false; - - if (!options.shuffle) { - episodesResult.Items = episodesResult.Items.filter(function (e) { - if (foundItem) { - return true; + if (options.shuffle) { + episodesResult.StartIndex = 0; + } else { + episodesResult.StartIndex = undefined; + let seasonStartIndex; + for (const [index, e] of episodesResult.Items.entries()) { + if (startSeasonId) { + if (e.SeasonId == startSeasonId) { + if (seasonStartIndex === undefined) { + seasonStartIndex = index; + } + } else { + continue; + } } - if (!e.UserData.Played) { - foundItem = true; - return true; + episodesResult.StartIndex = index; + break; } - - return false; - }); + } + episodesResult.StartIndex = episodesResult.StartIndex || seasonStartIndex || 0; } - if (episodesResult.Items.length === 0) { - episodesResult.Items = originalResults; - } + // TODO: fix calling code to read episodesResult.StartIndex instead when set. + options.startIndex = episodesResult.StartIndex; episodesResult.TotalRecordCount = episodesResult.Items.length; @@ -1965,13 +1968,13 @@ class PlaybackManager { function getEpisodePlaybackPromise(firstItem, options, items) { if (items.length === 1 && getPlayer(firstItem, options).supportsProgress !== false) { - return getEpisodes(firstItem); + return getEpisodes(firstItem, options); } else { return null; } } - function getEpisodes(firstItem) { + function getEpisodes(firstItem, options) { return new Promise(function (resolve, reject) { const apiClient = ServerConnections.getApiClient(firstItem.ServerId); @@ -1986,24 +1989,21 @@ class PlaybackManager { UserId: apiClient.getCurrentUserId(), Fields: ['Chapters', 'Trickplay'] }).then(function (episodesResult) { - resolve(filterEpisodes(episodesResult, firstItem)); + resolve(filterEpisodes(episodesResult, firstItem, options)); }, reject); }); } - function filterEpisodes(episodesResult, firstItem) { - let foundItem = false; - episodesResult.Items = episodesResult.Items.filter(function (e) { - if (foundItem) { - return true; - } + function filterEpisodes(episodesResult, firstItem, options) { + for (const [index, e] of episodesResult.Items.entries()) { if (e.Id === firstItem.Id) { - foundItem = true; - return true; + episodesResult.StartIndex = index; + break; } + } - return false; - }); + // TODO: fix calling code to read episodesResult.StartIndex instead when set. + options.startIndex = episodesResult.StartIndex; episodesResult.TotalRecordCount = episodesResult.Items.length; return episodesResult; } diff --git a/src/components/shortcuts.js b/src/components/shortcuts.js index cadb8cc20e..cdf51b4661 100644 --- a/src/components/shortcuts.js +++ b/src/components/shortcuts.js @@ -116,7 +116,7 @@ function showContextMenu(card, options) { item: item, play: true, queue: true, - playAllFromHere: !item.IsFolder, + playAllFromHere: item.Type === 'Season' || !item.IsFolder, queueAllFromHere: !item.IsFolder, playlistId: playlistId, collectionId: collectionId,