Fix subtitles not showing up in safari, using a blob

This commit is contained in:
mrjvs 2023-10-18 16:54:52 +02:00
parent adf5689c48
commit 2b240c8155
2 changed files with 47 additions and 9 deletions

View File

@ -1,7 +1,7 @@
import { ReactNode, useEffect, useMemo, useRef } from "react"; import { ReactNode, useEffect, useMemo, useRef } from "react";
import { makeVideoElementDisplayInterface } from "@/components/player/display/base"; import { makeVideoElementDisplayInterface } from "@/components/player/display/base";
import { convertSubtitlesToDataurl } from "@/components/player/utils/captions"; import { convertSubtitlesToObjectUrl } from "@/components/player/utils/captions";
import { playerStatus } from "@/stores/player/slices/source"; import { playerStatus } from "@/stores/player/slices/source";
import { usePlayerStore } from "@/stores/player/store"; import { usePlayerStore } from "@/stores/player/store";
@ -37,16 +37,39 @@ export function useShouldShowVideoElement() {
return true; return true;
} }
function useObjectUrl(cb: () => string | null, deps: any[]) {
const lastObjectUrl = useRef<string | null>(null);
const output = useMemo(() => {
if (lastObjectUrl.current) URL.revokeObjectURL(lastObjectUrl.current);
const data = cb();
lastObjectUrl.current = data;
return data;
// deps are passed in, cb is known not to be changed
// eslint-disable-next-line react-hooks/exhaustive-deps
}, deps);
useEffect(() => {
return () => {
// this is intentionally done only in cleanup
// eslint-disable-next-line react-hooks/exhaustive-deps
if (lastObjectUrl.current) URL.revokeObjectURL(lastObjectUrl.current);
};
}, []);
return output;
}
function VideoElement() { function VideoElement() {
const videoEl = useRef<HTMLVideoElement>(null); const videoEl = useRef<HTMLVideoElement>(null);
const trackEl = useRef<HTMLTrackElement>(null);
const display = usePlayerStore((s) => s.display); const display = usePlayerStore((s) => s.display);
const srtData = usePlayerStore((s) => s.caption.selected?.srtData); const srtData = usePlayerStore((s) => s.caption.selected?.srtData);
const captionAsTrack = usePlayerStore((s) => s.caption.asTrack); const captionAsTrack = usePlayerStore((s) => s.caption.asTrack);
const language = usePlayerStore((s) => s.caption.selected?.language); const language = usePlayerStore((s) => s.caption.selected?.language);
const trackObjectUrl = useObjectUrl(
const trackData = useMemo(() => { () => (srtData ? convertSubtitlesToObjectUrl(srtData) : null),
return srtData ? convertSubtitlesToDataurl(srtData) : null; [srtData]
}, [srtData]); );
// report video element to display interface // report video element to display interface
useEffect(() => { useEffect(() => {
@ -55,14 +78,21 @@ function VideoElement() {
} }
}, [display, videoEl]); }, [display, videoEl]);
// select track as showing if it exists
useEffect(() => {
if (trackEl.current) {
trackEl.current.track.mode = "showing";
}
}, [trackEl]);
let subtitleTrack: ReactNode = null; let subtitleTrack: ReactNode = null;
if (captionAsTrack && trackData && language) if (captionAsTrack && trackObjectUrl && language)
subtitleTrack = ( subtitleTrack = (
<track <track
label="Subtitles" label="movie-web"
kind="subtitles" kind="subtitles"
srcLang={language} srcLang={language}
src={trackData} src={trackObjectUrl}
default default
/> />
); );

View File

@ -41,5 +41,13 @@ export function parseSubtitles(text: string): CaptionCueType[] {
} }
export function convertSubtitlesToDataurl(text: string): string { export function convertSubtitlesToDataurl(text: string): string {
return `data:text/vtt;${convertSubtitlesToVtt(text)}`; return `data:text/vtt,${convertSubtitlesToVtt(text)}`;
}
export function convertSubtitlesToObjectUrl(text: string): string {
return URL.createObjectURL(
new Blob([convertSubtitlesToVtt(text)], {
type: "text/vtt",
})
);
} }