feat(casting): extract reusable next-episode helpers

This commit is contained in:
Uruk
2026-05-23 23:18:55 +02:00
parent 0ba3d19550
commit 57cfa5ce78
3 changed files with 73 additions and 21 deletions

View File

@@ -1,10 +1,11 @@
import type { Api } from "@jellyfin/sdk";
import type { BaseItemDto, UserDto } from "@jellyfin/sdk/lib/generated-client";
import { getTvShowsApi, getUserLibraryApi } from "@jellyfin/sdk/lib/utils/api";
import { getUserLibraryApi } from "@jellyfin/sdk/lib/utils/api";
import { useCallback, useEffect, useState } from "react";
import type { Device, RemoteMediaClient } from "react-native-google-cast";
import type { Settings } from "@/utils/atoms/settings";
import { loadCastMedia } from "@/utils/casting/castLoad";
import { fetchSeriesEpisodes, findNextEpisode } from "@/utils/casting/episodes";
interface UseCastEpisodesParams {
api: Api | null;
@@ -118,31 +119,24 @@ export function useCastEpisodes({
// Fetch episodes for TV shows
useEffect(() => {
if (currentItem?.Type !== "Episode" || !currentItem.SeriesId || !api)
if (
currentItem?.Type !== "Episode" ||
!currentItem.SeriesId ||
!api ||
!user
)
return;
const fetchEpisodes = async () => {
try {
const tvShowsApi = getTvShowsApi(api);
// Fetch ALL episodes from ALL seasons by removing seasonId filter
const response = await tvShowsApi.getEpisodes({
seriesId: currentItem.SeriesId!,
// Don't filter by seasonId - get all seasons
userId: api.accessToken ? undefined : "",
});
const episodeList = response.data.Items || [];
setEpisodes(episodeList);
// Find next episode
const currentIndex = episodeList.findIndex(
(ep) => ep.Id === currentItem.Id,
// Fetch ALL episodes from ALL seasons (no season filter).
const episodeList = await fetchSeriesEpisodes(
api,
user,
currentItem.SeriesId!,
);
if (currentIndex >= 0 && currentIndex < episodeList.length - 1) {
setNextEpisode(episodeList[currentIndex + 1]);
} else {
setNextEpisode(null);
}
setEpisodes(episodeList);
setNextEpisode(findNextEpisode(episodeList, currentItem.Id));
} catch (error) {
console.error("Failed to fetch episodes:", error);
}
@@ -155,6 +149,7 @@ export function useCastEpisodes({
currentItem?.SeasonId,
currentItem?.Id,
api,
user,
]);
return { episodes, nextEpisode, seasonData, loadEpisode, loadingEpisodeId };

View File

@@ -0,0 +1,19 @@
import { describe, expect, test } from "bun:test";
import { findNextEpisode } from "./episodes";
const ep = (id: string) => ({ Id: id });
describe("findNextEpisode", () => {
test("returns the episode after the current one", () => {
expect(findNextEpisode([ep("a"), ep("b"), ep("c")], "b")).toEqual(ep("c"));
});
test("returns null on the last episode", () => {
expect(findNextEpisode([ep("a"), ep("b")], "b")).toBeNull();
});
test("returns null when the current id is not found", () => {
expect(findNextEpisode([ep("a"), ep("b")], "x")).toBeNull();
});
test("returns null for an empty list", () => {
expect(findNextEpisode([], "a")).toBeNull();
});
});

38
utils/casting/episodes.ts Normal file
View File

@@ -0,0 +1,38 @@
/**
* Episode-list helpers for the casting player and the autoplay watcher.
*/
import type { Api } from "@jellyfin/sdk";
import type {
BaseItemDto,
UserDto,
} from "@jellyfin/sdk/lib/generated-client/models";
import { getTvShowsApi } from "@jellyfin/sdk/lib/utils/api";
/** The episode following `currentId` in `episodes`, or null if none / not found. */
export const findNextEpisode = (
episodes: BaseItemDto[],
currentId: string | null | undefined,
): BaseItemDto | null => {
const index = episodes.findIndex((e) => e.Id === currentId);
if (index < 0 || index + 1 >= episodes.length) return null;
return episodes[index + 1];
};
/**
* Fetch every episode of the series that owns the current episode.
* Mirrors the call previously inlined in `useCastEpisodes`: no season filter,
* and the same `userId` quirk (undefined when an access token is present, else
* the empty string) so the request payload stays byte-identical.
*/
export const fetchSeriesEpisodes = async (
api: Api,
_user: UserDto,
seriesId: string,
): Promise<BaseItemDto[]> => {
const res = await getTvShowsApi(api).getEpisodes({
seriesId,
userId: api.accessToken ? undefined : "",
});
return res.data.Items ?? [];
};