display caption in video (#873)
This commit is contained in:
@@ -1,29 +1,64 @@
|
||||
import { useContext } from "react";
|
||||
import { MediaPlayerProviderContext } from "@renderer/context";
|
||||
import { useContext, useEffect, useRef } from "react";
|
||||
import {
|
||||
MediaPlayerProviderContext,
|
||||
ThemeProviderContext,
|
||||
} from "@renderer/context";
|
||||
import {
|
||||
MediaPlayer as VidstackMediaPlayer,
|
||||
MediaProvider as VidstackMediaProvider,
|
||||
isAudioProvider,
|
||||
isVideoProvider,
|
||||
useMediaRemote,
|
||||
TextTrack,
|
||||
MediaPlayerInstance,
|
||||
} from "@vidstack/react";
|
||||
import {
|
||||
DefaultAudioLayout,
|
||||
DefaultVideoLayout,
|
||||
defaultLayoutIcons,
|
||||
} from "@vidstack/react/player/layouts/default";
|
||||
import { TimelineEntry } from "echogarden/dist/utilities/Timeline.d.js";
|
||||
import { milisecondsToTimestamp } from "@/utils";
|
||||
|
||||
export const MediaProvider = () => {
|
||||
const { media, setMediaProvider, setDecodeError } = useContext(
|
||||
const { theme } = useContext(ThemeProviderContext);
|
||||
const { media, setMediaProvider, setDecodeError, transcription } = useContext(
|
||||
MediaPlayerProviderContext
|
||||
);
|
||||
const mediaRemote = useMediaRemote();
|
||||
const player = useRef<MediaPlayerInstance>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (!transcription?.result) return;
|
||||
if (!player?.current) return;
|
||||
|
||||
const srt = transcription.result.timeline
|
||||
.map(
|
||||
(t: TimelineEntry) =>
|
||||
`1\n${milisecondsToTimestamp(
|
||||
t.startTime * 1000
|
||||
)} --> ${milisecondsToTimestamp(t.endTime * 1000)}\n${t.text}`
|
||||
)
|
||||
.join("\n\n");
|
||||
|
||||
player.current.textTracks.clear();
|
||||
player.current.textTracks.add(
|
||||
new TextTrack({
|
||||
content: srt,
|
||||
kind: "subtitles",
|
||||
type: "srt",
|
||||
language: transcription.result.language,
|
||||
})
|
||||
);
|
||||
}, [player, transcription]);
|
||||
|
||||
if (!media?.src) return null;
|
||||
|
||||
return (
|
||||
<div className="px-2 py-4" data-testid="media-player">
|
||||
<VidstackMediaPlayer
|
||||
ref={player}
|
||||
className="my-auto"
|
||||
controls
|
||||
src={media.src}
|
||||
onCanPlayThrough={(detail, nativeEvent) => {
|
||||
mediaRemote.setTarget(nativeEvent.target);
|
||||
@@ -37,7 +72,8 @@ export const MediaProvider = () => {
|
||||
onError={(err) => setDecodeError(err.message)}
|
||||
>
|
||||
<VidstackMediaProvider />
|
||||
<DefaultAudioLayout icons={defaultLayoutIcons} />
|
||||
<DefaultAudioLayout icons={defaultLayoutIcons} colorScheme={theme} />
|
||||
<DefaultVideoLayout icons={defaultLayoutIcons} colorScheme={theme} />
|
||||
</VidstackMediaPlayer>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -38,8 +38,8 @@ export const TranscriptionEditButton = (props: {
|
||||
// generate text in SRT format from timeline entries
|
||||
transcription.result.timeline
|
||||
.map(
|
||||
(t: TimelineEntry) =>
|
||||
`${milisecondsToTimestamp(
|
||||
(t: TimelineEntry, index: number) =>
|
||||
`${index + 1}\n${milisecondsToTimestamp(
|
||||
t.startTime * 1000
|
||||
)} --> ${milisecondsToTimestamp(t.endTime * 1000)}\n${t.text}`
|
||||
)
|
||||
|
||||
@@ -10,7 +10,7 @@ type ThemeProviderProps = {
|
||||
|
||||
type ThemeProviderState = {
|
||||
theme: Theme;
|
||||
colorScheme?: Omit<Theme, 'system'>;
|
||||
colorScheme?: Omit<Theme, "system">;
|
||||
setTheme: (theme: Theme) => void;
|
||||
};
|
||||
|
||||
@@ -19,7 +19,8 @@ const initialState: ThemeProviderState = {
|
||||
setTheme: () => null,
|
||||
};
|
||||
|
||||
const ThemeProviderContext = createContext<ThemeProviderState>(initialState);
|
||||
export const ThemeProviderContext =
|
||||
createContext<ThemeProviderState>(initialState);
|
||||
|
||||
export function ThemeProvider({
|
||||
children,
|
||||
@@ -30,7 +31,7 @@ export function ThemeProvider({
|
||||
const [theme, setTheme] = useState<Theme>(
|
||||
() => (localStorage.getItem(storageKey) as Theme) || defaultTheme
|
||||
);
|
||||
const [colorScheme, setColorScheme] = useState<Omit<Theme, 'system'>>();
|
||||
const [colorScheme, setColorScheme] = useState<Omit<Theme, "system">>();
|
||||
|
||||
useEffect(() => {
|
||||
const root = window.document.documentElement;
|
||||
|
||||
@@ -49,7 +49,7 @@ export function milisecondsToTimestamp(ms: number) {
|
||||
const hours = Math.floor(ms / 3600000).toString();
|
||||
const minutes = Math.floor((ms % 3600000) / 60000).toString();
|
||||
const seconds = Math.floor(((ms % 360000) % 60000) / 1000).toString();
|
||||
const milliseconds = Math.round(((ms % 360000) % 60000) % 1000).toString();
|
||||
const milliseconds = Math.floor(ms % 1000).toString();
|
||||
return `${hours.padStart(2, "0")}:${minutes.padStart(
|
||||
2,
|
||||
"0"
|
||||
|
||||
Reference in New Issue
Block a user