Feature: Download client rules (#18)

* feat(web): add and update download client rules

* feat: add and update download client rules

* feat: add active downloads check

* chore: update pkg

* feat: deluge max active downloads

* feat: use basic rules for deluge

* feat: add as paused

* refactor: download file if needed

* feat: better errors qbit
This commit is contained in:
Ludvig Lundgren 2021-09-10 16:54:30 +02:00 committed by GitHub
parent 09eb0b1716
commit c02f16b64d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
25 changed files with 628 additions and 228 deletions

View file

@ -10,7 +10,7 @@ import {
import { useToggle } from "../hooks/hooks";
import { useMutation } from "react-query";
import { Field, Form } from "react-final-form";
import { TextField } from "./inputs";
import { SwitchGroup, TextField } from "./inputs";
import { NumberField, SelectField } from "./inputs/compact";
import DEBUG from "./debug";
import APIClient from "../api/APIClient";
@ -281,6 +281,12 @@ function ListItem({ action, clients, filterID, idx }: ListItemProps) {
label="Limit upload speed (KB/s)"
/>
</div>
<div className="mt-6 grid grid-cols-12 gap-6">
<div className="col-span-6">
<SwitchGroup name="paused" label="Add paused" />
</div>
</div>
</div>
);
case "DELUGE_V1":
@ -313,6 +319,12 @@ function ListItem({ action, clients, filterID, idx }: ListItemProps) {
label="Limit upload speed (KB/s)"
/>
</div>
<div className="mt-6 grid grid-cols-12 gap-6">
<div className="col-span-6">
<SwitchGroup name="paused" label="Add paused" />
</div>
</div>
</div>
);
case "RADARR":

View file

@ -6,6 +6,7 @@ import { classNames } from "../../../styles/utils";
interface Props {
name: string;
label?: string;
help?: string;
placeholder?: string;
defaultValue?: number;
className?: string;
@ -17,6 +18,7 @@ const NumberFieldWide: React.FC<Props> = ({
name,
label,
placeholder,
help,
defaultValue,
required,
hidden,
@ -51,6 +53,9 @@ const NumberFieldWide: React.FC<Props> = ({
/>
)}
/>
{help && (
<p className="mt-2 text-sm text-gray-500" id={`${name}-description`}>{help}</p>
)}
<Error name={name} classNames="block text-red-500 mt-2" />
</div>
</div>

View file

@ -10,7 +10,7 @@ import { Field, Form } from "react-final-form";
import DEBUG from "../../components/debug";
import APIClient from "../../api/APIClient";
import { ActionTypeOptions } from "../../domain/constants";
import { TextFieldWide } from "../../components/inputs";
import { SwitchGroup, TextFieldWide } from "../../components/inputs";
import { AlertWarning } from "../../components/alerts";
import {
NumberFieldWide,
@ -206,6 +206,10 @@ function FilterActionAddForm({ filter, isOpen, toggle, clients }: props) {
/>
<TextFieldWide name="save_path" label="Save path" />
<div className="py-6 px-6 space-y-6 sm:py-0 sm:space-y-0 sm:divide-y sm:divide-gray-200">
<SwitchGroup name="paused" label="Add paused" />
</div>
<div className="divide-y divide-gray-200 pt-8 space-y-6 sm:pt-10 sm:space-y-5">
<div className="px-4">
<h3 className="text-lg leading-6 font-medium text-gray-900">
@ -240,6 +244,10 @@ function FilterActionAddForm({ filter, isOpen, toggle, clients }: props) {
<TextFieldWide name="label" label="Label" />
<TextFieldWide name="save_path" label="Save path" />
<div className="py-6 px-6 space-y-6 sm:py-0 sm:space-y-0 sm:divide-y sm:divide-gray-200">
<SwitchGroup name="paused" label="Add paused" />
</div>
<div className="divide-y divide-gray-200 pt-8 space-y-6 sm:pt-10 sm:space-y-5">
<div className="px-4">
<h3 className="text-lg leading-6 font-medium text-gray-900">

View file

@ -10,7 +10,7 @@ import DEBUG from "../../components/debug";
import APIClient from "../../api/APIClient";
import { ActionTypeOptions } from "../../domain/constants";
import { AlertWarning } from "../../components/alerts";
import { TextFieldWide } from "../../components/inputs";
import { SwitchGroup, TextFieldWide } from "../../components/inputs";
import {
NumberFieldWide,
RadioFieldsetWide,
@ -133,6 +133,9 @@ function FilterActionUpdateForm({
label="Limit upload speed"
/>
</div>
<div className="col-span-6">
<SwitchGroup name="paused" label="Add paused" />
</div>
</div>
);
case "DELUGE_V1":

View file

@ -1,4 +1,4 @@
import React, { Fragment, useState } from "react";
import { Fragment, useState } from "react";
import { useMutation } from "react-query";
import {
DOWNLOAD_CLIENT_TYPES,
@ -15,7 +15,7 @@ import APIClient from "../../../api/APIClient";
import { sleep } from "../../../utils/utils";
import { DownloadClientTypeOptions } from "../../../domain/constants";
import { RadioFieldsetWide } from "../../../components/inputs/wide";
import { componentMap } from "./shared";
import { componentMap, rulesComponentMap } from "./shared";
import { toast } from 'react-hot-toast'
import Toast from '../../../components/notifications/Toast';
@ -164,6 +164,8 @@ function DownloadClientAddForm({ isOpen, toggle }: any) {
<div>{componentMap[values.type]}</div>
</div>
</div>
{rulesComponentMap[values.type]}
<div className="flex-shrink-0 px-4 border-t border-gray-200 py-5 sm:px-6">
<div className="space-x-3 flex justify-end">

View file

@ -12,7 +12,7 @@ import { SwitchGroup, TextFieldWide } from "../../../components/inputs";
import { DownloadClientTypeOptions } from "../../../domain/constants";
import APIClient from "../../../api/APIClient";
import { sleep } from "../../../utils/utils";
import { componentMap } from "./shared";
import { componentMap, rulesComponentMap } from "./shared";
import { RadioFieldsetWide } from "../../../components/inputs/wide";
import { DeleteModal } from "../../../components/modals";
@ -190,6 +190,8 @@ function DownloadClientUpdateForm({ client, isOpen, toggle }: any) {
</div>
</div>
{rulesComponentMap[values.type]}
<div className="flex-shrink-0 px-4 border-t border-gray-200 py-5 sm:px-6">
<div className="space-x-3 flex justify-between">
<button

View file

@ -2,6 +2,7 @@ import { Fragment } from "react";
import { SwitchGroup, TextFieldWide } from "../../../components/inputs";
import { NumberFieldWide, PasswordFieldWide } from "../../../components/inputs/wide";
import { useField } from "react-final-form";
import { Dialog } from "@headlessui/react";
function FormFieldsDefault() {
return (
@ -50,3 +51,72 @@ export const componentMap: any = {
SONARR: <FormFieldsArr />,
LIDARR: <FormFieldsArr />,
};
function FormFieldsRulesBasic() {
const { input: enabled } = useField("settings.rules.enabled");
return (
<div className="border-t border-gray-200 py-5">
<div className="px-6 space-y-1">
<Dialog.Title className="text-lg font-medium text-gray-900">Rules</Dialog.Title>
<p className="text-sm text-gray-500">
Manage max downloads.
</p>
</div>
<div className="py-6 px-6 space-y-6 sm:py-0 sm:space-y-0 sm:divide-y sm:divide-gray-200">
<SwitchGroup name="settings.rules.enabled" label="Enabled" />
</div>
{enabled.value === true && (
<Fragment>
<NumberFieldWide name="settings.rules.max_active_downloads" label="Max active downloads" />
</Fragment>
)}
</div>
);
}
function FormFieldsRules() {
const { input } = useField("settings.rules.ignore_slow_torrents");
const { input: enabled } = useField("settings.rules.enabled");
return (
<div className="border-t border-gray-200 py-5">
<div className="px-6 space-y-1">
<Dialog.Title className="text-lg font-medium text-gray-900">Rules</Dialog.Title>
<p className="text-sm text-gray-500">
Manage max downloads etc.
</p>
</div>
<div className="py-6 px-6 space-y-6 sm:py-0 sm:space-y-0 sm:divide-y sm:divide-gray-200">
<SwitchGroup name="settings.rules.enabled" label="Enabled" />
</div>
{enabled.value === true && (
<Fragment>
<NumberFieldWide name="settings.rules.max_active_downloads" label="Max active downloads" />
<div className="py-6 px-6 space-y-6 sm:py-0 sm:space-y-0 sm:divide-y sm:divide-gray-200">
<SwitchGroup name="settings.rules.ignore_slow_torrents" label="Ignore slow torrents" />
</div>
{input.value === true && (
<Fragment>
<NumberFieldWide name="settings.rules.download_speed_threshold" label="Download speed threshold" placeholder="in KB/s" help="If download speed is below this when max active downloads is hit, download anyways. KB/s" />
</Fragment>
)}
</Fragment>
)}
</div>
);
}
export const rulesComponentMap: any = {
DELUGE_V1: <FormFieldsRulesBasic />,
DELUGE_V2: <FormFieldsRulesBasic />,
QBITTORRENT: <FormFieldsRules />,
};

View file

@ -42,7 +42,7 @@ function DownloadClientSettingsListItem({ client, idx }: DownloadLClientSettings
</Switch>
</td>
<td className="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">{client.name}</td>
<td className="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">{client.host}</td>
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">{client.host}</td>
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">{DownloadClientTypeNameMap[client.type]}</td>
<td className="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
<span className="text-indigo-600 hover:text-indigo-900 cursor-pointer" onClick={toggleUpdateClient}>