Add support of uploading multiple movie subtitles at the same time

pull/1515/head
LASER-Yi 3 years ago
parent f05daa8223
commit a5ecd84605

@ -1,105 +1,199 @@
import React, { FunctionComponent, useEffect, useMemo, useState } from "react"; import { faTrash } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import React, {
FunctionComponent,
useCallback,
useMemo,
useState,
} from "react";
import { Button, Container, Form } from "react-bootstrap"; import { Button, Container, Form } from "react-bootstrap";
import { FileForm, LanguageSelector } from ".."; import { Column, Row } from "react-table";
import { dispatchTask } from "../../@modules/task"; import { dispatchTask } from "../../@modules/task";
import { createTask } from "../../@modules/task/utilites"; import { createTask } from "../../@modules/task/utilites";
import { import { useProfileBy, useProfileItemsToLanguages } from "../../@redux/hooks";
useEnabledLanguages,
useLanguageBy,
useProfileBy,
} from "../../@redux/hooks";
import { MoviesApi } from "../../apis"; import { MoviesApi } from "../../apis";
import { BuildKey } from "../../utilites";
import { FileForm } from "../inputs";
import { LanguageSelector } from "../LanguageSelector";
import { SimpleTable } from "../tables";
import BaseModal, { BaseModalProps } from "./BaseModal"; import BaseModal, { BaseModalProps } from "./BaseModal";
import { useModalInformation } from "./hooks"; import { useModalInformation } from "./hooks";
interface PendingSubtitle {
file: File;
language: Language.Info;
forced: boolean;
}
export const TaskGroupName = "Uploading Subtitles..."; export const TaskGroupName = "Uploading Subtitles...";
const MovieUploadModal: FunctionComponent<BaseModalProps> = (props) => { const MovieUploadModal: FunctionComponent<BaseModalProps> = (props) => {
const modal = props; const modal = props;
const availableLanguages = useEnabledLanguages();
const { payload, closeModal } = useModalInformation<Item.Movie>( const { payload, closeModal } = useModalInformation<Item.Movie>(
modal.modalKey modal.modalKey
); );
const [language, setLanguage] = useState<Nullable<Language.Info>>(null);
const profile = useProfileBy(payload?.profileId); const profile = useProfileBy(payload?.profileId);
const defaultLanguage = useLanguageBy(profile?.items[0]?.language); const availableLanguages = useProfileItemsToLanguages(profile);
useEffect(() => setLanguage(defaultLanguage ?? null), [defaultLanguage]); const [pending, setPending] = useState<PendingSubtitle[]>([]);
const [file, setFile] = useState<Nullable<File>>(null); const filelist = useMemo(() => pending.map((v) => v.file), [pending]);
const [forced, setForced] = useState(false);
const canUpload = useMemo(() => { const setFiles = useCallback(
return file !== null && language?.code2; (files: File[]) => {
}, [language, file]); const list: PendingSubtitle[] = files.map((v) => ({
file: v,
forced: availableLanguages[0].forced ?? false,
language: availableLanguages[0],
}));
setPending(list);
},
[availableLanguages]
);
const footer = ( const upload = useCallback(() => {
<Button if (payload === null || pending.length === 0) {
disabled={!canUpload} return;
onClick={() => { }
if (file && payload && language) {
const id = payload.radarrId; const { radarrId } = payload;
const task = createTask(
file.name, const tasks = pending.map((v) => {
id, const { file, language, forced } = v;
MoviesApi.uploadSubtitles.bind(MoviesApi),
id, return createTask(
{ file.name,
file: file, radarrId,
forced, MoviesApi.uploadSubtitles.bind(MoviesApi),
hi: false, radarrId,
language: language.code2, {
} file: file,
); forced,
dispatchTask(TaskGroupName, [task], "Uploading subtitles..."); hi: false,
closeModal(props.modalKey); language: language.code2,
} }
}} );
> });
Upload
</Button> dispatchTask(TaskGroupName, tasks, "Uploading...");
setFiles([]);
closeModal();
}, [payload, closeModal, pending, setFiles]);
const modify = useCallback(
(row: Row<PendingSubtitle>, info?: PendingSubtitle) => {
setPending((pd) => {
const newPending = [...pd];
if (info) {
newPending[row.index] = info;
} else {
newPending.splice(row.index, 1);
}
return newPending;
});
},
[]
); );
return ( const columns = useMemo<Column<PendingSubtitle>[]>(
<BaseModal title={`Upload - ${payload?.title}`} footer={footer} {...modal}> () => [
<Container fluid> {
<Form> id: "name",
<Form.Group> Header: "File",
<Form.Label>Language</Form.Label> accessor: (d) => d.file.name,
},
{
Header: "Forced",
accessor: "forced",
Cell: ({ row, value, update }) => {
const { original, index } = row;
return (
<Form.Check
custom
id={BuildKey(index, original.file.name, "forced")}
checked={value}
onChange={(v) => {
const newInfo = { ...row.original };
newInfo.forced = v.target.checked;
update && update(row, newInfo);
}}
></Form.Check>
);
},
},
{
Header: "Language",
accessor: "language",
className: "w-25",
Cell: ({ row, update, value }) => {
return (
<LanguageSelector <LanguageSelector
options={availableLanguages} options={availableLanguages}
value={language} value={value}
onChange={(lang) => { onChange={(lang) => {
if (lang) { if (lang && update) {
setLanguage(lang); const newInfo = { ...row.original };
newInfo.language = lang;
update(row, newInfo);
} }
}} }}
></LanguageSelector> ></LanguageSelector>
</Form.Group> );
},
},
{
accessor: "file",
Cell: ({ row, update }) => {
return (
<Button
size="sm"
variant="light"
onClick={() => {
update && update(row);
}}
>
<FontAwesomeIcon icon={faTrash}></FontAwesomeIcon>
</Button>
);
},
},
],
[availableLanguages]
);
const canUpload = pending.length > 0;
const footer = (
<Button disabled={!canUpload} onClick={upload}>
Upload
</Button>
);
return (
<BaseModal title={`Upload - ${payload?.title}`} footer={footer} {...modal}>
<Container fluid className="flex-column">
<Form>
<Form.Group> <Form.Group>
<Form.Label>Subtitle File</Form.Label>
<FileForm <FileForm
emptyText="Select..." emptyText="Select..."
onChange={(list) => { disabled={canUpload || availableLanguages.length === 0}
setFile(list[0]); multiple
}} value={filelist}
onChange={setFiles}
></FileForm> ></FileForm>
</Form.Group> </Form.Group>
<Form.Group>
<Form.Check
custom
id="forced-checkbox"
defaultChecked={forced}
onChange={(e) => setForced(e.target.checked)}
label="Forced"
></Form.Check>
</Form.Group>
</Form> </Form>
<div hidden={!canUpload}>
<SimpleTable
columns={columns}
data={pending}
responsive={false}
update={modify}
></SimpleTable>
</div>
</Container> </Container>
</BaseModal> </BaseModal>
); );

@ -14,13 +14,7 @@ import React, {
} from "react"; } from "react";
import { Button, Container, Form } from "react-bootstrap"; import { Button, Container, Form } from "react-bootstrap";
import { Column, TableUpdater } from "react-table"; import { Column, TableUpdater } from "react-table";
import { import { FileForm, LanguageSelector, MessageIcon, SimpleTable } from "..";
AsyncButton,
FileForm,
LanguageSelector,
MessageIcon,
SimpleTable,
} from "..";
import { dispatchTask } from "../../@modules/task"; import { dispatchTask } from "../../@modules/task";
import { createTask } from "../../@modules/task/utilites"; import { createTask } from "../../@modules/task/utilites";
import { useProfileBy, useProfileItemsToLanguages } from "../../@redux/hooks"; import { useProfileBy, useProfileItemsToLanguages } from "../../@redux/hooks";
@ -53,8 +47,6 @@ const SeriesUploadModal: FunctionComponent<SerieProps & BaseModalProps> = ({
modal.modalKey modal.modalKey
); );
const [uploading, setUpload] = useState(false);
const [pending, setPending] = useState<PendingSubtitle[]>([]); const [pending, setPending] = useState<PendingSubtitle[]>([]);
const profile = useProfileBy(payload?.profileId); const profile = useProfileBy(payload?.profileId);
@ -119,7 +111,7 @@ const SeriesUploadModal: FunctionComponent<SerieProps & BaseModalProps> = ({
[checkEpisodes] [checkEpisodes]
); );
const uploadSubtitles = useCallback(async () => { const upload = useCallback(() => {
if (payload === null || language === null) { if (payload === null || language === null) {
return; return;
} }
@ -150,7 +142,9 @@ const SeriesUploadModal: FunctionComponent<SerieProps & BaseModalProps> = ({
}); });
dispatchTask(TaskGroupName, tasks, "Uploading subtitles..."); dispatchTask(TaskGroupName, tasks, "Uploading subtitles...");
}, [payload, pending, language]); setFiles([]);
closeModal();
}, [payload, pending, language, closeModal, setFiles]);
const canUpload = useMemo( const canUpload = useMemo(
() => () =>
@ -234,7 +228,6 @@ const SeriesUploadModal: FunctionComponent<SerieProps & BaseModalProps> = ({
return ( return (
<Selector <Selector
disabled={uploading}
options={options} options={options}
value={value ?? null} value={value ?? null}
onChange={change} onChange={change}
@ -249,7 +242,6 @@ const SeriesUploadModal: FunctionComponent<SerieProps & BaseModalProps> = ({
<Button <Button
size="sm" size="sm"
variant="light" variant="light"
disabled={uploading}
onClick={() => { onClick={() => {
update && update(row); update && update(row);
}} }}
@ -260,7 +252,7 @@ const SeriesUploadModal: FunctionComponent<SerieProps & BaseModalProps> = ({
}, },
}, },
], ],
[language?.code2, episodes, uploading] [language?.code2, episodes]
); );
const updateItem = useCallback<TableUpdater<PendingSubtitle>>( const updateItem = useCallback<TableUpdater<PendingSubtitle>>(
@ -282,7 +274,6 @@ const SeriesUploadModal: FunctionComponent<SerieProps & BaseModalProps> = ({
<div className="d-flex flex-row flex-grow-1 justify-content-between"> <div className="d-flex flex-row flex-grow-1 justify-content-between">
<div className="w-25"> <div className="w-25">
<LanguageSelector <LanguageSelector
disabled={uploading}
options={avaliableLanguages} options={avaliableLanguages}
value={language} value={language}
onChange={(l) => { onChange={(l) => {
@ -294,7 +285,6 @@ const SeriesUploadModal: FunctionComponent<SerieProps & BaseModalProps> = ({
</div> </div>
<div> <div>
<Button <Button
hidden={uploading}
disabled={pending.length === 0} disabled={pending.length === 0}
variant="outline-secondary" variant="outline-secondary"
className="mr-2" className="mr-2"
@ -302,29 +292,15 @@ const SeriesUploadModal: FunctionComponent<SerieProps & BaseModalProps> = ({
> >
Clean Clean
</Button> </Button>
<AsyncButton <Button disabled={!canUpload} onClick={upload}>
disabled={!canUpload}
onChange={setUpload}
promise={uploadSubtitles}
onSuccess={() => {
closeModal();
setFiles([]);
}}
>
Upload Upload
</AsyncButton> </Button>
</div> </div>
</div> </div>
); );
return ( return (
<BaseModal <BaseModal size="lg" title="Upload Subtitles" footer={footer} {...modal}>
size="lg"
title="Upload Subtitles"
closeable={!uploading}
footer={footer}
{...modal}
>
<Container fluid className="flex-column"> <Container fluid className="flex-column">
<Form> <Form>
<Form.Group> <Form.Group>

Loading…
Cancel
Save