import { getCaptionUrl, parseSubtitles, subtitleTypeList, } from "@/backend/helpers/captions"; import { MWCaption, MWCaptionType } from "@/backend/helpers/streams"; import { Icon, Icons } from "@/components/Icon"; import { FloatingCardView } from "@/components/popout/FloatingCard"; import { FloatingView } from "@/components/popout/FloatingView"; import { useFloatingRouter } from "@/hooks/useFloatingRouter"; import { useLoading } from "@/hooks/useLoading"; import { useVideoPlayerDescriptor } from "@/video/state/hooks"; import { useControls } from "@/video/state/logic/controls"; import { useMeta } from "@/video/state/logic/meta"; import { useSource } from "@/video/state/logic/source"; import { useMemo, useRef } from "react"; import { useTranslation } from "react-i18next"; import { PopoutListEntry, PopoutSection } from "./PopoutUtils"; const customCaption = "external-custom"; function makeCaptionId(caption: MWCaption, isLinked: boolean): string { return isLinked ? `linked-${caption.langIso}` : `external-${caption.langIso}`; } export function CaptionSelectionPopout(props: { router: ReturnType; prefix: string; }) { const { t } = useTranslation(); const descriptor = useVideoPlayerDescriptor(); const meta = useMeta(descriptor); const source = useSource(descriptor); const controls = useControls(descriptor); const linkedCaptions = useMemo( () => meta?.captions.map((v) => ({ ...v, id: makeCaptionId(v, true) })) ?? [], [meta] ); const loadingId = useRef(""); const [setCaption, loading, error] = useLoading( async (caption: MWCaption, isLinked: boolean) => { const id = makeCaptionId(caption, isLinked); loadingId.current = id; const blobUrl = await getCaptionUrl(caption); const result = await fetch(blobUrl); const text = await result.text(); parseSubtitles(text); // This will throw if the file is invalid controls.setCaption(id, blobUrl); // sometimes this doesn't work, so we add a small delay setTimeout(() => { controls.closePopout(); }, 100); } ); const currentCaption = source.source?.caption?.id; const customCaptionUploadElement = useRef(null); return ( props.router.navigate("/")} action={ } /> { controls.clearCaption(); controls.closePopout(); }} > {t("videoPlayer.popouts.noCaptions")} customCaptionUploadElement.current?.click()} > {currentCaption === customCaption ? t("videoPlayer.popouts.customCaption") : t("videoPlayer.popouts.uploadCustomCaption")} { if (!e.target.files) return; const customSubtitle = { langIso: "custom", url: URL.createObjectURL(e.target.files[0]), type: MWCaptionType.UNKNOWN, }; setCaption(customSubtitle, false); }} />

{t("videoPlayer.popouts.linkedCaptions")}

{linkedCaptions.map((link) => ( { loadingId.current = link.id; setCaption(link, true); }} > {link.langIso} ))}
); }