+
+
void;
+ error?: React.ReactNode;
+}) {
+ return (
+
+
+
+
+
+ {props.children}
+
+
+ );
+}
+
+function useSubtitleList(subs: CaptionListItem[], searchQuery: string) {
+ const { t: translate } = useTranslation();
+ const unknownChoice = translate("player.menus.subtitles.unknownLanguage");
+ return useMemo(() => {
+ const input = subs
+ .map((t) => ({
+ ...t,
+ languageName:
+ getPrettyLanguageNameFromLocale(t.language) ?? unknownChoice,
+ }))
+ .filter((x) => x.opensubtitles);
+ const sorted = sortLangCodes(input.map((t) => t.language));
+ let results = input.sort((a, b) => {
+ return sorted.indexOf(a.language) - sorted.indexOf(b.language);
+ });
+
+ if (searchQuery.trim().length > 0) {
+ const fuse = new Fuse(input, {
+ includeScore: true,
+ keys: ["languageName"],
+ });
+
+ results = fuse.search(searchQuery).map((res) => res.item);
+ }
+
+ return results;
+ }, [subs, searchQuery, unknownChoice]);
+}
+
+export function OpenSubtitlesCaptionView({ id }: { id: string }) {
+ const { t } = useTranslation();
+ const router = useOverlayRouter(id);
+ const selectedCaptionId = usePlayerStore((s) => s.caption.selected?.id);
+ const [currentlyDownloading, setCurrentlyDownloading] = useState<
+ string | null
+ >(null);
+ const { selectCaptionById } = useCaptions();
+ const captionList = usePlayerStore((s) => s.captionList);
+ const getHlsCaptionList = usePlayerStore((s) => s.display?.getCaptionList);
+ const [dragging] = useState(false);
+
+ const captions = useMemo(
+ () =>
+ captionList.length !== 0 ? captionList : getHlsCaptionList?.() ?? [],
+ [captionList, getHlsCaptionList],
+ );
+
+ const [searchQuery, setSearchQuery] = useState("");
+ const subtitleList = useSubtitleList(captions, searchQuery);
+
+ const [downloadReq, startDownload] = useAsyncFn(
+ async (captionId: string) => {
+ setCurrentlyDownloading(captionId);
+ return selectCaptionById(captionId);
+ },
+ [selectCaptionById, setCurrentlyDownloading],
+ );
+
+ const content = subtitleList.map((v) => {
+ return (
+ startDownload(v.id)}
+ >
+ {v.languageName}
+
+ );
+ });
+
+ return (
+ <>
+
+
+
+
+
+ {t("player.menus.subtitles.dropSubtitleFile")}
+
+
+
+
+
router.navigate("/captions")}
+ rightSide={
+
+ }
+ >
+ {t("player.menus.subtitles.title")}
+
+
+
+
+
+
+ {content}
+
+ >
+ );
+}
+
+export default OpenSubtitlesCaptionView;
diff --git a/src/components/player/utils/captions.ts b/src/components/player/utils/captions.ts
index df64fa5a..d144340e 100644
--- a/src/components/player/utils/captions.ts
+++ b/src/components/player/utils/captions.ts
@@ -102,5 +102,6 @@ export function convertProviderCaption(
language: v.language,
url: v.url,
needsProxy: v.hasCorsRestrictions,
+ opensubtitles: v.opensubtitles,
}));
}
diff --git a/src/stores/player/slices/source.ts b/src/stores/player/slices/source.ts
index 5cbfc6db..77924426 100644
--- a/src/stores/player/slices/source.ts
+++ b/src/stores/player/slices/source.ts
@@ -54,6 +54,7 @@ export interface CaptionListItem {
url: string;
needsProxy: boolean;
hls?: boolean;
+ opensubtitles?: boolean;
}
export interface AudioTrack {