import type { BaseItemDto, MediaSourceInfo, } from "@jellyfin/sdk/lib/generated-client/models"; import { Image } from "expo-image"; import { useNavigation } from "expo-router"; import { useAtom } from "jotai"; import React, { useEffect, useMemo, useState } from "react"; import { Platform, View } from "react-native"; import { useSafeAreaInsets } from "react-native-safe-area-context"; import { type Bitrate } from "@/components/BitrateSelector"; import { ItemImage } from "@/components/common/ItemImage"; import { DownloadSingleItem } from "@/components/DownloadItem"; import { ItemPeopleSections } from "@/components/item/ItemPeopleSections"; import { MediaSourceButton } from "@/components/MediaSourceButton"; import { OverviewText } from "@/components/OverviewText"; import { ParallaxScrollView } from "@/components/ParallaxPage"; // const PlayButton = !Platform.isTV ? require("@/components/PlayButton") : null; import { PlayButton } from "@/components/PlayButton"; import { PlayedStatus } from "@/components/PlayedStatus"; import { SimilarItems } from "@/components/SimilarItems"; import { CurrentSeries } from "@/components/series/CurrentSeries"; import { SeasonEpisodesCarousel } from "@/components/series/SeasonEpisodesCarousel"; import useDefaultPlaySettings from "@/hooks/useDefaultPlaySettings"; import { useImageColorsReturn } from "@/hooks/useImageColorsReturn"; import { useOrientation } from "@/hooks/useOrientation"; import * as ScreenOrientation from "@/packages/expo-screen-orientation"; import { apiAtom, userAtom } from "@/providers/JellyfinProvider"; import { useOfflineMode } from "@/providers/OfflineModeProvider"; import { useSettings } from "@/utils/atoms/settings"; import { getLogoImageUrlById } from "@/utils/jellyfin/image/getLogoImageUrlById"; import { AddToFavorites } from "./AddToFavorites"; import { AddToWatchlist } from "./AddToWatchlist"; import { ItemHeader } from "./ItemHeader"; import { ItemTechnicalDetails } from "./ItemTechnicalDetails"; import { PlayInRemoteSessionButton } from "./PlayInRemoteSession"; const Chromecast = !Platform.isTV ? require("./Chromecast") : null; export type SelectedOptions = { bitrate: Bitrate; mediaSource: MediaSourceInfo | undefined; audioIndex: number | undefined; subtitleIndex: number; }; interface ItemContentProps { item: BaseItemDto; itemWithSources?: BaseItemDto | null; } export const ItemContent: React.FC = React.memo( ({ item, itemWithSources }) => { const [api] = useAtom(apiAtom); const isOffline = useOfflineMode(); const { settings } = useSettings(); const { orientation } = useOrientation(); const navigation = useNavigation(); const insets = useSafeAreaInsets(); const [user] = useAtom(userAtom); const itemColors = useImageColorsReturn({ item }); const [loadingLogo, setLoadingLogo] = useState(true); const [headerHeight, setHeaderHeight] = useState(350); const [selectedOptions, setSelectedOptions] = useState< SelectedOptions | undefined >(undefined); // Use itemWithSources for play settings since it has MediaSources data const { defaultAudioIndex, defaultBitrate, defaultMediaSource, defaultSubtitleIndex, } = useDefaultPlaySettings(itemWithSources ?? item, settings); const logoUrl = useMemo( () => (item ? getLogoImageUrlById({ api, item }) : null), [api, item], ); const onLogoLoad = React.useCallback(() => { setLoadingLogo(false); }, []); const loading = useMemo(() => { return Boolean(logoUrl && loadingLogo); }, [loadingLogo, logoUrl]); // Needs to automatically change the selected to the default values for default indexes. useEffect(() => { setSelectedOptions(() => ({ bitrate: defaultBitrate, mediaSource: defaultMediaSource ?? undefined, subtitleIndex: defaultSubtitleIndex ?? -1, audioIndex: defaultAudioIndex, })); }, [ defaultAudioIndex, defaultBitrate, defaultSubtitleIndex, defaultMediaSource, ]); useEffect(() => { if (!Platform.isTV && itemWithSources) { navigation.setOptions({ headerRight: () => item && (Platform.OS === "ios" ? ( {item.Type !== "Program" && ( {!Platform.isTV && ( )} {user?.Policy?.IsAdministrator && !settings.hideRemoteSessionButton && ( )} {settings.streamyStatsServerUrl && !settings.hideWatchlistsTab && ( )} )} ) : ( {item.Type !== "Program" && ( {!Platform.isTV && ( )} {user?.Policy?.IsAdministrator && !settings.hideRemoteSessionButton && ( )} {settings.streamyStatsServerUrl && !settings.hideWatchlistsTab && ( )} )} )), }); } }, [ item, navigation, user, itemWithSources, settings.hideRemoteSessionButton, settings.streamyStatsServerUrl, settings.hideWatchlistsTab, ]); useEffect(() => { if (item) { if (orientation !== ScreenOrientation.OrientationLock.PORTRAIT_UP) setHeaderHeight(230); else if (item.Type === "Movie") setHeaderHeight(500); else setHeaderHeight(350); } }, [item, orientation]); if (!item || !selectedOptions) return null; return ( } logo={ logoUrl ? ( ) : ( ) } > {!isOffline && ( )} {item.Type === "Episode" && ( )} {!isOffline && selectedOptions.mediaSource?.MediaStreams && selectedOptions.mediaSource.MediaStreams.length > 0 && ( )} {item.Type !== "Program" && ( <> {item.Type === "Episode" && !isOffline && ( )} {!isOffline && } )} ); }, );