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 { 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) {
|
|||
</Transition>
|
||||
{show ? <PopoutProviderAction /> : null}
|
||||
</BackdropAction>
|
||||
<CaptionRenderer isControlsShown={show} />
|
||||
{props.children}
|
||||
</VideoPlayerError>
|
||||
</>
|
||||
|
|
|
@ -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<HTMLVideoElement>(null);
|
||||
|
||||
|
@ -45,9 +45,9 @@ function VideoElement(props: Props) {
|
|||
playsInline
|
||||
className="h-full w-full"
|
||||
>
|
||||
{source.source?.caption ? (
|
||||
{/* {source.source?.caption ? (
|
||||
<track default kind="captions" src={source.source.caption.url} />
|
||||
) : null}
|
||||
) : null} */}
|
||||
</video>
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue