import { useCallback, useEffect, useRef } from "react"; import { useAsync } from "react-use"; import { ContentCaption } from "subsrt-ts/dist/types/handler"; import { parseSubtitles, sanitize } from "@/backend/helpers/captions"; import { Transition } from "@/components/Transition"; import { useSettings } from "@/state/settings"; import { useVideoPlayerDescriptor } from "../../state/hooks"; import { useProgress } from "../../state/logic/progress"; import { useSource } from "../../state/logic/source"; export function CaptionCue({ text, scale }: { text?: string; scale?: number }) { const { captionSettings } = useSettings(); const textWithNewlines = (text || "").replaceAll(/\r?\n/g, "
"); // https://www.w3.org/TR/webvtt1/#dom-construction-rules // added a
for newlines const html = sanitize(textWithNewlines, { ALLOWED_TAGS: ["c", "b", "i", "u", "span", "ruby", "rt", "br"], ADD_TAGS: ["v", "lang"], ALLOWED_ATTR: ["title", "lang"], }); return (

); } export function CaptionRendererAction({ isControlsShown, }: { isControlsShown: boolean; }) { const descriptor = useVideoPlayerDescriptor(); const source = useSource(descriptor).source; const videoTime = useProgress(descriptor).time; const { captionSettings, setCaptionDelay } = useSettings(); const captions = useRef([]); useAsync(async () => { const blobUrl = source?.caption?.url; if (blobUrl) { const result = await fetch(blobUrl); const text = await result.text(); try { captions.current = parseSubtitles(text); } catch (error) { captions.current = []; } // reset delay on every subtitle change setCaptionDelay(0); } else { captions.current = []; } }, [source?.caption?.url]); useEffect(() => { // reset delay after video ends return () => setCaptionDelay(0); // eslint-disable-next-line react-hooks/exhaustive-deps }, []); const isVisible = useCallback( ( start: number, end: number, delay: number, currentTime: number ): boolean => { const delayedStart = start / 1000 + delay; const delayedEnd = end / 1000 + delay; return ( Math.max(0, delayedStart) <= currentTime && Math.max(0, delayedEnd) >= currentTime ); }, [] ); if (!captions.current.length) return null; const visibileCaptions = captions.current.filter(({ start, end }) => isVisible(start, end, captionSettings.delay, videoTime) ); return ( {visibileCaptions.map(({ start, end, content }) => ( ))} ); }