diff --git a/src/video/components/Caption.tsx b/src/video/components/Caption.tsx new file mode 100644 index 00000000..c1e7d4f4 --- /dev/null +++ b/src/video/components/Caption.tsx @@ -0,0 +1,19 @@ +import { useSettings } from "@/state/settings"; + +export function Caption({ text }: { text?: string }) { + const { captionSettings } = useSettings(); + return ( + , ) in them + but if we want full customization we will have to + remove tags with a regex from raw text + */ + dangerouslySetInnerHTML={{ __html: text ?? "" }} + style={{ + ...captionSettings.style, + }} + /> + ); +} diff --git a/src/video/components/CaptionRenderer.tsx b/src/video/components/CaptionRenderer.tsx new file mode 100644 index 00000000..8f7d2a55 --- /dev/null +++ b/src/video/components/CaptionRenderer.tsx @@ -0,0 +1,56 @@ +import { useSettings } from "@/state/settings"; +import { parse, Cue } from "node-webvtt"; +import { useRef } from "react"; +import { useAsync } from "react-use"; +import { useVideoPlayerDescriptor } from "../state/hooks"; +import { useProgress } from "../state/logic/progress"; +import { useSource } from "../state/logic/source"; +import { Caption } from "./Caption"; + +export function CaptionRenderer({ + isControlsShown, +}: { + isControlsShown: boolean; +}) { + const descriptor = useVideoPlayerDescriptor(); + const source = useSource(descriptor).source; + const videoTime = useProgress(descriptor).time; + const { captionSettings } = useSettings(); + const captions = useRef([]); + + useAsync(async () => { + const url = source?.caption?.url; + if (url) { + // Is there a better way? + const text = await (await fetch(url)).text(); + captions.current = parse(text, { strict: false }).cues; + } else { + captions.current = []; + } + }, [source]); + + if (!captions.current.length) return null; + const isVisible = (start: number, end: number): boolean => { + const delayedStart = start + captionSettings.delay; + const delayedEnd = end + captionSettings.delay; + return ( + Math.max(0, delayedStart) <= videoTime && + Math.max(0, delayedEnd) >= videoTime + ); + }; + return ( + + {captions.current.map( + ({ identifier, end, start, text }) => + isVisible(start, end) && ( + + ) + )} + {isControlsShown ? ( +
+ ) : ( +
+ )} + + ); +} diff --git a/src/video/components/VideoPlayer.tsx b/src/video/components/VideoPlayer.tsx index 9553b70c..c41734ed 100644 --- a/src/video/components/VideoPlayer.tsx +++ b/src/video/components/VideoPlayer.tsx @@ -31,6 +31,7 @@ import { PopoutProviderAction } from "@/video/components/popouts/PopoutProviderA import { ChromecastAction } from "@/video/components/actions/ChromecastAction"; import { CastingTextAction } from "@/video/components/actions/CastingTextAction"; import { DownloadAction } from "@/video/components/actions/DownloadAction"; +import { CaptionRenderer } from "./CaptionRenderer"; type Props = VideoPlayerBaseProps; @@ -169,6 +170,7 @@ export function VideoPlayer(props: Props) { {show ? : null} + {props.children} diff --git a/src/video/components/internal/VideoElementInternal.tsx b/src/video/components/internal/VideoElementInternal.tsx index 335e8a9f..9cf3ec86 100644 --- a/src/video/components/internal/VideoElementInternal.tsx +++ b/src/video/components/internal/VideoElementInternal.tsx @@ -1,7 +1,7 @@ import { useVideoPlayerDescriptor } from "@/video/state/hooks"; import { useMediaPlaying } from "@/video/state/logic/mediaplaying"; import { useMisc } from "@/video/state/logic/misc"; -import { useSource } from "@/video/state/logic/source"; +// import { useSource } from "@/video/state/logic/source"; import { setProvider, unsetStateProvider } from "@/video/state/providers/utils"; import { createVideoStateProvider } from "@/video/state/providers/videoStateProvider"; import { useEffect, useMemo, useRef } from "react"; @@ -13,7 +13,7 @@ interface Props { function VideoElement(props: Props) { const descriptor = useVideoPlayerDescriptor(); const mediaPlaying = useMediaPlaying(descriptor); - const source = useSource(descriptor); + // const source = useSource(descriptor); const misc = useMisc(descriptor); const ref = useRef(null); @@ -45,9 +45,9 @@ function VideoElement(props: Props) { playsInline className="h-full w-full" > - {source.source?.caption ? ( + {/* {source.source?.caption ? ( - ) : null} + ) : null} */} ); }