diff --git a/src/apps/experimental/components/library/PlayAllButton.tsx b/src/apps/experimental/components/library/PlayAllButton.tsx index 65315d21683..9c87d0056bb 100644 --- a/src/apps/experimental/components/library/PlayAllButton.tsx +++ b/src/apps/experimental/components/library/PlayAllButton.tsx @@ -27,10 +27,12 @@ const PlayAllButton: FC = ({ item, items, viewType, hasFilte SortBy: [libraryViewSettings.SortBy], SortOrder: [libraryViewSettings.SortOrder] } + }).catch(err => { + console.error('[PlayAllButton] failed to play', err); }); } else { playbackManager.play({ - items: items, + items, autoplay: true, queryOptions: { ParentId: item?.Id ?? undefined, @@ -38,7 +40,8 @@ const PlayAllButton: FC = ({ item, items, viewType, hasFilte SortBy: [libraryViewSettings.SortBy], SortOrder: [libraryViewSettings.SortOrder] } - + }).catch(err => { + console.error('[PlayAllButton] failed to play', err); }); } }, [hasFilters, item, items, libraryViewSettings, viewType]); diff --git a/src/apps/experimental/components/library/QueueButton.tsx b/src/apps/experimental/components/library/QueueButton.tsx index 3b9cca64c3a..31abe4d7b2f 100644 --- a/src/apps/experimental/components/library/QueueButton.tsx +++ b/src/apps/experimental/components/library/QueueButton.tsx @@ -17,10 +17,14 @@ const QueueButton: FC = ({ item, items, hasFilters }) => { if (item && !hasFilters) { playbackManager.queue({ items: [item] + }).catch(err => { + console.error('[QueueButton] failed to add to queue', err); }); } else { playbackManager.queue({ - items: items + items + }).catch(err => { + console.error('[QueueButton] failed to add to queue', err); }); } }, [hasFilters, item, items]); diff --git a/src/apps/experimental/components/library/ShuffleButton.tsx b/src/apps/experimental/components/library/ShuffleButton.tsx index dde6d564170..2a85add1f29 100644 --- a/src/apps/experimental/components/library/ShuffleButton.tsx +++ b/src/apps/experimental/components/library/ShuffleButton.tsx @@ -24,13 +24,15 @@ const ShuffleButton: FC = ({ item, items, viewType, hasFilte playbackManager.shuffle(item); } else { playbackManager.play({ - items: items, + items, autoplay: true, queryOptions: { ParentId: item?.Id ?? undefined, ...getFiltersQuery(viewType, libraryViewSettings), SortBy: [ItemSortBy.Random] } + }).catch(err => { + console.error('[ShuffleButton] failed to play', err); }); } }, [hasFilters, item, items, libraryViewSettings, viewType]); diff --git a/src/apps/experimental/features/details/components/buttons/MoreCommandsButton.tsx b/src/apps/experimental/features/details/components/buttons/MoreCommandsButton.tsx index b9c979f9f88..99db01378d7 100644 --- a/src/apps/experimental/features/details/components/buttons/MoreCommandsButton.tsx +++ b/src/apps/experimental/features/details/components/buttons/MoreCommandsButton.tsx @@ -39,7 +39,7 @@ function playAllFromHere(opts: PlayAllFromHereOptions) { } if (!ids.length) { - return; + return Promise.resolve(); } if (queue) { @@ -168,6 +168,8 @@ const MoreCommandsButton: FC = ({ item: item || {}, items: items || [], serverId: item?.ServerId + }).catch(err => { + console.error('[MoreCommandsButton] failed to play', err); }); } else if (result.command === 'queueallfromhere') { playAllFromHere({ @@ -175,6 +177,8 @@ const MoreCommandsButton: FC = ({ items: items || [], serverId: item?.ServerId, queue: true + }).catch(err => { + console.error('[MoreCommandsButton] failed to play', err); }); } else if (result.deleted) { if (result?.itemId !== itemId) { diff --git a/src/apps/experimental/features/details/components/buttons/PlayOrResumeButton.tsx b/src/apps/experimental/features/details/components/buttons/PlayOrResumeButton.tsx index a52453656f4..97298b38138 100644 --- a/src/apps/experimental/features/details/components/buttons/PlayOrResumeButton.tsx +++ b/src/apps/experimental/features/details/components/buttons/PlayOrResumeButton.tsx @@ -58,6 +58,8 @@ const PlayOrResumeButton: FC = ({ ); playbackManager.play({ items: [channel] + }).catch(err => { + console.error('[PlayOrResumeButton] failed to play', err); }); return; } @@ -65,6 +67,8 @@ const PlayOrResumeButton: FC = ({ playbackManager.play({ items: [item], ...playOptions + }).catch(err => { + console.error('[PlayOrResumeButton] failed to play', err); }); }, [apiContext, item, playOptions, queryClient]); diff --git a/src/components/playback/playbackmanager.js b/src/components/playback/playbackmanager.js index d3d74bdfc2d..c761f7317b6 100644 --- a/src/components/playback/playbackmanager.js +++ b/src/components/playback/playbackmanager.js @@ -2039,12 +2039,12 @@ export class PlaybackManager { self.translateItemsForPlayback = translateItemsForPlayback; self.getItemsForPlayback = getItemsForPlayback; - self.play = function (options) { + self.play = async function (options) { normalizePlayOptions(options); if (self._currentPlayer) { if (options.enableRemotePlayers === false && !self._currentPlayer.isLocalPlayer) { - return Promise.reject(); + throw new Error('Remote players are disabled'); } if (!self._currentPlayer.isLocalPlayer) { @@ -2056,29 +2056,35 @@ export class PlaybackManager { loading.show(); } - if (options.items) { - return translateItemsForPlayback(options.items, options) - .then((items) => getAdditionalParts(items)) - .then(function (allItems) { - const flattened = allItems.flatMap(i => i); - return playWithIntros(flattened, options); - }); - } else { + let { items } = options; + // If items were not passed directly, fetch them by ID + if (!items) { if (!options.serverId) { throw new Error('serverId required!'); } - return getItemsForPlayback(options.serverId, { + items = (await getItemsForPlayback(options.serverId, { Ids: options.ids.join(',') - }).then(function (result) { - return translateItemsForPlayback(result.Items, options) - .then((items) => getAdditionalParts(items)) - .then(function (allItems) { - const flattened = allItems.flatMap(i => i); - return playWithIntros(flattened, options); - }); - }); + })).Items; + } + + // Prepare the list of items + items = await translateItemsForPlayback(items, options); + // Add any additional parts for movies or episodes + items = await getAdditionalParts(items); + // Adjust the start index for additional parts added to the queue + if (options.startIndex) { + let adjustedStartIndex = 0; + for (let i = 0; i < options.startIndex; i++) { + adjustedStartIndex += items[i].length; + } + + options.startIndex = adjustedStartIndex; } + // getAdditionalParts returns an array of arrays of items, so flatten it + items = items.flat(); + + return playWithIntros(items, options); }; function getPlayerData(player) { @@ -2217,20 +2223,22 @@ export class PlaybackManager { } const getAdditionalParts = async (items) => { - const getOneAdditionalPart = async function (item) { - let retVal = [item]; - if (item.PartCount && item.PartCount > 1 && (item.Type === 'Movie' || item.Type === 'Episode')) { + const getItemAndParts = async function (item) { + if ( + item.PartCount && item.PartCount > 1 + && [ BaseItemKind.Episode, BaseItemKind.Movie ].includes(item.Type) + ) { const client = ServerConnections.getApiClient(item.ServerId); const user = await client.getCurrentUser(); const additionalParts = await client.getAdditionalVideoParts(user.Id, item.Id); if (additionalParts.Items.length) { - retVal = [item, ...additionalParts.Items]; + return [ item, ...additionalParts.Items ]; } } - return retVal; + return [ item ]; }; - return Promise.all(items.flatMap(async (item) => getOneAdditionalPart(item))); + return Promise.all(items.map(getItemAndParts)); }; function playWithIntros(items, options) { @@ -3105,11 +3113,11 @@ export class PlaybackManager { }; self.queue = function (options, player = this._currentPlayer) { - queue(options, '', player); + return queue(options, '', player); }; self.queueNext = function (options, player = this._currentPlayer) { - queue(options, 'next', player); + return queue(options, 'next', player); }; function queue(options, mode, player) { diff --git a/src/components/playlisteditor/playlisteditor.ts b/src/components/playlisteditor/playlisteditor.ts index 3db83914e16..62979069569 100644 --- a/src/components/playlisteditor/playlisteditor.ts +++ b/src/components/playlisteditor/playlisteditor.ts @@ -145,6 +145,8 @@ function addToPlaylist(dlg: DialogElement, id: string) { playbackManager.queue({ serverId: currentServerId, ids: itemIds.split(',') + }).catch(err => { + console.error('[PlaylistEditor] failed to add to queue', err); }); dlg.submitted = true; dialogHelper.close(dlg);