mirror of
https://github.com/morpheus65535/bazarr
synced 2025-02-21 21:47:15 +00:00
Upload movie subtitles in background
This commit is contained in:
parent
9c8119df3b
commit
87123ab4c7
6 changed files with 92 additions and 65 deletions
|
@ -4,10 +4,14 @@ export function useIsAnyTaskRunning() {
|
|||
return BGT.isRunning();
|
||||
}
|
||||
|
||||
export function useIsAnyTaskRunningWithId(id: number) {
|
||||
return BGT.hasId(id);
|
||||
}
|
||||
|
||||
export function useIsGroupTaskRunning(groupName: string) {
|
||||
return BGT.has(groupName);
|
||||
}
|
||||
|
||||
export function useIsIdRunning(groupName: string, id: number) {
|
||||
export function useIsGroupTaskRunningWithId(groupName: string, id: number) {
|
||||
return BGT.find(groupName, id);
|
||||
}
|
||||
|
|
|
@ -54,6 +54,16 @@ class BackgroundTask {
|
|||
return groupName in this.groups;
|
||||
}
|
||||
|
||||
hasId(id: number) {
|
||||
for (const key in this.groups) {
|
||||
const tasks = this.groups[key];
|
||||
if (tasks.find((v) => v.id === id) !== undefined) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
isRunning() {
|
||||
return keys(this.groups).length > 0;
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ import React, { FunctionComponent, useState } from "react";
|
|||
import { Container, Row } from "react-bootstrap";
|
||||
import { Helmet } from "react-helmet";
|
||||
import { Redirect, RouteComponentProps, withRouter } from "react-router-dom";
|
||||
import { useIsGroupTaskRunningWithId } from "../../@modules/task/hooks";
|
||||
import { useMovieBy, useProfileBy } from "../../@redux/hooks";
|
||||
import { MoviesApi, ProvidersApi } from "../../apis";
|
||||
import {
|
||||
|
@ -23,6 +24,7 @@ import {
|
|||
useShowModal,
|
||||
} from "../../components";
|
||||
import { ManualSearchModal } from "../../components/modals/ManualSearchModal";
|
||||
import { TaskGroupName } from "../../components/modals/MovieUploadModal";
|
||||
import ItemOverview from "../../generic/ItemOverview";
|
||||
import { RouterEmptyPath } from "../../special-pages/404";
|
||||
import { useOnLoadedOnce } from "../../utilites";
|
||||
|
@ -57,6 +59,8 @@ const MovieDetailView: FunctionComponent<Props> = ({ match }) => {
|
|||
|
||||
const [valid, setValid] = useState(true);
|
||||
|
||||
const hasTask = useIsGroupTaskRunningWithId(TaskGroupName, id);
|
||||
|
||||
useOnLoadedOnce(() => {
|
||||
if (movie.content === null) {
|
||||
setValid(false);
|
||||
|
@ -82,6 +86,7 @@ const MovieDetailView: FunctionComponent<Props> = ({ match }) => {
|
|||
<ContentHeader.Group pos="start">
|
||||
<ContentHeader.AsyncButton
|
||||
icon={faSync}
|
||||
disabled={hasTask}
|
||||
promise={() =>
|
||||
MoviesApi.action({ action: "scan-disk", radarrid: item.radarrId })
|
||||
}
|
||||
|
@ -90,7 +95,7 @@ const MovieDetailView: FunctionComponent<Props> = ({ match }) => {
|
|||
</ContentHeader.AsyncButton>
|
||||
<ContentHeader.AsyncButton
|
||||
icon={faSearch}
|
||||
disabled={item.profileId === null}
|
||||
disabled={item.profileId === null || hasTask}
|
||||
promise={() =>
|
||||
MoviesApi.action({
|
||||
action: "search-missing",
|
||||
|
@ -102,7 +107,7 @@ const MovieDetailView: FunctionComponent<Props> = ({ match }) => {
|
|||
</ContentHeader.AsyncButton>
|
||||
<ContentHeader.Button
|
||||
icon={faUser}
|
||||
disabled={item.profileId === null}
|
||||
disabled={item.profileId === null || hasTask}
|
||||
onClick={() => showModal<Item.Movie>("manual-search", item)}
|
||||
>
|
||||
Manual
|
||||
|
@ -115,6 +120,7 @@ const MovieDetailView: FunctionComponent<Props> = ({ match }) => {
|
|||
</ContentHeader.Button>
|
||||
<ContentHeader.Button
|
||||
icon={faToolbox}
|
||||
disabled={hasTask}
|
||||
onClick={() => showModal("tools", [item])}
|
||||
>
|
||||
Tools
|
||||
|
@ -123,7 +129,7 @@ const MovieDetailView: FunctionComponent<Props> = ({ match }) => {
|
|||
|
||||
<ContentHeader.Group pos="end">
|
||||
<ContentHeader.Button
|
||||
disabled={!allowEdit || item.profileId === null}
|
||||
disabled={!allowEdit || item.profileId === null || hasTask}
|
||||
icon={faCloudUploadAlt}
|
||||
onClick={() => showModal("upload", item)}
|
||||
>
|
||||
|
@ -131,6 +137,7 @@ const MovieDetailView: FunctionComponent<Props> = ({ match }) => {
|
|||
</ContentHeader.Button>
|
||||
<ContentHeader.Button
|
||||
icon={faWrench}
|
||||
disabled={hasTask}
|
||||
onClick={() => showModal("edit", item)}
|
||||
>
|
||||
Edit Movie
|
||||
|
|
|
@ -97,12 +97,14 @@ const MovieView: FunctionComponent<Props> = () => {
|
|||
{
|
||||
accessor: "radarrId",
|
||||
selectHide: true,
|
||||
Cell: ({ row, externalUpdate }) => (
|
||||
<ActionBadge
|
||||
icon={faWrench}
|
||||
onClick={() => externalUpdate && externalUpdate(row, "edit")}
|
||||
></ActionBadge>
|
||||
),
|
||||
Cell: ({ row, value, externalUpdate }) => {
|
||||
return (
|
||||
<ActionBadge
|
||||
icon={faWrench}
|
||||
onClick={() => externalUpdate && externalUpdate(row, "edit")}
|
||||
></ActionBadge>
|
||||
);
|
||||
},
|
||||
},
|
||||
],
|
||||
[]
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import React, { FunctionComponent, useMemo, useState } from "react";
|
||||
import { Container, Form } from "react-bootstrap";
|
||||
import { AsyncButton, Selector } from "../";
|
||||
import { useIsAnyTaskRunningWithId } from "../../@modules/task/hooks";
|
||||
import { useLanguageProfiles } from "../../@redux/hooks";
|
||||
import { GetItemId } from "../../utilites";
|
||||
import BaseModal, { BaseModalProps } from "./BaseModal";
|
||||
|
@ -20,6 +21,9 @@ const Editor: FunctionComponent<Props & BaseModalProps> = (props) => {
|
|||
modal.modalKey
|
||||
);
|
||||
|
||||
// TODO: Separate movies and series
|
||||
const hasTask = useIsAnyTaskRunningWithId(payload ? GetItemId(payload) : -1);
|
||||
|
||||
const profileOptions = useMemo<SelectorOption<number>[]>(
|
||||
() =>
|
||||
profiles?.map((v) => {
|
||||
|
@ -31,31 +35,29 @@ const Editor: FunctionComponent<Props & BaseModalProps> = (props) => {
|
|||
|
||||
const [updating, setUpdating] = useState(false);
|
||||
|
||||
const footer = useMemo(
|
||||
() => (
|
||||
<AsyncButton
|
||||
noReset
|
||||
onChange={setUpdating}
|
||||
promise={() => {
|
||||
if (payload) {
|
||||
const itemId = GetItemId(payload);
|
||||
return submit({
|
||||
id: [itemId],
|
||||
profileid: [id],
|
||||
});
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}}
|
||||
onSuccess={() => {
|
||||
closeModal();
|
||||
onSuccess && payload && onSuccess(payload);
|
||||
}}
|
||||
>
|
||||
Save
|
||||
</AsyncButton>
|
||||
),
|
||||
[closeModal, id, payload, onSuccess, submit]
|
||||
const footer = (
|
||||
<AsyncButton
|
||||
noReset
|
||||
onChange={setUpdating}
|
||||
disabled={hasTask}
|
||||
promise={() => {
|
||||
if (payload) {
|
||||
const itemId = GetItemId(payload);
|
||||
return submit({
|
||||
id: [itemId],
|
||||
profileid: [id],
|
||||
});
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}}
|
||||
onSuccess={() => {
|
||||
closeModal();
|
||||
onSuccess && payload && onSuccess(payload);
|
||||
}}
|
||||
>
|
||||
Save
|
||||
</AsyncButton>
|
||||
);
|
||||
|
||||
return (
|
||||
|
@ -81,6 +83,7 @@ const Editor: FunctionComponent<Props & BaseModalProps> = (props) => {
|
|||
<Form.Label>Languages Profiles</Form.Label>
|
||||
<Selector
|
||||
clearable
|
||||
disabled={hasTask}
|
||||
options={profileOptions}
|
||||
defaultValue={payload?.profileId}
|
||||
onChange={(v) => setId(v === undefined ? null : v)}
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
import React, { FunctionComponent, useEffect, useMemo, useState } from "react";
|
||||
import { Container, Form } from "react-bootstrap";
|
||||
import { AsyncButton, FileForm, LanguageSelector } from "..";
|
||||
import { Button, Container, Form } from "react-bootstrap";
|
||||
import { FileForm, LanguageSelector } from "..";
|
||||
import BackgroundTask from "../../@modules/task";
|
||||
import { useIsGroupTaskRunning } from "../../@modules/task/hooks";
|
||||
import { createTask } from "../../@modules/task/utilites";
|
||||
import {
|
||||
useEnabledLanguages,
|
||||
useLanguageBy,
|
||||
|
@ -9,11 +12,10 @@ import {
|
|||
import { MoviesApi } from "../../apis";
|
||||
import BaseModal, { BaseModalProps } from "./BaseModal";
|
||||
import { useModalInformation } from "./hooks";
|
||||
interface MovieProps {}
|
||||
|
||||
const MovieUploadModal: FunctionComponent<MovieProps & BaseModalProps> = (
|
||||
props
|
||||
) => {
|
||||
export const TaskGroupName = "Uploading Movie Subtitles...";
|
||||
|
||||
const MovieUploadModal: FunctionComponent<BaseModalProps> = (props) => {
|
||||
const modal = props;
|
||||
|
||||
const availableLanguages = useEnabledLanguages();
|
||||
|
@ -22,8 +24,6 @@ const MovieUploadModal: FunctionComponent<MovieProps & BaseModalProps> = (
|
|||
modal.modalKey
|
||||
);
|
||||
|
||||
const [uploading, setUpload] = useState(false);
|
||||
|
||||
const [language, setLanguage] = useState<Nullable<Language.Info>>(null);
|
||||
|
||||
const profile = useProfileBy(payload?.profileId);
|
||||
|
@ -35,40 +35,41 @@ const MovieUploadModal: FunctionComponent<MovieProps & BaseModalProps> = (
|
|||
const [file, setFile] = useState<Nullable<File>>(null);
|
||||
const [forced, setForced] = useState(false);
|
||||
|
||||
const hasTask = useIsGroupTaskRunning(TaskGroupName);
|
||||
|
||||
const canUpload = useMemo(() => {
|
||||
return file !== null && language?.code2;
|
||||
}, [language, file]);
|
||||
return file !== null && language?.code2 && !hasTask;
|
||||
}, [language, file, hasTask]);
|
||||
|
||||
const footer = (
|
||||
<AsyncButton
|
||||
noReset
|
||||
<Button
|
||||
disabled={!canUpload}
|
||||
onChange={setUpload}
|
||||
promise={() => {
|
||||
onClick={() => {
|
||||
if (file && payload && language) {
|
||||
return MoviesApi.uploadSubtitles(payload.radarrId, {
|
||||
file: file,
|
||||
forced,
|
||||
hi: false,
|
||||
language: language.code2,
|
||||
});
|
||||
} else {
|
||||
return null;
|
||||
const id = payload.radarrId;
|
||||
const task = createTask(
|
||||
file.name,
|
||||
id,
|
||||
MoviesApi.uploadSubtitles.bind(MoviesApi),
|
||||
id,
|
||||
{
|
||||
file: file,
|
||||
forced,
|
||||
hi: false,
|
||||
language: language.code2,
|
||||
}
|
||||
);
|
||||
BackgroundTask.dispatch(TaskGroupName, [task]);
|
||||
closeModal(props.modalKey);
|
||||
}
|
||||
}}
|
||||
onSuccess={() => closeModal()}
|
||||
>
|
||||
Upload
|
||||
</AsyncButton>
|
||||
</Button>
|
||||
);
|
||||
|
||||
return (
|
||||
<BaseModal
|
||||
title={`Upload - ${payload?.title}`}
|
||||
closeable={!uploading}
|
||||
footer={footer}
|
||||
{...modal}
|
||||
>
|
||||
<BaseModal title={`Upload - ${payload?.title}`} footer={footer} {...modal}>
|
||||
<Container fluid>
|
||||
<Form>
|
||||
<Form.Group>
|
||||
|
|
Loading…
Reference in a new issue