mirror of https://github.com/morpheus65535/bazarr
184 lines
5.0 KiB
TypeScript
184 lines
5.0 KiB
TypeScript
/* eslint-disable camelcase */
|
|
|
|
import {
|
|
useRefTracksByEpisodeId,
|
|
useRefTracksByMovieId,
|
|
useSubtitleAction,
|
|
} from "@/apis/hooks";
|
|
import { useModals, withModal } from "@/modules/modals";
|
|
import { task } from "@/modules/task";
|
|
import { syncMaxOffsetSecondsOptions } from "@/pages/Settings/Subtitles/options";
|
|
import { toPython } from "@/utilities";
|
|
import { faInfoCircle } from "@fortawesome/free-solid-svg-icons";
|
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
|
import { Alert, Button, Checkbox, Divider, Stack, Text } from "@mantine/core";
|
|
import { useForm } from "@mantine/form";
|
|
import { FunctionComponent } from "react";
|
|
import { Selector, SelectorOption } from "../inputs";
|
|
|
|
const TaskName = "Syncing Subtitle";
|
|
|
|
function useReferencedSubtitles(
|
|
mediaType: "episode" | "movie",
|
|
mediaId: number,
|
|
subtitlesPath: string
|
|
) {
|
|
// We cannot call hooks conditionally, we rely on useQuery "enabled" option to do only the required API call
|
|
const episodeData = useRefTracksByEpisodeId(
|
|
subtitlesPath,
|
|
mediaId,
|
|
mediaType === "episode"
|
|
);
|
|
const movieData = useRefTracksByMovieId(
|
|
subtitlesPath,
|
|
mediaId,
|
|
mediaType === "movie"
|
|
);
|
|
|
|
const mediaData = mediaType === "episode" ? episodeData : movieData;
|
|
|
|
const subtitles: { group: string; value: string; label: string }[] = [];
|
|
|
|
if (!mediaData.data) {
|
|
return [];
|
|
} else {
|
|
if (mediaData.data.audio_tracks.length > 0) {
|
|
mediaData.data.audio_tracks.forEach((item) => {
|
|
subtitles.push({
|
|
group: "Embedded audio tracks",
|
|
value: item.stream,
|
|
label: `${item.name || item.language} (${item.stream})`,
|
|
});
|
|
});
|
|
}
|
|
|
|
if (mediaData.data.embedded_subtitles_tracks.length > 0) {
|
|
mediaData.data.embedded_subtitles_tracks.forEach((item) => {
|
|
subtitles.push({
|
|
group: "Embedded subtitles tracks",
|
|
value: item.stream,
|
|
label: `${item.name || item.language} (${item.stream})`,
|
|
});
|
|
});
|
|
}
|
|
|
|
if (mediaData.data.external_subtitles_tracks.length > 0) {
|
|
mediaData.data.external_subtitles_tracks.forEach((item) => {
|
|
if (item) {
|
|
subtitles.push({
|
|
group: "External Subtitles files",
|
|
value: item.path,
|
|
label: item.name,
|
|
});
|
|
}
|
|
});
|
|
}
|
|
|
|
return subtitles;
|
|
}
|
|
}
|
|
|
|
interface Props {
|
|
selections: FormType.ModifySubtitle[];
|
|
onSubmit?: VoidFunction;
|
|
}
|
|
|
|
interface FormValues {
|
|
reference?: string;
|
|
maxOffsetSeconds?: string;
|
|
noFixFramerate: boolean;
|
|
gss: boolean;
|
|
}
|
|
|
|
const SyncSubtitleForm: FunctionComponent<Props> = ({
|
|
selections,
|
|
onSubmit,
|
|
}) => {
|
|
if (selections.length === 0) {
|
|
throw new Error("You need to select at least 1 media to sync");
|
|
}
|
|
|
|
const { mutateAsync } = useSubtitleAction();
|
|
const modals = useModals();
|
|
|
|
const mediaType = selections[0].type;
|
|
const mediaId = selections[0].id;
|
|
const subtitlesPath = selections[0].path;
|
|
|
|
const subtitles: SelectorOption<string>[] = useReferencedSubtitles(
|
|
mediaType,
|
|
mediaId,
|
|
subtitlesPath
|
|
);
|
|
|
|
const form = useForm<FormValues>({
|
|
initialValues: {
|
|
noFixFramerate: false,
|
|
gss: false,
|
|
},
|
|
});
|
|
|
|
return (
|
|
<form
|
|
onSubmit={form.onSubmit((parameters) => {
|
|
selections.forEach((s) => {
|
|
const form: FormType.ModifySubtitle = {
|
|
...s,
|
|
reference: parameters.reference,
|
|
max_offset_seconds: parameters.maxOffsetSeconds,
|
|
no_fix_framerate: toPython(parameters.noFixFramerate),
|
|
gss: toPython(parameters.gss),
|
|
};
|
|
|
|
task.create(s.path, TaskName, mutateAsync, { action: "sync", form });
|
|
});
|
|
|
|
onSubmit?.();
|
|
modals.closeSelf();
|
|
})}
|
|
>
|
|
<Stack>
|
|
<Alert
|
|
title="Subtitles"
|
|
color="gray"
|
|
icon={<FontAwesomeIcon icon={faInfoCircle}></FontAwesomeIcon>}
|
|
>
|
|
<Text size="sm">{selections.length} subtitles selected</Text>
|
|
</Alert>
|
|
<Selector
|
|
clearable
|
|
disabled={subtitles.length === 0 || selections.length !== 1}
|
|
label="Reference"
|
|
placeholder="Default: choose automatically within video file"
|
|
options={subtitles}
|
|
{...form.getInputProps("reference")}
|
|
></Selector>
|
|
<Selector
|
|
clearable
|
|
label="Max Offset Seconds"
|
|
options={syncMaxOffsetSecondsOptions}
|
|
placeholder="Select..."
|
|
{...form.getInputProps("maxOffsetSeconds")}
|
|
></Selector>
|
|
<Checkbox
|
|
label="No Fix Framerate"
|
|
{...form.getInputProps("noFixFramerate")}
|
|
></Checkbox>
|
|
<Checkbox
|
|
label="Golden-Section Search"
|
|
{...form.getInputProps("gss")}
|
|
></Checkbox>
|
|
<Divider></Divider>
|
|
<Button type="submit">Sync</Button>
|
|
</Stack>
|
|
</form>
|
|
);
|
|
};
|
|
|
|
export const SyncSubtitleModal = withModal(SyncSubtitleForm, "sync-subtitle", {
|
|
title: "Sync Subtitle Options",
|
|
size: "lg",
|
|
});
|
|
|
|
export default SyncSubtitleForm;
|