feat: add usenet support (#543)

* feat(autobrr): implement usenet support

* feat(sonarr): implement usenet support

* feat(radarr): implement usenet support

* feat(announce): implement usenet support

* announce: cast a line

* feat(release): prevent unknown protocol transfer

* release: lines for days.

* feat: add newznab and sabnzbd support

* feat: add category to sabnzbd

* feat(newznab): map categories

* feat(newznab): map categories

---------

Co-authored-by: ze0s <43699394+zze0s@users.noreply.github.com>
Co-authored-by: ze0s <ze0s@riseup.net>
This commit is contained in:
Kyle Sanderson 2023-03-04 11:27:18 -08:00 committed by GitHub
parent b2d93d50c5
commit 13a74f7cc8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
29 changed files with 1588 additions and 37 deletions

View file

@ -240,22 +240,71 @@ function FormFieldsTransmission() {
);
}
function FormFieldsSabnzbd() {
const {
values: { port, tls, settings }
} = useFormikContext<InitialValues>();
return (
<div className="flex flex-col space-y-4 px-1 py-6 sm:py-0 sm:space-y-0">
<TextFieldWide
name="host"
label="Host"
help="Eg. ip:port"
// tooltip={<div><p>See guides for how to connect to qBittorrent for various server types in our docs.</p><br /><p>Dedicated servers:</p><a href='https://autobrr.com/configuration/download-clients/dedicated#qbittorrent' className='text-blue-400 visited:text-blue-400' target='_blank'>https://autobrr.com/configuration/download-clients/dedicated#qbittorrent</a><p>Shared seedbox providers:</p><a href='https://autobrr.com/configuration/download-clients/shared-seedboxes#qbittorrent' className='text-blue-400 visited:text-blue-400' target='_blank'>https://autobrr.com/configuration/download-clients/shared-seedboxes#qbittorrent</a></div>}
/>
{port > 0 && (
<NumberFieldWide
name="port"
label="Port"
help="port for Sabnzbd"
/>
)}
<SwitchGroupWide name="tls" label="TLS" />
{tls && (
<SwitchGroupWide
name="tls_skip_verify"
label="Skip TLS verification (insecure)"
/>
)}
{/*<TextFieldWide name="username" label="Username" />*/}
{/*<PasswordFieldWide name="password" label="Password" />*/}
<PasswordFieldWide name="settings.apikey" label="API key" />
<SwitchGroupWide name="settings.basic.auth" label="Basic auth" />
{settings.basic?.auth === true && (
<>
<TextFieldWide name="settings.basic.username" label="Username" />
<PasswordFieldWide name="settings.basic.password" label="Password" />
</>
)}
</div>
);
}
export interface componentMapType {
[key: string]: React.ReactElement;
}
export const componentMap: componentMapType = {
DELUGE_V1: <FormFieldsDeluge/>,
DELUGE_V2: <FormFieldsDeluge/>,
QBITTORRENT: <FormFieldsQbit/>,
DELUGE_V1: <FormFieldsDeluge />,
DELUGE_V2: <FormFieldsDeluge />,
QBITTORRENT: <FormFieldsQbit />,
RTORRENT: <FormFieldsRTorrent />,
TRANSMISSION: <FormFieldsTransmission/>,
TRANSMISSION: <FormFieldsTransmission />,
PORLA: <FormFieldsPorla />,
RADARR: <FormFieldsArr/>,
SONARR: <FormFieldsArr/>,
LIDARR: <FormFieldsArr/>,
WHISPARR: <FormFieldsArr/>,
READARR: <FormFieldsArr/>
RADARR: <FormFieldsArr />,
SONARR: <FormFieldsArr />,
LIDARR: <FormFieldsArr />,
WHISPARR: <FormFieldsArr />,
READARR: <FormFieldsArr />,
SABNZBD: <FormFieldsSabnzbd />
};
function FormFieldsRulesBasic() {

View file

@ -203,6 +203,30 @@ function FormFieldsTorznab() {
);
}
function FormFieldsNewznab() {
const {
values: { interval }
} = useFormikContext<InitialValues>();
return (
<div className="border-t border-gray-200 dark:border-gray-700 py-5">
<TextFieldWide
name="url"
label="URL"
help="Newznab url"
/>
<PasswordFieldWide name="api_key" label="API key" />
{interval < 15 && <WarningLabel />}
<NumberFieldWide name="interval" label="Refresh interval" help="Minutes. Recommended 15-30. Too low and risk ban."/>
<NumberFieldWide name="timeout" label="Refresh timeout" help="Seconds to wait before cancelling refresh."/>
<NumberFieldWide name="max_age" label="Max age" help="Seconds. Will not grab older than this value."/>
</div>
);
}
function FormFieldsRSS() {
const {
values: { interval }
@ -230,5 +254,6 @@ function FormFieldsRSS() {
const componentMap: componentMapType = {
TORZNAB: <FormFieldsTorznab />,
NEWZNAB: <FormFieldsNewznab />,
RSS: <FormFieldsRSS />
};

View file

@ -100,7 +100,7 @@ const IrcSettingFields = (ind: IndexerDefinition, indexer: string) => {
}
};
const FeedSettingFields = (ind: IndexerDefinition, indexer: string) => {
const TorznabFeedSettingFields = (ind: IndexerDefinition, indexer: string) => {
if (indexer !== "") {
return (
<Fragment>
@ -139,6 +139,37 @@ const FeedSettingFields = (ind: IndexerDefinition, indexer: string) => {
}
};
const NewznabFeedSettingFields = (ind: IndexerDefinition, indexer: string) => {
if (indexer !== "") {
return (
<Fragment>
{ind && ind.newznab && ind.newznab.settings && (
<div className="">
<div className="px-4 space-y-1">
<Dialog.Title className="text-lg font-medium text-gray-900 dark:text-white">Newznab</Dialog.Title>
<p className="text-sm text-gray-500 dark:text-gray-200">
Newznab feed
</p>
</div>
<TextFieldWide name="name" label="Name" defaultValue="" />
{ind.newznab.settings.map((f: IndexerSetting, idx: number) => {
switch (f.type) {
case "text":
return <TextFieldWide name={`feed.${f.name}`} label={f.label} required={f.required} key={idx} help={f.help} validate={validateField(f)} />;
case "secret":
return <PasswordFieldWide name={`feed.${f.name}`} label={f.label} required={f.required} key={idx} help={f.help} defaultValue={f.default} validate={validateField(f)} />;
}
return null;
})}
</div>
)}
</Fragment>
);
}
};
const RSSFeedSettingFields = (ind: IndexerDefinition, indexer: string) => {
if (indexer !== "") {
return (
@ -274,6 +305,31 @@ export function IndexerAddForm({ isOpen, toggle }: AddProps) {
});
return;
} else if (formData.implementation === "newznab") {
formData.url = formData.feed.url;
const createFeed: FeedCreate = {
name: formData.name,
enabled: false,
type: "NEWZNAB",
url: formData.feed.newznab_url,
api_key: formData.feed.api_key,
interval: 30,
timeout: 60,
indexer_id: 0,
settings: formData.feed.settings
};
mutation.mutate(formData as Indexer, {
onSuccess: (indexer) => {
// @eslint-ignore
createFeed.indexer_id = indexer.id;
feedMutation.mutate(createFeed);
}
});
return;
} else if (formData.implementation === "rss") {
const createFeed: FeedCreate = {
name: formData.name,
@ -482,7 +538,8 @@ export function IndexerAddForm({ isOpen, toggle }: AddProps) {
</div>
{IrcSettingFields(indexer, values.identifier)}
{FeedSettingFields(indexer, values.identifier)}
{TorznabFeedSettingFields(indexer, values.identifier)}
{NewznabFeedSettingFields(indexer, values.identifier)}
{RSSFeedSettingFields(indexer, values.identifier)}
</div>