import Fuse from "fuse.js";
import { ReactNode, useRef, useState } from "react";
import { useAsync, useAsyncFn } from "react-use";
import { convert } from "subsrt-ts";
import { SubtitleSearchItem, subtitleTypeList } from "@/backend/helpers/subs";
import { FlagIcon } from "@/components/FlagIcon";
import { useCaptions } from "@/components/player/hooks/useCaptions";
import { Menu } from "@/components/player/internals/ContextMenu";
import { Input } from "@/components/player/internals/ContextMenu/Input";
import { SelectableLink } from "@/components/player/internals/ContextMenu/Links";
import { getLanguageFromIETF } from "@/components/player/utils/language";
import { useOverlayRouter } from "@/hooks/useOverlayRouter";
import { usePlayerStore } from "@/stores/player/store";
import { useSubtitleStore } from "@/stores/subtitles";
import { sortLangCodes } from "@/utils/sortLangCodes";
export function CaptionOption(props: {
countryCode?: string;
children: React.ReactNode;
selected?: boolean;
loading?: boolean;
onClick?: () => void;
error?: React.ReactNode;
}) {
return (
{props.children}
);
}
function searchSubs(
subs: (SubtitleSearchItem & { languageName: string })[],
searchQuery: string
) {
const sorted = sortLangCodes(subs.map((t) => t.attributes.language));
let results = subs.sort((a, b) => {
return (
sorted.indexOf(a.attributes.language) -
sorted.indexOf(b.attributes.language)
);
});
if (searchQuery.trim().length > 0) {
const fuse = new Fuse(subs, {
includeScore: true,
keys: ["languageName"],
});
results = fuse.search(searchQuery).map((res) => res.item);
}
return results;
}
function CustomCaptionOption() {
const lang = usePlayerStore((s) => s.caption.selected?.language);
const setCaption = usePlayerStore((s) => s.setCaption);
const setCustomSubs = useSubtitleStore((s) => s.setCustomSubs);
const fileInput = useRef(null);
return (
fileInput.current?.click()}
>
Upload captions
{
if (!e.target.files) return;
const reader = new FileReader();
reader.addEventListener("load", (event) => {
if (!event.target || typeof event.target.result !== "string")
return;
const converted = convert(event.target.result, "srt");
setCaption({
language: "custom",
srtData: converted,
});
setCustomSubs();
});
reader.readAsText(e.target.files[0], "utf-8");
}}
/>
);
}
export function CaptionsView({ id }: { id: string }) {
const router = useOverlayRouter(id);
const lang = usePlayerStore((s) => s.caption.selected?.language);
const [currentlyDownloading, setCurrentlyDownloading] = useState<
string | null
>(null);
const { search, download, disable } = useCaptions();
const [searchQuery, setSearchQuery] = useState("");
const req = useAsync(async () => search(), [search]);
const [downloadReq, startDownload] = useAsyncFn(
async (subtitleId: string, language: string) => {
setCurrentlyDownloading(subtitleId);
return download(subtitleId, language);
},
[download, setCurrentlyDownloading]
);
let content: ReactNode = null;
if (req.loading) content = loading...
;
else if (req.error) content = errored!
;
else if (req.value) {
const subs = req.value.filter(Boolean).map((v) => {
const languageName =
getLanguageFromIETF(v.attributes.language) ?? "unknown";
return {
...v,
languageName,
};
});
content = searchSubs(subs, searchQuery).map((v) => {
return (
startDownload(
v.attributes.legacy_subtitle_id,
v.attributes.language
)
}
>
{v.languageName}
);
});
}
return (
<>
disable()} selected={!lang}>
Off
{content}
>
);
}