diff options
author | LASER-Yi <[email protected]> | 2021-08-22 21:59:48 +0800 |
---|---|---|
committer | LASER-Yi <[email protected]> | 2021-08-22 21:59:48 +0800 |
commit | a5ecd84605cd46e2feab87b6275879a3962d76fb (patch) | |
tree | 6c57f0af1687fe0b1c152d4270819351c722fa8f /frontend | |
parent | f05daa8223d7b95e2f1c95d11a88558111ff512b (diff) | |
download | bazarr-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.tsx | 224 | ||||
-rw-r--r-- | frontend/src/components/modals/SeriesUploadModal.tsx | 42 |
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> |