summaryrefslogtreecommitdiffhomepage
path: root/frontend
diff options
context:
space:
mode:
authorLASER-Yi <[email protected]>2021-08-22 21:59:48 +0800
committerLASER-Yi <[email protected]>2021-08-22 21:59:48 +0800
commita5ecd84605cd46e2feab87b6275879a3962d76fb (patch)
tree6c57f0af1687fe0b1c152d4270819351c722fa8f /frontend
parentf05daa8223d7b95e2f1c95d11a88558111ff512b (diff)
downloadbazarr-a5ecd84605cd46e2feab87b6275879a3962d76fb.tar.gz
bazarr-a5ecd84605cd46e2feab87b6275879a3962d76fb.zip
Add support of uploading multiple movie subtitles at the same time
Diffstat (limited to 'frontend')
-rw-r--r--frontend/src/components/modals/MovieUploadModal.tsx224
-rw-r--r--frontend/src/components/modals/SeriesUploadModal.tsx42
2 files changed, 168 insertions, 98 deletions
diff --git a/frontend/src/components/modals/MovieUploadModal.tsx b/frontend/src/components/modals/MovieUploadModal.tsx
index f93465836..37785aada 100644
--- a/frontend/src/components/modals/MovieUploadModal.tsx
+++ b/frontend/src/components/modals/MovieUploadModal.tsx
@@ -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 { FileForm, LanguageSelector } from "..";
+import { Column, Row } from "react-table";
import { dispatchTask } from "../../@modules/task";
import { createTask } from "../../@modules/task/utilites";
-import {
- useEnabledLanguages,
- useLanguageBy,
- useProfileBy,
-} from "../../@redux/hooks";
+import { useProfileBy, useProfileItemsToLanguages } from "../../@redux/hooks";
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 { useModalInformation } from "./hooks";
+interface PendingSubtitle {
+ file: File;
+ language: Language.Info;
+ forced: boolean;
+}
+
export const TaskGroupName = "Uploading Subtitles...";
const MovieUploadModal: FunctionComponent<BaseModalProps> = (props) => {
const modal = props;
- const availableLanguages = useEnabledLanguages();
-
const { payload, closeModal } = useModalInformation<Item.Movie>(
modal.modalKey
);
- const [language, setLanguage] = useState<Nullable<Language.Info>>(null);
-
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 [forced, setForced] = useState(false);
+ const filelist = useMemo(() => pending.map((v) => v.file), [pending]);
- const canUpload = useMemo(() => {
- return file !== null && language?.code2;
- }, [language, file]);
+ const setFiles = useCallback(
+ (files: File[]) => {
+ const list: PendingSubtitle[] = files.map((v) => ({
+ file: v,
+ forced: availableLanguages[0].forced ?? false,
+ language: availableLanguages[0],
+ }));
+ setPending(list);
+ },
+ [availableLanguages]
+ );
- const footer = (
- <Button
- disabled={!canUpload}
- onClick={() => {
- if (file && payload && language) {
- const id = payload.radarrId;
- const task = createTask(
- file.name,
- id,
- MoviesApi.uploadSubtitles.bind(MoviesApi),
- id,
- {
- file: file,
- forced,
- hi: false,
- language: language.code2,
- }
- );
- dispatchTask(TaskGroupName, [task], "Uploading subtitles...");
- closeModal(props.modalKey);
+ const upload = useCallback(() => {
+ if (payload === null || pending.length === 0) {
+ return;
+ }
+
+ const { radarrId } = payload;
+
+ const tasks = pending.map((v) => {
+ const { file, language, forced } = v;
+
+ return createTask(
+ file.name,
+ radarrId,
+ MoviesApi.uploadSubtitles.bind(MoviesApi),
+ radarrId,
+ {
+ file: file,
+ forced,
+ hi: false,
+ 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 (
- <BaseModal title={`Upload - ${payload?.title}`} footer={footer} {...modal}>
- <Container fluid>
- <Form>
- <Form.Group>
- <Form.Label>Language</Form.Label>
+ const columns = useMemo<Column<PendingSubtitle>[]>(
+ () => [
+ {
+ id: "name",
+ Header: "File",
+ 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
options={availableLanguages}
- value={language}
+ value={value}
onChange={(lang) => {
- if (lang) {
- setLanguage(lang);
+ if (lang && update) {
+ const newInfo = { ...row.original };
+ newInfo.language = lang;
+ update(row, newInfo);
}
}}
></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.Label>Subtitle File</Form.Label>
<FileForm
emptyText="Select..."
- onChange={(list) => {
- setFile(list[0]);
- }}
+ disabled={canUpload || availableLanguages.length === 0}
+ multiple
+ value={filelist}
+ onChange={setFiles}
></FileForm>
</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>
+ <div hidden={!canUpload}>
+ <SimpleTable
+ columns={columns}
+ data={pending}
+ responsive={false}
+ update={modify}
+ ></SimpleTable>
+ </div>
</Container>
</BaseModal>
);
diff --git a/frontend/src/components/modals/SeriesUploadModal.tsx b/frontend/src/components/modals/SeriesUploadModal.tsx
index 7de4545fc..f0efa5a50 100644
--- a/frontend/src/components/modals/SeriesUploadModal.tsx
+++ b/frontend/src/components/modals/SeriesUploadModal.tsx
@@ -14,13 +14,7 @@ import React, {
} from "react";
import { Button, Container, Form } from "react-bootstrap";
import { Column, TableUpdater } from "react-table";
-import {
- AsyncButton,
- FileForm,
- LanguageSelector,
- MessageIcon,
- SimpleTable,
-} from "..";
+import { FileForm, LanguageSelector, MessageIcon, SimpleTable } from "..";
import { dispatchTask } from "../../@modules/task";
import { createTask } from "../../@modules/task/utilites";
import { useProfileBy, useProfileItemsToLanguages } from "../../@redux/hooks";
@@ -53,8 +47,6 @@ const SeriesUploadModal: FunctionComponent<SerieProps & BaseModalProps> = ({
modal.modalKey
);
- const [uploading, setUpload] = useState(false);
-
const [pending, setPending] = useState<PendingSubtitle[]>([]);
const profile = useProfileBy(payload?.profileId);
@@ -119,7 +111,7 @@ const SeriesUploadModal: FunctionComponent<SerieProps & BaseModalProps> = ({
[checkEpisodes]
);
- const uploadSubtitles = useCallback(async () => {
+ const upload = useCallback(() => {
if (payload === null || language === null) {
return;
}
@@ -150,7 +142,9 @@ const SeriesUploadModal: FunctionComponent<SerieProps & BaseModalProps> = ({
});
dispatchTask(TaskGroupName, tasks, "Uploading subtitles...");
- }, [payload, pending, language]);
+ setFiles([]);
+ closeModal();
+ }, [payload, pending, language, closeModal, setFiles]);
const canUpload = useMemo(
() =>
@@ -234,7 +228,6 @@ const SeriesUploadModal: FunctionComponent<SerieProps & BaseModalProps> = ({
return (
<Selector
- disabled={uploading}
options={options}
value={value ?? null}
onChange={change}
@@ -249,7 +242,6 @@ const SeriesUploadModal: FunctionComponent<SerieProps & BaseModalProps> = ({
<Button
size="sm"
variant="light"
- disabled={uploading}
onClick={() => {
update && update(row);
}}
@@ -260,7 +252,7 @@ const SeriesUploadModal: FunctionComponent<SerieProps & BaseModalProps> = ({
},
},
],
- [language?.code2, episodes, uploading]
+ [language?.code2, episodes]
);
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="w-25">
<LanguageSelector
- disabled={uploading}
options={avaliableLanguages}
value={language}
onChange={(l) => {
@@ -294,7 +285,6 @@ const SeriesUploadModal: FunctionComponent<SerieProps & BaseModalProps> = ({
</div>
<div>
<Button
- hidden={uploading}
disabled={pending.length === 0}
variant="outline-secondary"
className="mr-2"
@@ -302,29 +292,15 @@ const SeriesUploadModal: FunctionComponent<SerieProps & BaseModalProps> = ({
>
Clean
</Button>
- <AsyncButton
- disabled={!canUpload}
- onChange={setUpload}
- promise={uploadSubtitles}
- onSuccess={() => {
- closeModal();
- setFiles([]);
- }}
- >
+ <Button disabled={!canUpload} onClick={upload}>
Upload
- </AsyncButton>
+ </Button>
</div>
</div>
);
return (
- <BaseModal
- size="lg"
- title="Upload Subtitles"
- closeable={!uploading}
- footer={footer}
- {...modal}
- >
+ <BaseModal size="lg" title="Upload Subtitles" footer={footer} {...modal}>
<Container fluid className="flex-column">
<Form>
<Form.Group>