A small web app for watching movies and shows easily
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

62 lines
2.2 KiB

import DOMPurify from "dompurify";
import { convert, detect, list, parse } from "subsrt-ts";
import { ContentCaption } from "subsrt-ts/dist/types/handler";
import { mwFetch, proxiedFetch } from "@/backend/helpers/fetch";
import { MWCaption, MWCaptionType } from "@/backend/helpers/streams";
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 function isSupportedSubtitle(url: string): boolean {
return subtitleTypeList.some((type) => url.endsWith(type));
}
export function getMWCaptionTypeFromUrl(url: string): MWCaptionType {
if (!isSupportedSubtitle(url)) return MWCaptionType.UNKNOWN;
const type = subtitleTypeList.find((t) => url.endsWith(t));
if (!type) return MWCaptionType.UNKNOWN;
return type.slice(1) as MWCaptionType;
}
export const sanitize = DOMPurify.sanitize;
export async function getCaptionUrl(caption: MWCaption): Promise<string> {
let captionBlob: Blob;
if (caption.url.startsWith("blob:")) {
// custom subtitle
captionBlob = await (await fetch(caption.url)).blob();
} else if (caption.needsProxy) {
captionBlob = await proxiedFetch<Blob>(caption.url, {
responseType: "blob" as any,
});
} else {
captionBlob = await mwFetch<Blob>(caption.url, {
responseType: "blob" as any,
});
}
// convert to vtt for track element source which will be used in PiP mode
const text = await captionBlob.text();
const vtt = convert(text, "vtt");
return URL.createObjectURL(new Blob([vtt], { type: "text/vtt" }));
}
export function revokeCaptionBlob(url: string | undefined) {
if (url && url.startsWith("blob:")) {
URL.revokeObjectURL(url);
}
}
export function parseSubtitles(text: string): ContentCaption[] {
const textTrimmed = text.trim();
if (textTrimmed === "") {
throw new Error("Given text is empty");
}
if (detect(textTrimmed) === "") {
throw new Error("Invalid subtitle format");
}
return parse(textTrimmed).filter(
(cue) => cue.type === "caption"
) as ContentCaption[];
}