1
0
Fork 0
mirror of https://github.com/sussy-code/smov.git synced 2024-12-30 16:17:41 +01:00

add subtitle renderer and remove track element

This commit is contained in:
frost768 2023-03-09 20:09:22 +03:00
parent f264457c57
commit 875be16c4c
4 changed files with 81 additions and 4 deletions

View file

@ -0,0 +1,19 @@
import { useSettings } from "@/state/settings";
export function Caption({ text }: { text?: string }) {
const { captionSettings } = useSettings();
return (
<span
className="pointer-events-none mb-1 select-none px-1 text-center"
/*
WebVTT files may have html elements (such as <i>, <b>) 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,
}}
/>
);
}

View file

@ -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<Cue[]>([]);
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 (
<span className="flex h-full flex-col items-center justify-end">
{captions.current.map(
({ identifier, end, start, text }) =>
isVisible(start, end) && (
<Caption key={identifier ?? Math.random() * 9999999} text={text} />
)
)}
{isControlsShown ? (
<div className="h-[100px]" />
) : (
<div className="h-[50px]" />
)}
</span>
);
}

View file

@ -31,6 +31,7 @@ import { PopoutProviderAction } from "@/video/components/popouts/PopoutProviderA
import { ChromecastAction } from "@/video/components/actions/ChromecastAction"; import { ChromecastAction } from "@/video/components/actions/ChromecastAction";
import { CastingTextAction } from "@/video/components/actions/CastingTextAction"; import { CastingTextAction } from "@/video/components/actions/CastingTextAction";
import { DownloadAction } from "@/video/components/actions/DownloadAction"; import { DownloadAction } from "@/video/components/actions/DownloadAction";
import { CaptionRenderer } from "./CaptionRenderer";
type Props = VideoPlayerBaseProps; type Props = VideoPlayerBaseProps;
@ -169,6 +170,7 @@ export function VideoPlayer(props: Props) {
</Transition> </Transition>
{show ? <PopoutProviderAction /> : null} {show ? <PopoutProviderAction /> : null}
</BackdropAction> </BackdropAction>
<CaptionRenderer isControlsShown={show} />
{props.children} {props.children}
</VideoPlayerError> </VideoPlayerError>
</> </>

View file

@ -1,7 +1,7 @@
import { useVideoPlayerDescriptor } from "@/video/state/hooks"; import { useVideoPlayerDescriptor } from "@/video/state/hooks";
import { useMediaPlaying } from "@/video/state/logic/mediaplaying"; import { useMediaPlaying } from "@/video/state/logic/mediaplaying";
import { useMisc } from "@/video/state/logic/misc"; 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 { setProvider, unsetStateProvider } from "@/video/state/providers/utils";
import { createVideoStateProvider } from "@/video/state/providers/videoStateProvider"; import { createVideoStateProvider } from "@/video/state/providers/videoStateProvider";
import { useEffect, useMemo, useRef } from "react"; import { useEffect, useMemo, useRef } from "react";
@ -13,7 +13,7 @@ interface Props {
function VideoElement(props: Props) { function VideoElement(props: Props) {
const descriptor = useVideoPlayerDescriptor(); const descriptor = useVideoPlayerDescriptor();
const mediaPlaying = useMediaPlaying(descriptor); const mediaPlaying = useMediaPlaying(descriptor);
const source = useSource(descriptor); // const source = useSource(descriptor);
const misc = useMisc(descriptor); const misc = useMisc(descriptor);
const ref = useRef<HTMLVideoElement>(null); const ref = useRef<HTMLVideoElement>(null);
@ -45,9 +45,9 @@ function VideoElement(props: Props) {
playsInline playsInline
className="h-full w-full" className="h-full w-full"
> >
{source.source?.caption ? ( {/* {source.source?.caption ? (
<track default kind="captions" src={source.source.caption.url} /> <track default kind="captions" src={source.source.caption.url} />
) : null} ) : null} */}
</video> </video>
); );
} }