import React, { useCallback, useMemo, useState } from "react"; import { View, TouchableOpacity } from "react-native"; import { Ionicons } from "@expo/vector-icons"; import * as DropdownMenu from "zeego/dropdown-menu"; import { useControlContext } from "../contexts/ControlContext"; import { useVideoContext } from "../contexts/VideoContext"; import { TranscodedSubtitle } from "../types"; import { useAtomValue } from "jotai"; import { apiAtom } from "@/providers/JellyfinProvider"; import { useLocalSearchParams, useRouter } from "expo-router"; import { SubtitleHelper } from "@/utils/SubtitleHelper"; interface DropdownViewProps { showControls: boolean; offline?: boolean; // used to disable external subs for downloads } const DropdownView: React.FC = ({ showControls }) => { const router = useRouter(); const api = useAtomValue(apiAtom); const ControlContext = useControlContext(); const mediaSource = ControlContext?.mediaSource; const item = ControlContext?.item; const isVideoLoaded = ControlContext?.isVideoLoaded; const videoContext = useVideoContext(); const { subtitleTracks, setSubtitleTrack } = videoContext; const { subtitleIndex, audioIndex, bitrateValue } = useLocalSearchParams<{ itemId: string; audioIndex: string; subtitleIndex: string; mediaSourceId: string; bitrateValue: string; }>(); // Either its on a text subtitle or its on not on any subtitle therefore it should show all the embedded HLS subtitles. const isOnTextSubtitle = useMemo(() => { const res = Boolean( mediaSource?.MediaStreams?.find( (x) => x.Index === parseInt(subtitleIndex) && x.IsTextSubtitleStream ) || subtitleIndex === "-1" ); return res; }, []); const allSubs = mediaSource?.MediaStreams?.filter((x) => x.Type === "Subtitle") ?? []; const subtitleHelper = new SubtitleHelper(mediaSource?.MediaStreams ?? []); const allSubtitleTracksForTranscodingStream = useMemo(() => { const disableSubtitle = { name: "Disable", index: -1, IsTextSubtitleStream: true, } as TranscodedSubtitle; if (isOnTextSubtitle) { const textSubtitles = subtitleTracks?.map((s) => ({ name: s.name, index: s.index, IsTextSubtitleStream: true, })) || []; const sortedSubtitles = subtitleHelper.getSortedSubtitles(textSubtitles); return [disableSubtitle, ...sortedSubtitles]; } const transcodedSubtitle: TranscodedSubtitle[] = allSubs.map((x) => ({ name: x.DisplayTitle!, index: x.Index!, IsTextSubtitleStream: x.IsTextSubtitleStream!, })); return [disableSubtitle, ...transcodedSubtitle]; }, [item, isVideoLoaded, subtitleTracks, mediaSource?.MediaStreams]); const changeToImageBasedSub = useCallback( (subtitleIndex: number) => { const queryParams = new URLSearchParams({ itemId: item.Id ?? "", // Ensure itemId is a string audioIndex: audioIndex?.toString() ?? "", subtitleIndex: subtitleIndex?.toString() ?? "", mediaSourceId: mediaSource?.Id ?? "", // Ensure mediaSourceId is a string bitrateValue: bitrateValue, }).toString(); // @ts-expect-error router.replace(`player/transcoding-player?${queryParams}`); }, [mediaSource] ); // Audio tracks for transcoding streams. const allAudio = mediaSource?.MediaStreams?.filter((x) => x.Type === "Audio").map((x) => ({ name: x.DisplayTitle!, index: x.Index!, })) || []; const ChangeTranscodingAudio = useCallback( (audioIndex: number) => { const queryParams = new URLSearchParams({ itemId: item.Id ?? "", // Ensure itemId is a string audioIndex: audioIndex?.toString() ?? "", subtitleIndex: subtitleIndex?.toString() ?? "", mediaSourceId: mediaSource?.Id ?? "", // Ensure mediaSourceId is a string bitrateValue: bitrateValue, }).toString(); // @ts-expect-error router.replace(`player/transcoding-player?${queryParams}`); }, [mediaSource, subtitleIndex, audioIndex] ); return ( Subtitle {allSubtitleTracksForTranscodingStream?.map( (sub, idx: number) => ( { if ( subtitleIndex === (isOnTextSubtitle && sub.IsTextSubtitleStream ? subtitleHelper .getSourceSubtitleIndex(sub.index) .toString() : sub?.index.toString()) ) return; router.setParams({ subtitleIndex: subtitleHelper .getSourceSubtitleIndex(sub.index) .toString(), }); if (sub.IsTextSubtitleStream && isOnTextSubtitle) { setSubtitleTrack && setSubtitleTrack(sub.index); return; } changeToImageBasedSub(sub.index); }} > {sub.name} ) )} Audio {allAudio?.map((track, idx: number) => ( { if (audioIndex === track.index.toString()) return; router.setParams({ audioIndex: track.index.toString(), }); ChangeTranscodingAudio(track.index); }} > {track.name} ))} ); }; export default DropdownView;