mirror of
https://github.com/streamyfin/streamyfin.git
synced 2026-06-02 20:18:29 +01:00
feat(casting): extract reusable next-episode helpers
This commit is contained in:
@@ -1,10 +1,11 @@
|
|||||||
import type { Api } from "@jellyfin/sdk";
|
import type { Api } from "@jellyfin/sdk";
|
||||||
import type { BaseItemDto, UserDto } from "@jellyfin/sdk/lib/generated-client";
|
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 { useCallback, useEffect, useState } from "react";
|
||||||
import type { Device, RemoteMediaClient } from "react-native-google-cast";
|
import type { Device, RemoteMediaClient } from "react-native-google-cast";
|
||||||
import type { Settings } from "@/utils/atoms/settings";
|
import type { Settings } from "@/utils/atoms/settings";
|
||||||
import { loadCastMedia } from "@/utils/casting/castLoad";
|
import { loadCastMedia } from "@/utils/casting/castLoad";
|
||||||
|
import { fetchSeriesEpisodes, findNextEpisode } from "@/utils/casting/episodes";
|
||||||
|
|
||||||
interface UseCastEpisodesParams {
|
interface UseCastEpisodesParams {
|
||||||
api: Api | null;
|
api: Api | null;
|
||||||
@@ -118,31 +119,24 @@ export function useCastEpisodes({
|
|||||||
|
|
||||||
// Fetch episodes for TV shows
|
// Fetch episodes for TV shows
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (currentItem?.Type !== "Episode" || !currentItem.SeriesId || !api)
|
if (
|
||||||
|
currentItem?.Type !== "Episode" ||
|
||||||
|
!currentItem.SeriesId ||
|
||||||
|
!api ||
|
||||||
|
!user
|
||||||
|
)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const fetchEpisodes = async () => {
|
const fetchEpisodes = async () => {
|
||||||
try {
|
try {
|
||||||
const tvShowsApi = getTvShowsApi(api);
|
// Fetch ALL episodes from ALL seasons (no season filter).
|
||||||
// Fetch ALL episodes from ALL seasons by removing seasonId filter
|
const episodeList = await fetchSeriesEpisodes(
|
||||||
const response = await tvShowsApi.getEpisodes({
|
api,
|
||||||
seriesId: currentItem.SeriesId!,
|
user,
|
||||||
// Don't filter by seasonId - get all seasons
|
currentItem.SeriesId!,
|
||||||
userId: api.accessToken ? undefined : "",
|
|
||||||
});
|
|
||||||
|
|
||||||
const episodeList = response.data.Items || [];
|
|
||||||
setEpisodes(episodeList);
|
|
||||||
|
|
||||||
// Find next episode
|
|
||||||
const currentIndex = episodeList.findIndex(
|
|
||||||
(ep) => ep.Id === currentItem.Id,
|
|
||||||
);
|
);
|
||||||
if (currentIndex >= 0 && currentIndex < episodeList.length - 1) {
|
setEpisodes(episodeList);
|
||||||
setNextEpisode(episodeList[currentIndex + 1]);
|
setNextEpisode(findNextEpisode(episodeList, currentItem.Id));
|
||||||
} else {
|
|
||||||
setNextEpisode(null);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Failed to fetch episodes:", error);
|
console.error("Failed to fetch episodes:", error);
|
||||||
}
|
}
|
||||||
@@ -155,6 +149,7 @@ export function useCastEpisodes({
|
|||||||
currentItem?.SeasonId,
|
currentItem?.SeasonId,
|
||||||
currentItem?.Id,
|
currentItem?.Id,
|
||||||
api,
|
api,
|
||||||
|
user,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return { episodes, nextEpisode, seasonData, loadEpisode, loadingEpisodeId };
|
return { episodes, nextEpisode, seasonData, loadEpisode, loadingEpisodeId };
|
||||||
|
|||||||
19
utils/casting/episodes.test.ts
Normal file
19
utils/casting/episodes.test.ts
Normal 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
38
utils/casting/episodes.ts
Normal 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 ?? [];
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user