From 596e97e1baa77a08c328a4fc072f2ead487d0981 Mon Sep 17 00:00:00 2001 From: mrjvs Date: Wed, 18 Oct 2023 15:00:12 +0200 Subject: [PATCH] track element support --- .../player/internals/VideoContainer.tsx | 30 +++++++++++++++++-- src/components/player/utils/captions.ts | 13 +++++++- 2 files changed, 40 insertions(+), 3 deletions(-) diff --git a/src/components/player/internals/VideoContainer.tsx b/src/components/player/internals/VideoContainer.tsx index eef906b9..898fbb4e 100644 --- a/src/components/player/internals/VideoContainer.tsx +++ b/src/components/player/internals/VideoContainer.tsx @@ -1,6 +1,10 @@ -import { useEffect, useRef } from "react"; +import { ReactNode, useEffect, useMemo, useRef } from "react"; import { makeVideoElementDisplayInterface } from "@/components/player/display/base"; +import { + convertSubtitlesToVtt, + vttToDataurl, +} from "@/components/player/utils/captions"; import { playerStatus } from "@/stores/player/slices/source"; import { usePlayerStore } from "@/stores/player/store"; @@ -39,6 +43,14 @@ export function useShouldShowVideoElement() { function VideoElement() { const videoEl = useRef(null); const display = usePlayerStore((s) => s.display); + const srtData = usePlayerStore((s) => s.caption.selected?.srtData); + const captionAsTrack = usePlayerStore((s) => s.caption.asTrack); + const language = usePlayerStore((s) => s.caption.selected?.language); + + const trackData = useMemo( + () => (srtData ? vttToDataurl(convertSubtitlesToVtt(srtData)) : null), + [srtData] + ); // report video element to display interface useEffect(() => { @@ -47,13 +59,27 @@ function VideoElement() { } }, [display, videoEl]); + let subtitleTrack: ReactNode = null; + if (captionAsTrack && trackData && language) + subtitleTrack = ( + + ); + return ( ); } diff --git a/src/components/player/utils/captions.ts b/src/components/player/utils/captions.ts index c048908e..1a4d2183 100644 --- a/src/components/player/utils/captions.ts +++ b/src/components/player/utils/captions.ts @@ -23,7 +23,7 @@ export function makeQueId(index: number, start: number, end: number): string { return `${index}-${start}-${end}`; } -export function parseSubtitles(text: string): CaptionCueType[] { +export function convertSubtitlesToVtt(text: string): string { const textTrimmed = text.trim(); if (textTrimmed === "") { throw new Error("Given text is empty"); @@ -32,5 +32,16 @@ export function parseSubtitles(text: string): CaptionCueType[] { if (detect(vtt) === "") { throw new Error("Invalid subtitle format"); } + return vtt; +} + +export function parseSubtitles(text: string): CaptionCueType[] { + const vtt = convertSubtitlesToVtt(text); return parse(vtt).filter((cue) => cue.type === "caption") as CaptionCueType[]; } + +export function vttToDataurl(vtt: string): string { + const bytes = new TextEncoder().encode(vtt); + const encoded = btoa(String.fromCodePoint(...bytes)); + return `data:text/vtt;base64,${encoded}`; +}