mirror of
https://github.com/streamyfin/streamyfin.git
synced 2026-06-02 12:08:37 +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 { 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 };
|
||||
|
||||
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