mirror of
https://github.com/sussy-code/smov.git
synced 2024-12-21 14:47:41 +01:00
add subtitle renderer and remove track element
This commit is contained in:
parent
f264457c57
commit
875be16c4c
4 changed files with 81 additions and 4 deletions
19
src/video/components/Caption.tsx
Normal file
19
src/video/components/Caption.tsx
Normal 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,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
56
src/video/components/CaptionRenderer.tsx
Normal file
56
src/video/components/CaptionRenderer.tsx
Normal 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>
|
||||||
|
);
|
||||||
|
}
|
|
@ -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>
|
||||||
</>
|
</>
|
||||||
|
|
|
@ -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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue