mirror of
https://github.com/movie-web/movie-web.git
synced 2025-01-07 19:56:00 +00:00
auto select subtitle
This commit is contained in:
parent
9e961223f6
commit
2178057633
|
@ -4,6 +4,10 @@ import DOMPurify from "dompurify";
|
||||||
import { parse, detect, list } from "subsrt-ts";
|
import { parse, detect, list } from "subsrt-ts";
|
||||||
import { ContentCaption } from "subsrt-ts/dist/types/handler";
|
import { ContentCaption } from "subsrt-ts/dist/types/handler";
|
||||||
|
|
||||||
|
export const customCaption = "external-custom";
|
||||||
|
export function makeCaptionId(caption: MWCaption, isLinked: boolean): string {
|
||||||
|
return isLinked ? `linked-${caption.langIso}` : `external-${caption.langIso}`;
|
||||||
|
}
|
||||||
export const subtitleTypeList = list().map((type) => `.${type}`);
|
export const subtitleTypeList = list().map((type) => `.${type}`);
|
||||||
export const sanitize = DOMPurify.sanitize;
|
export const sanitize = DOMPurify.sanitize;
|
||||||
export async function getCaptionUrl(caption: MWCaption): Promise<string> {
|
export async function getCaptionUrl(caption: MWCaption): Promise<string> {
|
||||||
|
|
|
@ -1,4 +1,11 @@
|
||||||
import { MWStreamQuality, MWStreamType } from "@/backend/helpers/streams";
|
import { getCaptionUrl, makeCaptionId } from "@/backend/helpers/captions";
|
||||||
|
import {
|
||||||
|
MWCaption,
|
||||||
|
MWStreamQuality,
|
||||||
|
MWStreamType,
|
||||||
|
} from "@/backend/helpers/streams";
|
||||||
|
import { captionLanguages } from "@/setup/iso6391";
|
||||||
|
import { useSettings } from "@/state/settings";
|
||||||
import { useInitialized } from "@/video/components/hooks/useInitialized";
|
import { useInitialized } from "@/video/components/hooks/useInitialized";
|
||||||
import { useVideoPlayerDescriptor } from "@/video/state/hooks";
|
import { useVideoPlayerDescriptor } from "@/video/state/hooks";
|
||||||
import { useControls } from "@/video/state/logic/controls";
|
import { useControls } from "@/video/state/logic/controls";
|
||||||
|
@ -10,6 +17,19 @@ interface SourceControllerProps {
|
||||||
quality: MWStreamQuality;
|
quality: MWStreamQuality;
|
||||||
providerId?: string;
|
providerId?: string;
|
||||||
embedId?: string;
|
embedId?: string;
|
||||||
|
captions: MWCaption[];
|
||||||
|
}
|
||||||
|
async function tryFetch(captions: MWCaption[]) {
|
||||||
|
for (let i = 0; i < captions.length; i += 1) {
|
||||||
|
const caption = captions[i];
|
||||||
|
try {
|
||||||
|
const blobUrl = await getCaptionUrl(caption);
|
||||||
|
return { caption, blobUrl };
|
||||||
|
} catch (error) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function SourceController(props: SourceControllerProps) {
|
export function SourceController(props: SourceControllerProps) {
|
||||||
|
@ -17,13 +37,35 @@ export function SourceController(props: SourceControllerProps) {
|
||||||
const controls = useControls(descriptor);
|
const controls = useControls(descriptor);
|
||||||
const { initialized } = useInitialized(descriptor);
|
const { initialized } = useInitialized(descriptor);
|
||||||
const didInitialize = useRef<boolean>(false);
|
const didInitialize = useRef<boolean>(false);
|
||||||
|
const { captionSettings } = useSettings();
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (didInitialize.current) return;
|
if (didInitialize.current) return;
|
||||||
if (!initialized) return;
|
if (!initialized) return;
|
||||||
controls.setSource(props);
|
controls.setSource(props);
|
||||||
|
// get preferred language
|
||||||
|
const preferredLanguage = captionLanguages.find(
|
||||||
|
(v) => v.id === captionSettings.language
|
||||||
|
);
|
||||||
|
if (!preferredLanguage) return;
|
||||||
|
const captions = props.captions.filter(
|
||||||
|
(v) =>
|
||||||
|
// langIso may contain the English name or the native name of the language
|
||||||
|
v.langIso.indexOf(preferredLanguage.englishName) !== -1 ||
|
||||||
|
v.langIso.indexOf(preferredLanguage.nativeName) !== -1
|
||||||
|
);
|
||||||
|
if (!captions) return;
|
||||||
|
// caption url can return a response other than 200
|
||||||
|
// that's why we fetch until we get a 200 response
|
||||||
|
tryFetch(captions).then((response) => {
|
||||||
|
// none of them were successful
|
||||||
|
if (!response) return;
|
||||||
|
// set the preferred language
|
||||||
|
const id = makeCaptionId(response.caption, true);
|
||||||
|
controls.setCaption(id, response.blobUrl);
|
||||||
|
});
|
||||||
|
|
||||||
didInitialize.current = true;
|
didInitialize.current = true;
|
||||||
}, [props, controls, initialized]);
|
}, [props, controls, initialized, captionSettings.language]);
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
import {
|
import {
|
||||||
|
customCaption,
|
||||||
getCaptionUrl,
|
getCaptionUrl,
|
||||||
|
makeCaptionId,
|
||||||
parseSubtitles,
|
parseSubtitles,
|
||||||
subtitleTypeList,
|
subtitleTypeList,
|
||||||
} from "@/backend/helpers/captions";
|
} from "@/backend/helpers/captions";
|
||||||
|
@ -17,11 +19,6 @@ import { useMemo, useRef } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { PopoutListEntry, PopoutSection } from "./PopoutUtils";
|
import { PopoutListEntry, PopoutSection } from "./PopoutUtils";
|
||||||
|
|
||||||
const customCaption = "external-custom";
|
|
||||||
function makeCaptionId(caption: MWCaption, isLinked: boolean): string {
|
|
||||||
return isLinked ? `linked-${caption.langIso}` : `external-${caption.langIso}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function CaptionSelectionPopout(props: {
|
export function CaptionSelectionPopout(props: {
|
||||||
router: ReturnType<typeof useFloatingRouter>;
|
router: ReturnType<typeof useFloatingRouter>;
|
||||||
prefix: string;
|
prefix: string;
|
||||||
|
|
|
@ -106,14 +106,7 @@ export function CaptionSettingsPopout(props: {
|
||||||
captionSettings.style.backgroundColor.substring(7, 9),
|
captionSettings.style.backgroundColor.substring(7, 9),
|
||||||
16
|
16
|
||||||
)}
|
)}
|
||||||
onChange={(e) =>
|
onChange={(e) => setCaptionBackgroundColor(e.target.valueAsNumber)}
|
||||||
setCaptionBackgroundColor(
|
|
||||||
`${captionSettings.style.backgroundColor.substring(
|
|
||||||
0,
|
|
||||||
7
|
|
||||||
)}${e.target.valueAsNumber.toString(16)}`
|
|
||||||
)
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
<div className="flex flex-row justify-between">
|
<div className="flex flex-row justify-between">
|
||||||
<label className="font-bold" htmlFor="color">
|
<label className="font-bold" htmlFor="color">
|
||||||
|
|
|
@ -66,6 +66,7 @@ export default function VideoTesterView() {
|
||||||
source={video.streamUrl}
|
source={video.streamUrl}
|
||||||
type={videoType}
|
type={videoType}
|
||||||
quality={MWStreamQuality.QUNKNOWN}
|
quality={MWStreamQuality.QUNKNOWN}
|
||||||
|
captions={[]}
|
||||||
/>
|
/>
|
||||||
</VideoPlayer>
|
</VideoPlayer>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -148,6 +148,7 @@ export function MediaViewPlayer(props: MediaViewPlayerProps) {
|
||||||
quality={props.stream.quality}
|
quality={props.stream.quality}
|
||||||
embedId={props.stream.embedId}
|
embedId={props.stream.embedId}
|
||||||
providerId={props.stream.providerId}
|
providerId={props.stream.providerId}
|
||||||
|
captions={props.stream.captions}
|
||||||
/>
|
/>
|
||||||
<ProgressListenerController
|
<ProgressListenerController
|
||||||
startAt={firstStartTime.current}
|
startAt={firstStartTime.current}
|
||||||
|
|
Loading…
Reference in a new issue