From de4f60f56468faea5eee17a7d030a04f18656df6 Mon Sep 17 00:00:00 2001 From: Alex Kim Date: Sun, 8 Dec 2024 07:44:35 +1100 Subject: [PATCH] WIP --- components/series/SeasonDropdown.tsx | 67 ++++++++------- .../video-player/controls/EpisodeList.tsx | 81 +++++++++++++++++-- 2 files changed, 109 insertions(+), 39 deletions(-) diff --git a/components/series/SeasonDropdown.tsx b/components/series/SeasonDropdown.tsx index f34609ea..1d007c64 100644 --- a/components/series/SeasonDropdown.tsx +++ b/components/series/SeasonDropdown.tsx @@ -1,22 +1,22 @@ -import {BaseItemDto} from "@jellyfin/sdk/lib/generated-client/models"; -import {useEffect, useMemo} from "react"; -import {TouchableOpacity, View} from "react-native"; +import { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models"; +import { useEffect, useMemo } from "react"; +import { TouchableOpacity, View } from "react-native"; import * as DropdownMenu from "zeego/dropdown-menu"; -import {Text} from "../common/Text"; +import { Text } from "../common/Text"; type Props = { item: BaseItemDto; seasons: BaseItemDto[]; initialSeasonIndex?: number; state: SeasonIndexState; - onSelect: (season: BaseItemDto) => void + onSelect: (season: BaseItemDto) => void; }; type SeasonKeys = { - id: keyof BaseItemDto, - title: keyof BaseItemDto, - index: keyof BaseItemDto -} + id: keyof BaseItemDto; + title: keyof BaseItemDto; + index: keyof BaseItemDto; +}; export type SeasonIndexState = { [seriesId: string]: number | null | undefined; @@ -27,19 +27,22 @@ export const SeasonDropdown: React.FC = ({ seasons, initialSeasonIndex, state, - onSelect + onSelect, }) => { - const keys = useMemo(() => - item.Type === "Episode" ? { - id: "ParentId", - title: "SeasonName", - index: "ParentIndexNumber" - } - : { - id: "Id", - title: "Name", - index: "IndexNumber" - }, [item] + const keys = useMemo( + () => + item.Type === "Episode" + ? { + id: "ParentId", + title: "SeasonName", + index: "ParentIndexNumber", + } + : { + id: "Id", + title: "Name", + index: "IndexNumber", + }, + [item] ); const seasonIndex = useMemo(() => state[item[keys.id] ?? ""], [state]); @@ -62,28 +65,28 @@ export const SeasonDropdown: React.FC = ({ const season1 = seasons.find((season: any) => season[keys.index] === 1); const season0 = seasons.find((season: any) => season[keys.index] === 0); const firstSeason = season1 || season0 || seasons[0]; - onSelect(firstSeason) + onSelect(firstSeason); } if (initialIndex !== undefined) { - const initialSeason = seasons.find((season: any) => - season[keys.index] === initialIndex - ) + const initialSeason = seasons.find( + (season: any) => season[keys.index] === initialIndex + ); - if (initialSeason) onSelect(initialSeason!) - else throw Error("Initial index could not be found!") + if (initialSeason) onSelect(initialSeason!); + else throw Error("Initial index could not be found!"); } } }, [seasons, seasonIndex, item[keys.id], initialSeasonIndex]); - const sortByIndex = (a: BaseItemDto, b: BaseItemDto) => a[keys.index] - b[keys.index]; + const sortByIndex = (a: BaseItemDto, b: BaseItemDto) => + a[keys.index] - b[keys.index]; return ( - + Season {seasonIndex} @@ -103,7 +106,9 @@ export const SeasonDropdown: React.FC = ({ key={season[keys.title]} onSelect={() => onSelect(season)} > - {season[keys.title]} + + {season[keys.title]} + ))} diff --git a/components/video-player/controls/EpisodeList.tsx b/components/video-player/controls/EpisodeList.tsx index 0795fc6f..cf968aed 100644 --- a/components/video-player/controls/EpisodeList.tsx +++ b/components/video-player/controls/EpisodeList.tsx @@ -2,7 +2,7 @@ import { apiAtom, userAtom } from "@/providers/JellyfinProvider"; import { runtimeTicksToSeconds } from "@/utils/time"; import { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models"; import { useQuery, useQueryClient } from "@tanstack/react-query"; -import { useAtom } from "jotai"; +import { atom, useAtom } from "jotai"; import { useEffect, useMemo, useState, useRef } from "react"; import { View, TouchableOpacity } from "react-native"; import { getTvShowsApi } from "@jellyfin/sdk/lib/utils/api"; @@ -21,35 +21,88 @@ import { router } from "expo-router"; import { getDefaultPlaySettings } from "@/utils/jellyfin/getDefaultPlaySettings"; import { getItemById } from "@/utils/jellyfin/user-library/getItemById"; import { useSettings } from "@/utils/atoms/settings"; +import { + SeasonDropdown, + SeasonIndexState, +} from "@/components/series/SeasonDropdown"; +import { Item } from "zeego/dropdown-menu"; type Props = { item: BaseItemDto; close: () => void; }; +export const seasonIndexAtom = atom({}); + export const EpisodeList: React.FC = ({ item, close }) => { const [api] = useAtom(apiAtom); const [user] = useAtom(userAtom); const scrollViewRef = useRef(null); // Reference to the HorizontalScroll const insets = useSafeAreaInsets(); // Get safe area insets const [settings] = useSettings(); - const SeasonId = item.ParentId; + + const [seasonIndexState, setSeasonIndexState] = useAtom(seasonIndexAtom); + const seasonIndex = seasonIndexState[item.Id ?? ""]; + + const [seriesItem, setSeriesItem] = useState(null); + + useEffect(() => { + if (item.SeriesId) { + getUserItemData({ api, userId: user?.Id, itemId: item.SeriesId }).then( + (res) => { + setSeriesItem(res); + } + ); + } + }, [item.SeriesId]); + + const { data: seasons } = useQuery({ + queryKey: ["seasons", item.SeriesId], + queryFn: async () => { + console.log("Seasons", Boolean(api), user?.Id, item.SeriesId); + if (!api || !user?.Id || !item.SeriesId) return []; + console.log("Seasons", "Fetching"); + const response = await api.axiosInstance.get( + `${api.basePath}/Shows/${item.SeriesId}/Seasons`, + { + params: { + userId: user?.Id, + itemId: item.SeriesId, + Fields: + "ItemCounts,PrimaryImageAspectRatio,CanDelete,MediaSourceCount", + }, + headers: { + Authorization: `MediaBrowser DeviceId="${api.deviceInfo.id}", Token="${api.accessToken}"`, + }, + } + ); + console.log("Response", response.data.Items); + return response.data.Items; + }, + enabled: !!api && !!user?.Id && !!item.SeasonId, + }); + + const selectedSeasonId: string | null = useMemo( + () => + seasons?.find((season: any) => season.IndexNumber === seasonIndex)?.Id, + [seasons, seasonIndex] + ); const { data: episodes, isFetching } = useQuery({ - queryKey: ["episodes", SeasonId], + queryKey: ["episodes", item.SeriesId, item.SeasonId], queryFn: async () => { - if (!api || !user?.Id || !item.Id || !SeasonId) return []; + if (!api || !user?.Id || !item.Id || !item.SeasonId) return []; const res = await getTvShowsApi(api).getEpisodes({ - seriesId: item.Id, + seriesId: item.SeriesId || "", userId: user.Id, - seasonId: SeasonId, + seasonId: item.SeasonId || undefined, enableUserData: true, fields: ["MediaSources", "MediaStreams", "Overview"], }); return res.data.Items; }, - enabled: !!api && !!user?.Id && !!item.Id && !!SeasonId, + enabled: !!api && !!user?.Id && !!item.SeasonId, }); const queryClient = useQueryClient(); @@ -146,6 +199,18 @@ export const EpisodeList: React.FC = ({ item, close }) => { }} className={`flex flex-row items-center space-x-2`} > + { + setSeasonIndexState((prev) => ({ + ...prev, + [item.SeasonId ?? ""]: season.IndexNumber, + })); + }} + /> { close(); @@ -182,7 +247,7 @@ export const EpisodeList: React.FC = ({ item, close }) => {