diff --git a/components/video-player/controls/Controls.tsx b/components/video-player/controls/Controls.tsx index c8b44a5f..e61579de 100644 --- a/components/video-player/controls/Controls.tsx +++ b/components/video-player/controls/Controls.tsx @@ -47,8 +47,9 @@ import { import { VideoRef } from "react-native-video"; import { ControlProvider } from "./contexts/ControlContext"; import { VideoProvider } from "./contexts/VideoContext"; -import DropdownView from "./DropdownView"; import * as Haptics from "expo-haptics"; +import DropdownViewDirect from "./dropdown/DropdownViewDirect"; +import DropdownViewTranscoding from "./dropdown/DropdownViewTranscoding"; interface Props { item: BaseItemDto; @@ -340,7 +341,11 @@ export const Controls: React.FC = ({ setSubtitleTrack={setSubtitleTrack} setSubtitleURL={setSubtitleURL} > - + {!mediaSource?.TranscodingUrl ? ( + + ) : ( + + )} = ({ + showControls, + offline = false, +}) => { + const api = useAtomValue(apiAtom); + const ControlContext = useControlContext(); + const mediaSource = ControlContext?.mediaSource; + const item = ControlContext?.item; + const isVideoLoaded = ControlContext?.isVideoLoaded; + + const videoContext = useVideoContext(); + const { + subtitleTracks, + audioTracks, + setSubtitleURL, + setSubtitleTrack, + setAudioTrack, + } = videoContext; + + const allSubtitleTracksForDirectPlay = useMemo(() => { + if (mediaSource?.TranscodingUrl) return null; + const embeddedSubs = + subtitleTracks + ?.map((s) => ({ + name: s.name, + index: s.index, + deliveryUrl: undefined, + })) + .filter((sub) => !sub.name.endsWith("[External]")) || []; + + const externalSubs = + mediaSource?.MediaStreams?.filter( + (stream) => stream.Type === "Subtitle" && !!stream.DeliveryUrl + ).map((s) => ({ + name: s.DisplayTitle! + " [External]", + index: s.Index!, + deliveryUrl: s.DeliveryUrl, + })) || []; + + // Combine embedded subs with external subs only if not offline + if (!offline) { + return [...embeddedSubs, ...externalSubs] as ( + | EmbeddedSubtitle + | ExternalSubtitle + )[]; + } + return embeddedSubs as EmbeddedSubtitle[]; + }, [item, isVideoLoaded, subtitleTracks, mediaSource?.MediaStreams, offline]); + + const { subtitleIndex, audioIndex } = useLocalSearchParams<{ + itemId: string; + audioIndex: string; + subtitleIndex: string; + mediaSourceId: string; + bitrateValue: string; + }>(); + + const [selectedSubtitleIndex, setSelectedSubtitleIndex] = useState( + parseInt(subtitleIndex) + ); + const [selectedAudioIndex, setSelectedAudioIndex] = useState( + parseInt(audioIndex) + ); + + return ( + + + + + + + + + + + Subtitle + + + {allSubtitleTracksForDirectPlay?.map((sub, idx: number) => ( + { + if ("deliveryUrl" in sub && sub.deliveryUrl) { + setSubtitleURL && + setSubtitleURL( + api?.basePath + sub.deliveryUrl, + sub.name + ); + + console.log( + "Set external subtitle: ", + api?.basePath + sub.deliveryUrl + ); + } else { + console.log("Set sub index: ", sub.index); + setSubtitleTrack && setSubtitleTrack(sub.index); + } + + setSelectedSubtitleIndex(sub.index); + console.log("Subtitle: ", sub); + }} + > + + {sub.name} + + + ))} + + + + + Audio + + + {audioTracks?.map((track, idx: number) => ( + { + setSelectedAudioIndex(track.index); + setAudioTrack && setAudioTrack(track.index); + }} + > + + {track.name} + + + ))} + + + + + + ); +}; + +export default DropdownViewDirect; diff --git a/components/video-player/controls/DropdownView.tsx b/components/video-player/controls/dropdown/DropdownViewTranscoding.tsx similarity index 57% rename from components/video-player/controls/DropdownView.tsx rename to components/video-player/controls/dropdown/DropdownViewTranscoding.tsx index 63a96f01..66e68450 100644 --- a/components/video-player/controls/DropdownView.tsx +++ b/components/video-player/controls/dropdown/DropdownViewTranscoding.tsx @@ -2,13 +2,9 @@ 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 { - EmbeddedSubtitle, - ExternalSubtitle, - TranscodedSubtitle, -} from "./types"; +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"; @@ -30,43 +26,7 @@ const DropdownView: React.FC = ({ const isVideoLoaded = ControlContext?.isVideoLoaded; const videoContext = useVideoContext(); - const { - subtitleTracks, - audioTracks, - setSubtitleURL, - setSubtitleTrack, - setAudioTrack, - } = videoContext; - - const allSubtitleTracksForDirectPlay = useMemo(() => { - if (mediaSource?.TranscodingUrl) return null; - const embeddedSubs = - subtitleTracks - ?.map((s) => ({ - name: s.name, - index: s.index, - deliveryUrl: undefined, - })) - .filter((sub) => !sub.name.endsWith("[External]")) || []; - - const externalSubs = - mediaSource?.MediaStreams?.filter( - (stream) => stream.Type === "Subtitle" && !!stream.DeliveryUrl - ).map((s) => ({ - name: s.DisplayTitle! + " [External]", - index: s.Index!, - deliveryUrl: s.DeliveryUrl, - })) || []; - - // Combine embedded subs with external subs only if not offline - if (!offline) { - return [...embeddedSubs, ...externalSubs] as ( - | EmbeddedSubtitle - | ExternalSubtitle - )[]; - } - return embeddedSubs as EmbeddedSubtitle[]; - }, [item, isVideoLoaded, subtitleTracks, mediaSource?.MediaStreams, offline]); + const { subtitleTracks, setSubtitleTrack } = videoContext; const { subtitleIndex, audioIndex, bitrateValue } = useLocalSearchParams<{ itemId: string; @@ -91,23 +51,19 @@ const DropdownView: React.FC = ({ (x) => x.Index === parseInt(subtitleIndex) ); - const intialSubtitleIndex = - !bitrateValue || !isOnTextSubtitle - ? parseInt(subtitleIndex) - : chosenSubtitle && isOnTextSubtitle - ? textBasedSubs.indexOf(chosenSubtitle) - : -1; + let initialSubtitleIndex = -1; + if (!isOnTextSubtitle) { + initialSubtitleIndex = parseInt(subtitleIndex); + } else if (chosenSubtitle) { + initialSubtitleIndex = textBasedSubs.indexOf(chosenSubtitle); + } const [selectedSubtitleIndex, setSelectedSubtitleIndex] = - useState(intialSubtitleIndex); - const [selectedAudioIndex, setSelectedAudioIndex] = useState( + useState(initialSubtitleIndex); + const [selectedAudioIndex, setSelectedAudioIndex] = useState( parseInt(audioIndex) ); - // TODO: Need to account for the fact when user is on text-based subtitle at start. - // Then the user swaps to another text based subtitle. - // Then changes audio track. - // The user will have the first text based subtitle selected still but it should be the second text based subtitle. const allSubtitleTracksForTranscodingStream = useMemo(() => { const disableSubtitle = { name: "Disable", @@ -190,11 +146,25 @@ const DropdownView: React.FC = ({ index: x.Index!, })) || []; const ChangeTranscodingAudio = useCallback( - (audioIndex: number) => { + (audioIndex: number, currentSelectedSubtitleIndex: number) => { + let newSubtitleIndex: number; + + if (!isOnTextSubtitle) { + newSubtitleIndex = parseInt(subtitleIndex); + } else if ( + currentSelectedSubtitleIndex >= 0 && + currentSelectedSubtitleIndex < textBasedSubs.length + ) { + console.log("setHere SubtitleIndex", currentSelectedSubtitleIndex); + newSubtitleIndex = textBasedSubs[currentSelectedSubtitleIndex].Index!; + console.log("newSubtitleIndex", newSubtitleIndex); + } else { + newSubtitleIndex = -1; + } const queryParams = new URLSearchParams({ itemId: item.Id ?? "", // Ensure itemId is a string audioIndex: audioIndex?.toString() ?? "", - subtitleIndex: subtitleIndex, + subtitleIndex: newSubtitleIndex?.toString() ?? "", mediaSourceId: mediaSource?.Id ?? "", // Ensure mediaSourceId is a string bitrateValue: bitrateValue, }).toString(); @@ -240,62 +210,29 @@ const DropdownView: React.FC = ({ loop={true} sideOffset={10} > - {!mediaSource?.TranscodingUrl && - allSubtitleTracksForDirectPlay?.map((sub, idx: number) => ( + {allSubtitleTracksForTranscodingStream?.map( + (sub, idx: number) => ( { - if ("deliveryUrl" in sub && sub.deliveryUrl) { - setSubtitleURL && - setSubtitleURL( - api?.basePath + sub.deliveryUrl, - sub.name - ); - - console.log( - "Set external subtitle: ", - api?.basePath + sub.deliveryUrl - ); - } else { - console.log("Set sub index: ", sub.index); + console.log("sub", sub); + if (selectedSubtitleIndex === sub?.index) return; + setSelectedSubtitleIndex(sub.index); + if (sub.IsTextSubtitleStream && isOnTextSubtitle) { setSubtitleTrack && setSubtitleTrack(sub.index); + return; } - setSelectedSubtitleIndex(sub.index); - console.log("Subtitle: ", sub); + ChangeTranscodingSubtitle(sub.index); }} > {sub.name} - ))} - {mediaSource?.TranscodingUrl && - allSubtitleTracksForTranscodingStream?.map( - (sub, idx: number) => ( - { - if (selectedSubtitleIndex === sub?.index) return; - setSelectedSubtitleIndex(sub.index); - if (sub.IsTextSubtitleStream && isOnTextSubtitle) { - setSubtitleTrack && setSubtitleTrack(sub.index); - return; - } - - ChangeTranscodingSubtitle(sub.index); - }} - > - - {sub.name} - - - ) - )} + ) + )} @@ -309,37 +246,21 @@ const DropdownView: React.FC = ({ loop={true} sideOffset={10} > - {!mediaSource?.TranscodingUrl && - audioTracks?.map((track, idx: number) => ( - { - setSelectedAudioIndex(track.index); - setAudioTrack && setAudioTrack(track.index); - }} - > - - {track.name} - - - ))} - {mediaSource?.TranscodingUrl && - allAudio?.map((track, idx: number) => ( - { - if (selectedAudioIndex === track.index) return; - setSelectedAudioIndex(track.index); - ChangeTranscodingAudio(track.index); - }} - > - - {track.name} - - - ))} + {allAudio?.map((track, idx: number) => ( + { + if (selectedAudioIndex === track.index) return; + setSelectedAudioIndex(track.index); + ChangeTranscodingAudio(track.index, selectedSubtitleIndex); + }} + > + + {track.name} + + + ))}