Compare commits

..

3 Commits

Author SHA1 Message Date
Robin Dadswell 0150988bcd Added Indexer Factory 2021-06-04 23:08:02 +01:00
Robin Dadswell 593231c3af Updated config definition to match Jacketts config definition 2021-06-04 23:07:40 +01:00
Robin Dadswell dbbb6bf0d1 Starts of Jackett Migrations
request extension

start migration controller/resource

removed unneccesary using - probalby more to go still

more scaffolding

added more framework; contemplating using the importlists from other arrs as a base for this

Revert "jackett config"

This reverts commit 6523eaf55450ceed84b3667421595a9d9e34dc51.

added stuff from nit's radarr pr

neated code up a little bit, more to do on this though

get config sorted, api logic also added - migration todo and indexer config to do
2021-06-03 11:07:56 +01:00
101 changed files with 502 additions and 906 deletions
+2 -2
View File
@@ -2,7 +2,7 @@
[![Build Status](https://dev.azure.com/Prowlarr/Prowlarr/_apis/build/status/Prowlarr.Prowlarr?branchName=develop)](https://dev.azure.com/Prowlarr/Prowlarr/_build/latest?definitionId=1&branchName=develop) [![Build Status](https://dev.azure.com/Prowlarr/Prowlarr/_apis/build/status/Prowlarr.Prowlarr?branchName=develop)](https://dev.azure.com/Prowlarr/Prowlarr/_build/latest?definitionId=1&branchName=develop)
[![Translated](https://translate.servarr.com/widgets/servarr/-/prowlarr/svg-badge.svg)](https://translate.servarr.com/engage/prowlarr/?utm_source=widget) [![Translated](https://translate.servarr.com/widgets/servarr/-/prowlarr/svg-badge.svg)](https://translate.servarr.com/engage/prowlarr/?utm_source=widget)
[![Docker Pulls](https://img.shields.io/docker/pulls/hotio/prowlarr.svg)](https://wikijs.servarr.com/prowlarr/installation#docker) [![Docker Pulls](https://img.shields.io/docker/pulls/hotio/prowlarr.svg)](https://wiki.servarr.com/Prowlarr_Installation#Docker)
![Github Downloads](https://img.shields.io/github/downloads/Prowlarr/Prowlarr/total.svg) ![Github Downloads](https://img.shields.io/github/downloads/Prowlarr/Prowlarr/total.svg)
[![Backers on Open Collective](https://opencollective.com/Prowlarr/backers/badge.svg)](#backers) [![Backers on Open Collective](https://opencollective.com/Prowlarr/backers/badge.svg)](#backers)
[![Sponsors on Open Collective](https://opencollective.com/Prowlarr/sponsors/badge.svg)](#sponsors) [![Sponsors on Open Collective](https://opencollective.com/Prowlarr/sponsors/badge.svg)](#sponsors)
@@ -23,7 +23,7 @@ Note: Prowlarr is currently early in life, thus bugs should be expected
[![Discord](https://img.shields.io/badge/discord-chat-7289DA.svg?maxAge=60)](https://prowlarr.com/discord) [![Discord](https://img.shields.io/badge/discord-chat-7289DA.svg?maxAge=60)](https://prowlarr.com/discord)
[![Reddit](https://img.shields.io/badge/reddit-discussion-FF4500.svg?maxAge=60)](https://www.reddit.com/r/Prowlarr) [![Reddit](https://img.shields.io/badge/reddit-discussion-FF4500.svg?maxAge=60)](https://www.reddit.com/r/Prowlarr)
[![GitHub - Bugs and Feature Requests Only](https://img.shields.io/badge/github-issues-red.svg?maxAge=60)](https://github.com/Prowlarr/Prowlarr/issues) [![GitHub - Bugs and Feature Requests Only](https://img.shields.io/badge/github-issues-red.svg?maxAge=60)](https://github.com/Prowlarr/Prowlarr/issues)
[![Wiki](https://img.shields.io/badge/servarr-wiki-181717.svg?maxAge=60)](https://wikijs.servarr.com/prowlarr) [![Wiki](https://img.shields.io/badge/servarr-wiki-181717.svg?maxAge=60)](https://wiki.servarr.com/Prowlarr)
## Feature Requests ## Feature Requests
@@ -129,7 +129,7 @@ class FileBrowserModalContent extends Component {
className={styles.mappedDrivesWarning} className={styles.mappedDrivesWarning}
kind={kinds.WARNING} kind={kinds.WARNING}
> >
<Link to="https://wikijs.servarr.com/prowlarr/faq#why-cant-prowlarr-see-my-files-on-a-remote-server"> <Link to="https://wiki.servarr.com/Prowlarr_FAQ#Why_cant_Prowlarr_see_my_files_on_a_remote_server">
{translate('MappedDrivesRunningAsService')} {translate('MappedDrivesRunningAsService')}
</Link> </Link>
</Alert> </Alert>
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

@@ -99,7 +99,7 @@ function EditIndexerModalContent(props) {
<FormInputGroup <FormInputGroup
type={inputTypes.CHECK} type={inputTypes.CHECK}
name="redirect" name="redirect"
helpText={translate('RedirectHelpText')} helpText={'Redirect incoming download requests for indexer instead of Proxying using Prowlarr'}
isDisabled={!supportsRedirect.value} isDisabled={!supportsRedirect.value}
{...redirect} {...redirect}
onChange={onInputChange} onChange={onInputChange}
@@ -113,7 +113,6 @@ function EditIndexerModalContent(props) {
type={inputTypes.APP_PROFILE_SELECT} type={inputTypes.APP_PROFILE_SELECT}
name="appProfileId" name="appProfileId"
{...appProfileId} {...appProfileId}
helpText={translate('AppProfileSelectHelpText')}
onChange={onInputChange} onChange={onInputChange}
/> />
</FormGroup> </FormGroup>
@@ -55,7 +55,7 @@ function UpdateSettings(props) {
type={inputTypes.TEXT} type={inputTypes.TEXT}
name="branch" name="branch"
helpText={usingExternalUpdateMechanism ? translate('BranchUpdateMechanism') : translate('BranchUpdate')} helpText={usingExternalUpdateMechanism ? translate('BranchUpdateMechanism') : translate('BranchUpdate')}
helpLink="https://wikijs.servarr.com/prowlarr/settings#updates" helpLink="https://wiki.servarr.com/Prowlarr_Settings#Updates"
{...branch} {...branch}
onChange={onInputChange} onChange={onInputChange}
readOnly={usingExternalUpdateMechanism} readOnly={usingExternalUpdateMechanism}
@@ -92,7 +92,7 @@ function UpdateSettings(props) {
name="updateMechanism" name="updateMechanism"
values={updateOptions} values={updateOptions}
helpText={translate('UpdateMechanismHelpText')} helpText={translate('UpdateMechanismHelpText')}
helpLink="https://wikijs.servarr.com/prowlarr/settings#updates" helpLink="https://wiki.servarr.com/Prowlarr_Settings#Updates"
onChange={onInputChange} onChange={onInputChange}
{...updateMechanism} {...updateMechanism}
/> />
@@ -26,7 +26,7 @@ function NotificationEventItems(props) {
<div> <div>
<FormInputHelpText <FormInputHelpText
text={translate('NotifcationTriggersHelpText')} text={translate('NotifcationTriggersHelpText')}
link="https://wikijs.servarr.com/prowlarr/settings#connections" link="https://wiki.servarr.com/Prowlarr_Settings#Connections"
/> />
<div className={styles.events}> <div className={styles.events}>
<div> <div>
@@ -13,7 +13,7 @@ function createHealthCheckSelector() {
source: 'UI', source: 'UI',
type: 'warning', type: 'warning',
message: translate('CouldNotConnectSignalR'), message: translate('CouldNotConnectSignalR'),
wikiUrl: 'https://wikijs.servarr.com/prowlarr/system#could-not-connect-to-signalr' wikiUrl: 'https://wiki.servarr.com/Prowlarr_System#Could_not_connect_to_signalR'
}); });
} }
@@ -1,64 +0,0 @@
import React, { Component } from 'react';
import FieldSet from 'Components/FieldSet';
import Link from 'Components/Link/Link';
import translate from 'Utilities/String/translate';
import styles from '../styles.css';
class Donations extends Component {
//
// Render
render() {
return (
<FieldSet legend={translate('Donations')}>
<div className={styles.logoContainer} title="Radarr">
<Link to="https://opencollective.com/radarr">
<img
className={styles.logo}
src={`${window.Prowlarr.urlBase}/Content/Images/Icons/logo-radarr.png`}
/>
</Link>
</div>
<div className={styles.logoContainer} title="Lidarr">
<Link to="https://opencollective.com/lidarr">
<img
className={styles.logo}
src={`${window.Prowlarr.urlBase}/Content/Images/Icons/logo-lidarr.png`}
/>
</Link>
</div>
<div className={styles.logoContainer} title="Readarr">
<Link to="https://opencollective.com/readarr">
<img
className={styles.logo}
src={`${window.Prowlarr.urlBase}/Content/Images/Icons/logo-readarr.png`}
/>
</Link>
</div>
<div className={styles.logoContainer} title="Prowlarr">
<Link to="https://opencollective.com/prowlarr">
<img
className={styles.logo}
src={`${window.Prowlarr.urlBase}/Content/Images/Icons/logo-prowlarr.png`}
/>
</Link>
</div>
<div className={styles.logoContainer} title="Sonarr">
<Link to="https://opencollective.com/sonarr">
<img
className={styles.logo}
src={`${window.Prowlarr.urlBase}/Content/Images/Icons/logo-sonarr.png`}
/>
</Link>
</div>
</FieldSet>
);
}
}
Donations.propTypes = {
};
export default Donations;
@@ -27,7 +27,7 @@ class MoreInfo extends Component {
<DescriptionListItemTitle>Wiki</DescriptionListItemTitle> <DescriptionListItemTitle>Wiki</DescriptionListItemTitle>
<DescriptionListItemDescription> <DescriptionListItemDescription>
<Link to="https://wikijs.servarr.com/prowlarr">wikijs.servarr.com/prowlarr</Link> <Link to="https://wiki.servarr.com/Prowlarr">wiki.servarr.com/Prowlarr</Link>
</DescriptionListItemDescription> </DescriptionListItemDescription>
<DescriptionListItemTitle>Donations</DescriptionListItemTitle> <DescriptionListItemTitle>Donations</DescriptionListItemTitle>
-2
View File
@@ -3,7 +3,6 @@ import PageContent from 'Components/Page/PageContent';
import PageContentBody from 'Components/Page/PageContentBody'; import PageContentBody from 'Components/Page/PageContentBody';
import translate from 'Utilities/String/translate'; import translate from 'Utilities/String/translate';
import AboutConnector from './About/AboutConnector'; import AboutConnector from './About/AboutConnector';
import Donations from './Donations/Donations';
import HealthConnector from './Health/HealthConnector'; import HealthConnector from './Health/HealthConnector';
import MoreInfo from './MoreInfo/MoreInfo'; import MoreInfo from './MoreInfo/MoreInfo';
@@ -19,7 +18,6 @@ class Status extends Component {
<HealthConnector /> <HealthConnector />
<AboutConnector /> <AboutConnector />
<MoreInfo /> <MoreInfo />
<Donations />
</PageContentBody> </PageContentBody>
</PageContent> </PageContent>
); );
-17
View File
@@ -1,17 +0,0 @@
.logo {
margin: auto;
padding: 9px;
}
.logoContainer {
display: inline-block;
margin: 1em;
width: 50px;
height: 50px;
outline: none;
border: solid 1px #e6e6e6;
border-radius: 0.5em;
background: #f8f8ff;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
cursor: pointer;
}
+1 -1
View File
@@ -252,7 +252,7 @@
</span> </span>
<a <a
href="https://wikijs.servarr.com/prowlarr/faq#help-i-have-locked-myself-out" href="https://wiki.servarr.com/Prowlarr_FAQ#Help_I_have_locked_my_self_out_of_Prowlarr_and_do_not_know_the_password"
class="forgot-password" class="forgot-password"
>Forgot your password?</a >Forgot your password?</a
> >
@@ -1,227 +0,0 @@
using System;
using System.Collections.Generic;
using FizzWare.NBuilder;
using FluentAssertions;
using Moq;
using NUnit.Framework;
using NzbDrone.Core.Download;
using NzbDrone.Core.Indexers;
using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test.Download
{
[TestFixture]
public class DownloadClientProviderFixture : CoreTest<DownloadClientProvider>
{
private List<IDownloadClient> _downloadClients;
private List<DownloadClientStatus> _blockedProviders;
private int _nextId;
[SetUp]
public void SetUp()
{
_downloadClients = new List<IDownloadClient>();
_blockedProviders = new List<DownloadClientStatus>();
_nextId = 1;
Mocker.GetMock<IDownloadClientFactory>()
.Setup(v => v.GetAvailableProviders())
.Returns(_downloadClients);
Mocker.GetMock<IDownloadClientStatusService>()
.Setup(v => v.GetBlockedProviders())
.Returns(_blockedProviders);
}
private Mock<IDownloadClient> WithUsenetClient(int priority = 0)
{
var mock = new Mock<IDownloadClient>(MockBehavior.Default);
mock.SetupGet(s => s.Definition)
.Returns(Builder<DownloadClientDefinition>
.CreateNew()
.With(v => v.Id = _nextId++)
.With(v => v.Priority = priority)
.Build());
_downloadClients.Add(mock.Object);
mock.SetupGet(v => v.Protocol).Returns(DownloadProtocol.Usenet);
return mock;
}
private Mock<IDownloadClient> WithTorrentClient(int priority = 0)
{
var mock = new Mock<IDownloadClient>(MockBehavior.Default);
mock.SetupGet(s => s.Definition)
.Returns(Builder<DownloadClientDefinition>
.CreateNew()
.With(v => v.Id = _nextId++)
.With(v => v.Priority = priority)
.Build());
_downloadClients.Add(mock.Object);
mock.SetupGet(v => v.Protocol).Returns(DownloadProtocol.Torrent);
return mock;
}
private void GivenBlockedClient(int id)
{
_blockedProviders.Add(new DownloadClientStatus
{
ProviderId = id,
DisabledTill = DateTime.UtcNow.AddHours(3)
});
}
[Test]
public void should_roundrobin_over_usenet_client()
{
WithUsenetClient();
WithUsenetClient();
WithUsenetClient();
WithTorrentClient();
var client1 = Subject.GetDownloadClient(DownloadProtocol.Usenet);
var client2 = Subject.GetDownloadClient(DownloadProtocol.Usenet);
var client3 = Subject.GetDownloadClient(DownloadProtocol.Usenet);
var client4 = Subject.GetDownloadClient(DownloadProtocol.Usenet);
var client5 = Subject.GetDownloadClient(DownloadProtocol.Usenet);
client1.Definition.Id.Should().Be(1);
client2.Definition.Id.Should().Be(2);
client3.Definition.Id.Should().Be(3);
client4.Definition.Id.Should().Be(1);
client5.Definition.Id.Should().Be(2);
}
[Test]
public void should_roundrobin_over_torrent_client()
{
WithUsenetClient();
WithTorrentClient();
WithTorrentClient();
WithTorrentClient();
var client1 = Subject.GetDownloadClient(DownloadProtocol.Torrent);
var client2 = Subject.GetDownloadClient(DownloadProtocol.Torrent);
var client3 = Subject.GetDownloadClient(DownloadProtocol.Torrent);
var client4 = Subject.GetDownloadClient(DownloadProtocol.Torrent);
var client5 = Subject.GetDownloadClient(DownloadProtocol.Torrent);
client1.Definition.Id.Should().Be(2);
client2.Definition.Id.Should().Be(3);
client3.Definition.Id.Should().Be(4);
client4.Definition.Id.Should().Be(2);
client5.Definition.Id.Should().Be(3);
}
[Test]
public void should_roundrobin_over_protocol_separately()
{
WithUsenetClient();
WithTorrentClient();
WithTorrentClient();
var client1 = Subject.GetDownloadClient(DownloadProtocol.Usenet);
var client2 = Subject.GetDownloadClient(DownloadProtocol.Torrent);
var client3 = Subject.GetDownloadClient(DownloadProtocol.Torrent);
var client4 = Subject.GetDownloadClient(DownloadProtocol.Torrent);
client1.Definition.Id.Should().Be(1);
client2.Definition.Id.Should().Be(2);
client3.Definition.Id.Should().Be(3);
client4.Definition.Id.Should().Be(2);
}
[Test]
public void should_skip_blocked_torrent_client()
{
WithUsenetClient();
WithTorrentClient();
WithTorrentClient();
WithTorrentClient();
GivenBlockedClient(3);
var client1 = Subject.GetDownloadClient(DownloadProtocol.Torrent);
var client2 = Subject.GetDownloadClient(DownloadProtocol.Torrent);
var client3 = Subject.GetDownloadClient(DownloadProtocol.Torrent);
var client4 = Subject.GetDownloadClient(DownloadProtocol.Torrent);
var client5 = Subject.GetDownloadClient(DownloadProtocol.Torrent);
client1.Definition.Id.Should().Be(2);
client2.Definition.Id.Should().Be(4);
client3.Definition.Id.Should().Be(2);
client4.Definition.Id.Should().Be(4);
}
[Test]
public void should_not_skip_blocked_torrent_client_if_all_blocked()
{
WithUsenetClient();
WithTorrentClient();
WithTorrentClient();
WithTorrentClient();
GivenBlockedClient(2);
GivenBlockedClient(3);
GivenBlockedClient(4);
var client1 = Subject.GetDownloadClient(DownloadProtocol.Torrent);
var client2 = Subject.GetDownloadClient(DownloadProtocol.Torrent);
var client3 = Subject.GetDownloadClient(DownloadProtocol.Torrent);
var client4 = Subject.GetDownloadClient(DownloadProtocol.Torrent);
var client5 = Subject.GetDownloadClient(DownloadProtocol.Torrent);
client1.Definition.Id.Should().Be(2);
client2.Definition.Id.Should().Be(3);
client3.Definition.Id.Should().Be(4);
client4.Definition.Id.Should().Be(2);
}
[Test]
public void should_skip_secondary_prio_torrent_client()
{
WithUsenetClient();
WithTorrentClient(2);
WithTorrentClient();
WithTorrentClient();
var client1 = Subject.GetDownloadClient(DownloadProtocol.Torrent);
var client2 = Subject.GetDownloadClient(DownloadProtocol.Torrent);
var client3 = Subject.GetDownloadClient(DownloadProtocol.Torrent);
var client4 = Subject.GetDownloadClient(DownloadProtocol.Torrent);
var client5 = Subject.GetDownloadClient(DownloadProtocol.Torrent);
client1.Definition.Id.Should().Be(3);
client2.Definition.Id.Should().Be(4);
client3.Definition.Id.Should().Be(3);
client4.Definition.Id.Should().Be(4);
}
[Test]
public void should_not_skip_secondary_prio_torrent_client_if_primary_blocked()
{
WithUsenetClient();
WithTorrentClient(2);
WithTorrentClient(2);
WithTorrentClient();
GivenBlockedClient(4);
var client1 = Subject.GetDownloadClient(DownloadProtocol.Torrent);
var client2 = Subject.GetDownloadClient(DownloadProtocol.Torrent);
var client3 = Subject.GetDownloadClient(DownloadProtocol.Torrent);
var client4 = Subject.GetDownloadClient(DownloadProtocol.Torrent);
var client5 = Subject.GetDownloadClient(DownloadProtocol.Torrent);
client1.Definition.Id.Should().Be(2);
client2.Definition.Id.Should().Be(3);
client3.Definition.Id.Should().Be(2);
client4.Definition.Id.Should().Be(3);
}
}
}
@@ -1,161 +0,0 @@
using System;
using System.Linq;
using FluentAssertions;
using Moq;
using NUnit.Framework;
using NzbDrone.Common.EnvironmentInfo;
using NzbDrone.Core.Download;
using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test.Download
{
public class DownloadClientStatusServiceFixture : CoreTest<DownloadClientStatusService>
{
private DateTime _epoch;
[SetUp]
public void SetUp()
{
_epoch = DateTime.UtcNow;
Mocker.GetMock<IRuntimeInfo>()
.SetupGet(v => v.StartTime)
.Returns(_epoch - TimeSpan.FromHours(1));
}
private DownloadClientStatus WithStatus(DownloadClientStatus status)
{
Mocker.GetMock<IDownloadClientStatusRepository>()
.Setup(v => v.FindByProviderId(1))
.Returns(status);
Mocker.GetMock<IDownloadClientStatusRepository>()
.Setup(v => v.All())
.Returns(new[] { status });
return status;
}
private void VerifyUpdate()
{
Mocker.GetMock<IDownloadClientStatusRepository>()
.Verify(v => v.Upsert(It.IsAny<DownloadClientStatus>()), Times.Once());
}
private void VerifyNoUpdate()
{
Mocker.GetMock<IDownloadClientStatusRepository>()
.Verify(v => v.Upsert(It.IsAny<DownloadClientStatus>()), Times.Never());
}
[Test]
public void should_not_consider_blocked_within_5_minutes_since_initial_failure()
{
WithStatus(new DownloadClientStatus
{
InitialFailure = _epoch - TimeSpan.FromMinutes(4),
MostRecentFailure = _epoch - TimeSpan.FromSeconds(4),
EscalationLevel = 3
});
Subject.RecordFailure(1);
VerifyUpdate();
var status = Subject.GetBlockedProviders().FirstOrDefault();
status.Should().BeNull();
}
[Test]
public void should_consider_blocked_after_5_minutes_since_initial_failure()
{
WithStatus(new DownloadClientStatus
{
InitialFailure = _epoch - TimeSpan.FromMinutes(6),
MostRecentFailure = _epoch - TimeSpan.FromSeconds(120),
EscalationLevel = 3
});
Subject.RecordFailure(1);
VerifyUpdate();
var status = Subject.GetBlockedProviders().FirstOrDefault();
status.Should().NotBeNull();
}
[Test]
public void should_not_escalate_further_till_after_5_minutes_since_initial_failure()
{
var origStatus = WithStatus(new DownloadClientStatus
{
InitialFailure = _epoch - TimeSpan.FromMinutes(4),
MostRecentFailure = _epoch - TimeSpan.FromSeconds(4),
EscalationLevel = 3
});
Subject.RecordFailure(1);
Subject.RecordFailure(1);
Subject.RecordFailure(1);
Subject.RecordFailure(1);
Subject.RecordFailure(1);
Subject.RecordFailure(1);
Subject.RecordFailure(1);
var status = Subject.GetBlockedProviders().FirstOrDefault();
status.Should().BeNull();
origStatus.EscalationLevel.Should().Be(3);
}
[Test]
public void should_escalate_further_after_5_minutes_since_initial_failure()
{
WithStatus(new DownloadClientStatus
{
InitialFailure = _epoch - TimeSpan.FromMinutes(6),
MostRecentFailure = _epoch - TimeSpan.FromSeconds(120),
EscalationLevel = 3
});
Subject.RecordFailure(1);
Subject.RecordFailure(1);
Subject.RecordFailure(1);
Subject.RecordFailure(1);
Subject.RecordFailure(1);
Subject.RecordFailure(1);
Subject.RecordFailure(1);
var status = Subject.GetBlockedProviders().FirstOrDefault();
status.Should().NotBeNull();
status.EscalationLevel.Should().BeGreaterThan(3);
}
[Test]
public void should_not_escalate_beyond_3_hours()
{
WithStatus(new DownloadClientStatus
{
InitialFailure = _epoch - TimeSpan.FromMinutes(6),
MostRecentFailure = _epoch - TimeSpan.FromSeconds(120),
EscalationLevel = 3
});
Subject.RecordFailure(1);
Subject.RecordFailure(1);
Subject.RecordFailure(1);
Subject.RecordFailure(1);
Subject.RecordFailure(1);
Subject.RecordFailure(1);
Subject.RecordFailure(1);
Subject.RecordFailure(1);
Subject.RecordFailure(1);
var status = Subject.GetBlockedProviders().FirstOrDefault();
status.Should().NotBeNull();
status.DisabledTill.Should().HaveValue();
status.DisabledTill.Should().NotBeAfter(_epoch + TimeSpan.FromHours(3.1));
}
}
}
@@ -1,4 +1,4 @@
using FluentAssertions; using FluentAssertions;
using NUnit.Framework; using NUnit.Framework;
using NzbDrone.Core.HealthCheck; using NzbDrone.Core.HealthCheck;
using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Test.Framework;
@@ -8,11 +8,11 @@ namespace NzbDrone.Core.Test.HealthCheck
[TestFixture] [TestFixture]
public class HealthCheckFixture : CoreTest public class HealthCheckFixture : CoreTest
{ {
private const string WikiRoot = "https://wikijs.servarr.com/"; private const string WikiRoot = "https://wiki.servarr.com/";
[TestCase("I blew up because of some weird user mistake", null, WikiRoot + "prowlarr/system#i-blew-up-because-of-some-weird-user-mistake")] [TestCase("I blew up because of some weird user mistake", null, WikiRoot + "Prowlarr_System#i_blew_up_because_of_some_weird_user_mistake")]
[TestCase("I blew up because of some weird user mistake", "#my-health-check", WikiRoot + "prowlarr/system#my-health-check")] [TestCase("I blew up because of some weird user mistake", "#my_health_check", WikiRoot + "Prowlarr_System#my_health_check")]
[TestCase("I blew up because of some weird user mistake", "custom-page#my-health-check", WikiRoot + "prowlarr/custom-page#my-health-check")] [TestCase("I blew up because of some weird user mistake", "Custom-Page#my_health_check", WikiRoot + "Custom-Page#my_health_check")]
public void should_format_wiki_url(string message, string wikiFragment, string expectedUrl) public void should_format_wiki_url(string message, string wikiFragment, string expectedUrl)
{ {
var subject = new NzbDrone.Core.HealthCheck.HealthCheck(typeof(HealthCheckBase), HealthCheckResult.Warning, message, wikiFragment); var subject = new NzbDrone.Core.HealthCheck.HealthCheck(typeof(HealthCheckBase), HealthCheckResult.Warning, message, wikiFragment);
@@ -1,13 +1,12 @@
using NLog; using NLog;
using NzbDrone.Common.Http; using NzbDrone.Common.Http;
using NzbDrone.Core.Configuration; using NzbDrone.Core.Configuration;
using NzbDrone.Core.Download;
using NzbDrone.Core.Indexers; using NzbDrone.Core.Indexers;
using NzbDrone.Core.Messaging.Events; using NzbDrone.Core.Messaging.Events;
namespace NzbDrone.Core.Test.IndexerTests namespace NzbDrone.Core.Test.IndexerTests
{ {
public class TestIndexer : UsenetIndexerBase<TestIndexerSettings> public class TestIndexer : HttpIndexerBase<TestIndexerSettings>
{ {
public override string Name => "Test Indexer"; public override string Name => "Test Indexer";
public override string BaseUrl => "http://testindexer.com"; public override string BaseUrl => "http://testindexer.com";
@@ -19,8 +18,8 @@ namespace NzbDrone.Core.Test.IndexerTests
public int _supportedPageSize; public int _supportedPageSize;
public override int PageSize => _supportedPageSize; public override int PageSize => _supportedPageSize;
public TestIndexer(IHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, IValidateNzbs nzbValidationService, Logger logger) public TestIndexer(IHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger)
: base(httpClient, eventAggregator, indexerStatusService, configService, nzbValidationService, logger) : base(httpClient, eventAggregator, indexerStatusService, configService, logger)
{ {
} }
@@ -146,7 +146,7 @@ namespace NzbDrone.Core.Applications.Lidarr
Fields = schema.Fields, Fields = schema.Fields,
}; };
lidarrIndexer.Fields.FirstOrDefault(x => x.Name == "baseUrl").Value = $"{Settings.ProwlarrUrl.TrimEnd('/')}/{indexer.Id}/"; lidarrIndexer.Fields.FirstOrDefault(x => x.Name == "baseUrl").Value = $"{Settings.ProwlarrUrl}/{indexer.Id}/";
lidarrIndexer.Fields.FirstOrDefault(x => x.Name == "apiPath").Value = "/api"; lidarrIndexer.Fields.FirstOrDefault(x => x.Name == "apiPath").Value = "/api";
lidarrIndexer.Fields.FirstOrDefault(x => x.Name == "apiKey").Value = _configFileProvider.ApiKey; lidarrIndexer.Fields.FirstOrDefault(x => x.Name == "apiKey").Value = _configFileProvider.ApiKey;
lidarrIndexer.Fields.FirstOrDefault(x => x.Name == "categories").Value = JArray.FromObject(indexer.Capabilities.Categories.SupportedCategories(Settings.SyncCategories.ToArray())); lidarrIndexer.Fields.FirstOrDefault(x => x.Name == "categories").Value = JArray.FromObject(indexer.Capabilities.Categories.SupportedCategories(Settings.SyncCategories.ToArray()));
@@ -146,7 +146,7 @@ namespace NzbDrone.Core.Applications.Radarr
Fields = schema.Fields, Fields = schema.Fields,
}; };
radarrIndexer.Fields.FirstOrDefault(x => x.Name == "baseUrl").Value = $"{Settings.ProwlarrUrl.TrimEnd('/')}/{indexer.Id}/"; radarrIndexer.Fields.FirstOrDefault(x => x.Name == "baseUrl").Value = $"{Settings.ProwlarrUrl}/{indexer.Id}/";
radarrIndexer.Fields.FirstOrDefault(x => x.Name == "apiPath").Value = "/api"; radarrIndexer.Fields.FirstOrDefault(x => x.Name == "apiPath").Value = "/api";
radarrIndexer.Fields.FirstOrDefault(x => x.Name == "apiKey").Value = _configFileProvider.ApiKey; radarrIndexer.Fields.FirstOrDefault(x => x.Name == "apiKey").Value = _configFileProvider.ApiKey;
radarrIndexer.Fields.FirstOrDefault(x => x.Name == "categories").Value = JArray.FromObject(indexer.Capabilities.Categories.SupportedCategories(Settings.SyncCategories.ToArray())); radarrIndexer.Fields.FirstOrDefault(x => x.Name == "categories").Value = JArray.FromObject(indexer.Capabilities.Categories.SupportedCategories(Settings.SyncCategories.ToArray()));
@@ -146,7 +146,7 @@ namespace NzbDrone.Core.Applications.Readarr
Fields = schema.Fields, Fields = schema.Fields,
}; };
readarrIndexer.Fields.FirstOrDefault(x => x.Name == "baseUrl").Value = $"{Settings.ProwlarrUrl.TrimEnd('/')}/{indexer.Id}/"; readarrIndexer.Fields.FirstOrDefault(x => x.Name == "baseUrl").Value = $"{Settings.ProwlarrUrl}/{indexer.Id}/";
readarrIndexer.Fields.FirstOrDefault(x => x.Name == "apiPath").Value = "/api"; readarrIndexer.Fields.FirstOrDefault(x => x.Name == "apiPath").Value = "/api";
readarrIndexer.Fields.FirstOrDefault(x => x.Name == "apiKey").Value = _configFileProvider.ApiKey; readarrIndexer.Fields.FirstOrDefault(x => x.Name == "apiKey").Value = _configFileProvider.ApiKey;
readarrIndexer.Fields.FirstOrDefault(x => x.Name == "categories").Value = JArray.FromObject(indexer.Capabilities.Categories.SupportedCategories(Settings.SyncCategories.ToArray())); readarrIndexer.Fields.FirstOrDefault(x => x.Name == "categories").Value = JArray.FromObject(indexer.Capabilities.Categories.SupportedCategories(Settings.SyncCategories.ToArray()));
@@ -146,7 +146,7 @@ namespace NzbDrone.Core.Applications.Sonarr
Fields = schema.Fields, Fields = schema.Fields,
}; };
sonarrIndexer.Fields.FirstOrDefault(x => x.Name == "baseUrl").Value = $"{Settings.ProwlarrUrl.TrimEnd('/')}/{indexer.Id}/"; sonarrIndexer.Fields.FirstOrDefault(x => x.Name == "baseUrl").Value = $"{Settings.ProwlarrUrl}/{indexer.Id}/";
sonarrIndexer.Fields.FirstOrDefault(x => x.Name == "apiPath").Value = "/api"; sonarrIndexer.Fields.FirstOrDefault(x => x.Name == "apiPath").Value = "/api";
sonarrIndexer.Fields.FirstOrDefault(x => x.Name == "apiKey").Value = _configFileProvider.ApiKey; sonarrIndexer.Fields.FirstOrDefault(x => x.Name == "apiKey").Value = _configFileProvider.ApiKey;
sonarrIndexer.Fields.FirstOrDefault(x => x.Name == "categories").Value = JArray.FromObject(indexer.Capabilities.Categories.SupportedCategories(Settings.SyncCategories.ToArray())); sonarrIndexer.Fields.FirstOrDefault(x => x.Name == "categories").Value = JArray.FromObject(indexer.Capabilities.Categories.SupportedCategories(Settings.SyncCategories.ToArray()));
+2 -2
View File
@@ -108,10 +108,10 @@ namespace NzbDrone.Core.Datastore
if (OsInfo.IsOsx) if (OsInfo.IsOsx)
{ {
throw new CorruptDatabaseException("Database file: {0} is corrupt, restore from backup if available. See: https://wikijs.servarr.com/prowlarr/faq#i-use-prowlarr-on-a-mac-and-it-suddenly-stopped-working-what-happened", e, fileName); throw new CorruptDatabaseException("Database file: {0} is corrupt, restore from backup if available. See: https://wiki.servarr.com/Prowlarr_FAQ#I_use_Prowlarr_on_a_Mac_and_it_suddenly_stopped_working_What_happened", e, fileName);
} }
throw new CorruptDatabaseException("Database file: {0} is corrupt, restore from backup if available. See: https://wikijs.servarr.com/prowlarr/faq#i-am-getting-an-error-database-disk-image-is-malformed", e, fileName); throw new CorruptDatabaseException("Database file: {0} is corrupt, restore from backup if available. See: https://wiki.servarr.com/Prowlarr_FAQ#I_am_getting_an_error_Database_disk_image_is_malformed", e, fileName);
} }
catch (Exception e) catch (Exception e)
{ {
@@ -31,8 +31,9 @@ namespace NzbDrone.Core.Download.Clients.DownloadStation
IHttpClient httpClient, IHttpClient httpClient,
IConfigService configService, IConfigService configService,
IDiskProvider diskProvider, IDiskProvider diskProvider,
IValidateNzbs nzbValidationService,
Logger logger) Logger logger)
: base(httpClient, configService, diskProvider, logger) : base(httpClient, configService, diskProvider, nzbValidationService, logger)
{ {
_dsInfoProxy = dsInfoProxy; _dsInfoProxy = dsInfoProxy;
_dsTaskProxy = dsTaskProxy; _dsTaskProxy = dsTaskProxy;
@@ -20,8 +20,9 @@ namespace NzbDrone.Core.Download.Clients.NzbVortex
IHttpClient httpClient, IHttpClient httpClient,
IConfigService configService, IConfigService configService,
IDiskProvider diskProvider, IDiskProvider diskProvider,
IValidateNzbs nzbValidationService,
Logger logger) Logger logger)
: base(httpClient, configService, diskProvider, logger) : base(httpClient, configService, diskProvider, nzbValidationService, logger)
{ {
_proxy = proxy; _proxy = proxy;
} }
@@ -25,8 +25,9 @@ namespace NzbDrone.Core.Download.Clients.Nzbget
IHttpClient httpClient, IHttpClient httpClient,
IConfigService configService, IConfigService configService,
IDiskProvider diskProvider, IDiskProvider diskProvider,
IValidateNzbs nzbValidationService,
Logger logger) Logger logger)
: base(httpClient, configService, diskProvider, logger) : base(httpClient, configService, diskProvider, nzbValidationService, logger)
{ {
_proxy = proxy; _proxy = proxy;
} }
@@ -1,7 +1,5 @@
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Threading.Tasks;
using FluentValidation.Results; using FluentValidation.Results;
using NLog; using NLog;
using NzbDrone.Common.Disk; using NzbDrone.Common.Disk;
@@ -16,20 +14,24 @@ namespace NzbDrone.Core.Download.Clients.Pneumatic
{ {
public class Pneumatic : DownloadClientBase<PneumaticSettings> public class Pneumatic : DownloadClientBase<PneumaticSettings>
{ {
public Pneumatic(IConfigService configService, private readonly IHttpClient _httpClient;
public Pneumatic(IHttpClient httpClient,
IConfigService configService,
IDiskProvider diskProvider, IDiskProvider diskProvider,
Logger logger) Logger logger)
: base(configService, diskProvider, logger) : base(configService, diskProvider, logger)
{ {
_httpClient = httpClient;
} }
public override string Name => "Pneumatic"; public override string Name => "Pneumatic";
public override DownloadProtocol Protocol => DownloadProtocol.Usenet; public override DownloadProtocol Protocol => DownloadProtocol.Usenet;
public override async Task<string> Download(ReleaseInfo release, bool redirect, IIndexer indexer) public override string Download(ReleaseInfo release, bool redirect)
{ {
var url = new Uri(release.DownloadUrl); var url = release.DownloadUrl;
var title = release.Title; var title = release.Title;
title = StringUtil.CleanFileName(title); title = StringUtil.CleanFileName(title);
@@ -38,10 +40,7 @@ namespace NzbDrone.Core.Download.Clients.Pneumatic
var nzbFile = Path.Combine(Settings.NzbFolder, title + ".nzb"); var nzbFile = Path.Combine(Settings.NzbFolder, title + ".nzb");
_logger.Debug("Downloading NZB from: {0} to: {1}", url, nzbFile); _logger.Debug("Downloading NZB from: {0} to: {1}", url, nzbFile);
_httpClient.DownloadFile(url, nzbFile);
var nzbData = await indexer.Download(url);
File.WriteAllBytes(nzbFile, nzbData);
_logger.Debug("NZB Download succeeded, saved to: {0}", nzbFile); _logger.Debug("NZB Download succeeded, saved to: {0}", nzbFile);
@@ -22,8 +22,9 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd
IHttpClient httpClient, IHttpClient httpClient,
IConfigService configService, IConfigService configService,
IDiskProvider diskProvider, IDiskProvider diskProvider,
IValidateNzbs nzbValidationService,
Logger logger) Logger logger)
: base(httpClient, configService, diskProvider, logger) : base(httpClient, configService, diskProvider, nzbValidationService, logger)
{ {
_proxy = proxy; _proxy = proxy;
} }
@@ -66,7 +66,7 @@ namespace NzbDrone.Core.Download.Clients.RTorrent
{ {
_logger.Debug("rTorrent didn't add the torrent within {0} seconds: {1}.", tries * retryDelay / 1000, filename); _logger.Debug("rTorrent didn't add the torrent within {0} seconds: {1}.", tries * retryDelay / 1000, filename);
throw new ReleaseDownloadException("Downloading torrent failed"); throw new ReleaseDownloadException(release, "Downloading torrent failed");
} }
return hash; return hash;
@@ -1,6 +1,5 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks;
using FluentValidation.Results; using FluentValidation.Results;
using NLog; using NLog;
using NzbDrone.Common.Disk; using NzbDrone.Common.Disk;
@@ -55,7 +54,7 @@ namespace NzbDrone.Core.Download
get; get;
} }
public abstract Task<string> Download(ReleaseInfo release, bool redirect, IIndexer indexer); public abstract string Download(ReleaseInfo release, bool redirect);
public ValidationResult Test() public ValidationResult Test()
{ {
@@ -17,7 +17,7 @@ namespace NzbDrone.Core.Download
{ {
public interface IDownloadService public interface IDownloadService
{ {
Task SendReportToClient(ReleaseInfo release, string source, string host, bool redirect); void SendReportToClient(ReleaseInfo release, string source, string host, bool redirect);
Task<byte[]> DownloadReport(string link, int indexerId, string source, string host, string title); Task<byte[]> DownloadReport(string link, int indexerId, string source, string host, string title);
void RecordRedirect(string link, int indexerId, string source, string host, string title); void RecordRedirect(string link, int indexerId, string source, string host, string title);
} }
@@ -49,7 +49,7 @@ namespace NzbDrone.Core.Download
_logger = logger; _logger = logger;
} }
public async Task SendReportToClient(ReleaseInfo release, string source, string host, bool redirect) public void SendReportToClient(ReleaseInfo release, string source, string host, bool redirect)
{ {
var downloadTitle = release.Title; var downloadTitle = release.Title;
var downloadClient = _downloadClientProvider.GetDownloadClient(release.DownloadProtocol); var downloadClient = _downloadClientProvider.GetDownloadClient(release.DownloadProtocol);
@@ -69,12 +69,10 @@ namespace NzbDrone.Core.Download
_rateLimitService.WaitAndPulse(url.Host, TimeSpan.FromSeconds(2)); _rateLimitService.WaitAndPulse(url.Host, TimeSpan.FromSeconds(2));
} }
var indexer = _indexerFactory.GetInstance(_indexerFactory.Get(release.IndexerId));
string downloadClientId; string downloadClientId;
try try
{ {
downloadClientId = await downloadClient.Download(release, redirect, indexer); downloadClientId = downloadClient.Download(release, redirect);
_downloadClientStatusService.RecordSuccess(downloadClient.Definition.Id); _downloadClientStatusService.RecordSuccess(downloadClient.Definition.Id);
_indexerStatusService.RecordSuccess(release.IndexerId); _indexerStatusService.RecordSuccess(release.IndexerId);
} }
@@ -1,4 +1,3 @@
using System.Threading.Tasks;
using NzbDrone.Core.Indexers; using NzbDrone.Core.Indexers;
using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.ThingiProvider; using NzbDrone.Core.ThingiProvider;
@@ -8,6 +7,6 @@ namespace NzbDrone.Core.Download
public interface IDownloadClient : IProvider public interface IDownloadClient : IProvider
{ {
DownloadProtocol Protocol { get; } DownloadProtocol Protocol { get; }
Task<string> Download(ReleaseInfo release, bool redirect, IIndexer indexer); string Download(ReleaseInfo release, bool redirect);
} }
} }
@@ -8,12 +8,12 @@ namespace NzbDrone.Core.Download
{ {
public interface IValidateNzbs public interface IValidateNzbs
{ {
void Validate(byte[] fileContent); void Validate(string filename, byte[] fileContent);
} }
public class NzbValidationService : IValidateNzbs public class NzbValidationService : IValidateNzbs
{ {
public void Validate(byte[] fileContent) public void Validate(string filename, byte[] fileContent)
{ {
var reader = new StreamReader(new MemoryStream(fileContent)); var reader = new StreamReader(new MemoryStream(fileContent));
@@ -24,7 +24,7 @@ namespace NzbDrone.Core.Download
if (nzb == null) if (nzb == null)
{ {
throw new InvalidNzbException("Invalid NZB: No Root element"); throw new InvalidNzbException("Invalid NZB: No Root element [{0}]", filename);
} }
// nZEDb has an bug in their error reporting code spitting out invalid http status codes // nZEDb has an bug in their error reporting code spitting out invalid http status codes
@@ -37,7 +37,7 @@ namespace NzbDrone.Core.Download
if (!nzb.Name.LocalName.Equals("nzb")) if (!nzb.Name.LocalName.Equals("nzb"))
{ {
throw new InvalidNzbException("Invalid NZB: Unexpected root element. Expected 'nzb' found '{0}'", nzb.Name.LocalName); throw new InvalidNzbException("Invalid NZB: Unexpected root element. Expected 'nzb' found '{0}' [{1}]", nzb.Name.LocalName, filename);
} }
var ns = nzb.Name.Namespace; var ns = nzb.Name.Namespace;
@@ -45,7 +45,7 @@ namespace NzbDrone.Core.Download
if (files.Empty()) if (files.Empty())
{ {
throw new InvalidNzbException("Invalid NZB: No files"); throw new InvalidNzbException("Invalid NZB: No files [{0}]", filename);
} }
} }
} }
+62 -21
View File
@@ -1,7 +1,5 @@
using System; using System;
using System.Net; using System.Net;
using System.Text;
using System.Threading.Tasks;
using MonoTorrent; using MonoTorrent;
using NLog; using NLog;
using NzbDrone.Common.Disk; using NzbDrone.Common.Disk;
@@ -41,7 +39,7 @@ namespace NzbDrone.Core.Download
protected abstract string AddFromTorrentFile(ReleaseInfo release, string hash, string filename, byte[] fileContent); protected abstract string AddFromTorrentFile(ReleaseInfo release, string hash, string filename, byte[] fileContent);
protected abstract string AddFromTorrentLink(ReleaseInfo release, string hash, string torrentLink); protected abstract string AddFromTorrentLink(ReleaseInfo release, string hash, string torrentLink);
public override async Task<string> Download(ReleaseInfo release, bool redirect, IIndexer indexer) public override string Download(ReleaseInfo release, bool redirect)
{ {
var torrentInfo = release as TorrentInfo; var torrentInfo = release as TorrentInfo;
@@ -68,7 +66,7 @@ namespace NzbDrone.Core.Download
{ {
try try
{ {
return await DownloadFromWebUrl(release, indexer, torrentUrl); return DownloadFromWebUrl(release, torrentUrl);
} }
catch (Exception ex) catch (Exception ex)
{ {
@@ -89,7 +87,7 @@ namespace NzbDrone.Core.Download
} }
catch (NotSupportedException ex) catch (NotSupportedException ex)
{ {
throw new ReleaseDownloadException("Magnet not supported by download client. ({0})", ex.Message); throw new ReleaseDownloadException(release, "Magnet not supported by download client. ({0})", ex.Message);
} }
} }
} }
@@ -105,7 +103,7 @@ namespace NzbDrone.Core.Download
{ {
if (torrentUrl.IsNullOrWhiteSpace()) if (torrentUrl.IsNullOrWhiteSpace())
{ {
throw new ReleaseDownloadException("Magnet not supported by download client. ({0})", ex.Message); throw new ReleaseDownloadException(release, "Magnet not supported by download client. ({0})", ex.Message);
} }
_logger.Debug("Magnet not supported by download client, trying torrent. ({0})", ex.Message); _logger.Debug("Magnet not supported by download client, trying torrent. ({0})", ex.Message);
@@ -114,31 +112,74 @@ namespace NzbDrone.Core.Download
if (torrentUrl.IsNotNullOrWhiteSpace()) if (torrentUrl.IsNotNullOrWhiteSpace())
{ {
return await DownloadFromWebUrl(release, indexer, torrentUrl); return DownloadFromWebUrl(release, torrentUrl);
} }
} }
return null; return null;
} }
private async Task<string> DownloadFromWebUrl(ReleaseInfo release, IIndexer indexer, string torrentUrl) private string DownloadFromWebUrl(ReleaseInfo release, string torrentUrl)
{ {
byte[] torrentFile = null; byte[] torrentFile = null;
torrentFile = await indexer.Download(new Uri(torrentUrl)); try
// handle magnet URLs
if (torrentFile.Length >= 7
&& torrentFile[0] == 0x6d
&& torrentFile[1] == 0x61
&& torrentFile[2] == 0x67
&& torrentFile[3] == 0x6e
&& torrentFile[4] == 0x65
&& torrentFile[5] == 0x74
&& torrentFile[6] == 0x3a)
{ {
var magnetUrl = Encoding.UTF8.GetString(torrentFile); var request = new HttpRequest(torrentUrl);
return DownloadFromMagnetUrl(release, magnetUrl); request.Headers.Accept = "application/x-bittorrent";
request.AllowAutoRedirect = false;
var response = _httpClient.Get(request);
if (response.StatusCode == HttpStatusCode.MovedPermanently ||
response.StatusCode == HttpStatusCode.Found ||
response.StatusCode == HttpStatusCode.SeeOther)
{
var locationHeader = response.Headers.GetSingleValue("Location");
_logger.Trace("Torrent request is being redirected to: {0}", locationHeader);
if (locationHeader != null)
{
if (locationHeader.StartsWith("magnet:"))
{
return DownloadFromMagnetUrl(release, locationHeader);
}
return DownloadFromWebUrl(release, locationHeader);
}
throw new WebException("Remote website tried to redirect without providing a location.");
}
torrentFile = response.ResponseData;
_logger.Debug("Downloading torrent for release '{0}' finished ({1} bytes from {2})", release.Title, torrentFile.Length, torrentUrl);
}
catch (HttpException ex)
{
if (ex.Response.StatusCode == HttpStatusCode.NotFound)
{
_logger.Error(ex, "Downloading torrent file for release '{0}' failed since it no longer exists ({1})", release.Title, torrentUrl);
throw new ReleaseUnavailableException(release, "Downloading torrent failed", ex);
}
if ((int)ex.Response.StatusCode == 429)
{
_logger.Error("API Grab Limit reached for {0}", torrentUrl);
}
else
{
_logger.Error(ex, "Downloading torrent file for release '{0}' failed ({1})", release.Title, torrentUrl);
}
throw new ReleaseDownloadException(release, "Downloading torrent failed", ex);
}
catch (WebException ex)
{
_logger.Error(ex, "Downloading torrent file for release '{0}' failed ({1})", release.Title, torrentUrl);
throw new ReleaseDownloadException(release, "Downloading torrent failed", ex);
} }
var filename = string.Format("{0}.torrent", StringUtil.CleanFileName(release.Title)); var filename = string.Format("{0}.torrent", StringUtil.CleanFileName(release.Title));
+41 -5
View File
@@ -1,9 +1,9 @@
using System; using System.Net;
using System.Threading.Tasks;
using NLog; using NLog;
using NzbDrone.Common.Disk; using NzbDrone.Common.Disk;
using NzbDrone.Common.Http; using NzbDrone.Common.Http;
using NzbDrone.Core.Configuration; using NzbDrone.Core.Configuration;
using NzbDrone.Core.Exceptions;
using NzbDrone.Core.Indexers; using NzbDrone.Core.Indexers;
using NzbDrone.Core.Parser; using NzbDrone.Core.Parser;
using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Parser.Model;
@@ -15,14 +15,17 @@ namespace NzbDrone.Core.Download
where TSettings : IProviderConfig, new() where TSettings : IProviderConfig, new()
{ {
protected readonly IHttpClient _httpClient; protected readonly IHttpClient _httpClient;
private readonly IValidateNzbs _nzbValidationService;
protected UsenetClientBase(IHttpClient httpClient, protected UsenetClientBase(IHttpClient httpClient,
IConfigService configService, IConfigService configService,
IDiskProvider diskProvider, IDiskProvider diskProvider,
IValidateNzbs nzbValidationService,
Logger logger) Logger logger)
: base(configService, diskProvider, logger) : base(configService, diskProvider, logger)
{ {
_httpClient = httpClient; _httpClient = httpClient;
_nzbValidationService = nzbValidationService;
} }
public override DownloadProtocol Protocol => DownloadProtocol.Usenet; public override DownloadProtocol Protocol => DownloadProtocol.Usenet;
@@ -30,9 +33,9 @@ namespace NzbDrone.Core.Download
protected abstract string AddFromNzbFile(ReleaseInfo release, string filename, byte[] fileContents); protected abstract string AddFromNzbFile(ReleaseInfo release, string filename, byte[] fileContents);
protected abstract string AddFromLink(ReleaseInfo release); protected abstract string AddFromLink(ReleaseInfo release);
public override async Task<string> Download(ReleaseInfo release, bool redirect, IIndexer indexer) public override string Download(ReleaseInfo release, bool redirect)
{ {
var url = new Uri(release.DownloadUrl); var url = release.DownloadUrl;
if (redirect) if (redirect)
{ {
@@ -43,7 +46,40 @@ namespace NzbDrone.Core.Download
byte[] nzbData; byte[] nzbData;
nzbData = await indexer.Download(url); try
{
var request = new HttpRequest(url);
nzbData = _httpClient.Get(request).ResponseData;
_logger.Debug("Downloaded nzb for release '{0}' finished ({1} bytes from {2})", release.Title, nzbData.Length, url);
}
catch (HttpException ex)
{
if (ex.Response.StatusCode == HttpStatusCode.NotFound)
{
_logger.Error(ex, "Downloading nzb file for release '{0}' failed since it no longer exists ({1})", release.Title, url);
throw new ReleaseUnavailableException(release, "Downloading nzb failed", ex);
}
if ((int)ex.Response.StatusCode == 429)
{
_logger.Error("API Grab Limit reached for {0}", url);
}
else
{
_logger.Error(ex, "Downloading nzb for release '{0}' failed ({1})", release.Title, url);
}
throw new ReleaseDownloadException(release, "Downloading nzb failed", ex);
}
catch (WebException ex)
{
_logger.Error(ex, "Downloading nzb for release '{0}' failed ({1})", release.Title, url);
throw new ReleaseDownloadException(release, "Downloading nzb failed", ex);
}
_nzbValidationService.Validate(filename, nzbData);
_logger.Info("Adding report [{0}] to the queue.", release.Title); _logger.Info("Adding report [{0}] to the queue.", release.Title);
return AddFromNzbFile(release, filename, nzbData); return AddFromNzbFile(release, filename, nzbData);
@@ -1,33 +1,28 @@
using System; using System;
using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Parser.Model;
namespace NzbDrone.Core.Exceptions namespace NzbDrone.Core.Exceptions
{ {
public class DownloadClientRejectedReleaseException : ReleaseDownloadException public class DownloadClientRejectedReleaseException : ReleaseDownloadException
{ {
public ReleaseInfo Release { get; set; }
public DownloadClientRejectedReleaseException(ReleaseInfo release, string message, params object[] args) public DownloadClientRejectedReleaseException(ReleaseInfo release, string message, params object[] args)
: base(message, args) : base(release, message, args)
{ {
Release = release;
} }
public DownloadClientRejectedReleaseException(ReleaseInfo release, string message) public DownloadClientRejectedReleaseException(ReleaseInfo release, string message)
: base(message) : base(release, message)
{ {
Release = release;
} }
public DownloadClientRejectedReleaseException(ReleaseInfo release, string message, Exception innerException, params object[] args) public DownloadClientRejectedReleaseException(ReleaseInfo release, string message, Exception innerException, params object[] args)
: base(message, innerException, args) : base(release, message, innerException, args)
{ {
Release = release;
} }
public DownloadClientRejectedReleaseException(ReleaseInfo release, string message, Exception innerException) public DownloadClientRejectedReleaseException(ReleaseInfo release, string message, Exception innerException)
: base(message, innerException) : base(release, message, innerException)
{ {
Release = release;
} }
} }
} }
@@ -1,4 +1,4 @@
using System; using System;
using NzbDrone.Common.Exceptions; using NzbDrone.Common.Exceptions;
using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Parser.Model;
@@ -6,24 +6,30 @@ namespace NzbDrone.Core.Exceptions
{ {
public class ReleaseDownloadException : NzbDroneException public class ReleaseDownloadException : NzbDroneException
{ {
public ReleaseDownloadException(string message, params object[] args) public ReleaseInfo Release { get; set; }
public ReleaseDownloadException(ReleaseInfo release, string message, params object[] args)
: base(message, args) : base(message, args)
{ {
Release = release;
} }
public ReleaseDownloadException(string message) public ReleaseDownloadException(ReleaseInfo release, string message)
: base(message) : base(message)
{ {
Release = release;
} }
public ReleaseDownloadException(string message, Exception innerException, params object[] args) public ReleaseDownloadException(ReleaseInfo release, string message, Exception innerException, params object[] args)
: base(message, innerException, args) : base(message, innerException, args)
{ {
Release = release;
} }
public ReleaseDownloadException(string message, Exception innerException) public ReleaseDownloadException(ReleaseInfo release, string message, Exception innerException)
: base(message, innerException) : base(message, innerException)
{ {
Release = release;
} }
} }
} }
@@ -1,27 +1,27 @@
using System; using System;
using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Parser.Model;
namespace NzbDrone.Core.Exceptions namespace NzbDrone.Core.Exceptions
{ {
public class ReleaseUnavailableException : ReleaseDownloadException public class ReleaseUnavailableException : ReleaseDownloadException
{ {
public ReleaseUnavailableException(string message, params object[] args) public ReleaseUnavailableException(ReleaseInfo release, string message, params object[] args)
: base(message, args) : base(release, message, args)
{ {
} }
public ReleaseUnavailableException(string message) public ReleaseUnavailableException(ReleaseInfo release, string message)
: base(message) : base(release, message)
{ {
} }
public ReleaseUnavailableException(string message, Exception innerException, params object[] args) public ReleaseUnavailableException(ReleaseInfo release, string message, Exception innerException, params object[] args)
: base(message, innerException, args) : base(release, message, innerException, args)
{ {
} }
public ReleaseUnavailableException(string message, Exception innerException) public ReleaseUnavailableException(ReleaseInfo release, string message, Exception innerException)
: base(message, innerException) : base(release, message, innerException)
{ {
} }
} }
@@ -44,14 +44,14 @@ namespace NzbDrone.Core.HealthCheck.Checks
return new HealthCheck(GetType(), return new HealthCheck(GetType(),
HealthCheckResult.Error, HealthCheckResult.Error,
_localizationService.GetLocalizedString("ApplicationStatusCheckAllClientMessage"), _localizationService.GetLocalizedString("ApplicationStatusCheckAllClientMessage"),
"#applications-are-unavailable-due-to-failures"); "#applications_are_unavailable_due_to_failures");
} }
return new HealthCheck(GetType(), return new HealthCheck(GetType(),
HealthCheckResult.Warning, HealthCheckResult.Warning,
string.Format(_localizationService.GetLocalizedString("ApplicationStatusCheckSingleClientMessage"), string.Format(_localizationService.GetLocalizedString("ApplicationStatusCheckSingleClientMessage"),
string.Join(", ", backOffProviders.Select(v => v.Provider.Definition.Name))), string.Join(", ", backOffProviders.Select(v => v.Provider.Definition.Name))),
"#applications-are-unavailable-due-to-failures"); "#applications_are_unavailable_due_to_failures");
} }
} }
} }
@@ -37,10 +37,10 @@ namespace NzbDrone.Core.HealthCheck.Checks
if (backOffProviders.Count == enabledProviders.Count) if (backOffProviders.Count == enabledProviders.Count)
{ {
return new HealthCheck(GetType(), HealthCheckResult.Error, _localizationService.GetLocalizedString("DownloadClientStatusCheckAllClientMessage"), "#download-clients-are-unavailable-due-to-failures"); return new HealthCheck(GetType(), HealthCheckResult.Error, _localizationService.GetLocalizedString("DownloadClientStatusCheckAllClientMessage"), "#download_clients_are_unavailable_due_to_failures");
} }
return new HealthCheck(GetType(), HealthCheckResult.Warning, string.Format(_localizationService.GetLocalizedString("DownloadClientStatusCheckSingleClientMessage"), string.Join(", ", backOffProviders.Select(v => v.Provider.Definition.Name))), "#download-clients-are-unavailable-due-to-failures"); return new HealthCheck(GetType(), HealthCheckResult.Warning, string.Format(_localizationService.GetLocalizedString("DownloadClientStatusCheckSingleClientMessage"), string.Join(", ", backOffProviders.Select(v => v.Provider.Definition.Name))), "#download_clients_are_unavailable_due_to_failures");
} }
} }
} }
@@ -46,14 +46,14 @@ namespace NzbDrone.Core.HealthCheck.Checks
return new HealthCheck(GetType(), return new HealthCheck(GetType(),
HealthCheckResult.Error, HealthCheckResult.Error,
_localizationService.GetLocalizedString("IndexerLongTermStatusCheckAllClientMessage"), _localizationService.GetLocalizedString("IndexerLongTermStatusCheckAllClientMessage"),
"#indexers-are-unavailable-due-to-failures"); "#indexers_are_unavailable_due_to_failures");
} }
return new HealthCheck(GetType(), return new HealthCheck(GetType(),
HealthCheckResult.Warning, HealthCheckResult.Warning,
string.Format(_localizationService.GetLocalizedString("IndexerLongTermStatusCheckSingleClientMessage"), string.Format(_localizationService.GetLocalizedString("IndexerLongTermStatusCheckSingleClientMessage"),
string.Join(", ", backOffProviders.Select(v => v.Provider.Definition.Name))), string.Join(", ", backOffProviders.Select(v => v.Provider.Definition.Name))),
"#indexers-are-unavailable-due-to-failures"); "#indexers_are_unavailable_due_to_failures");
} }
} }
} }
@@ -44,14 +44,14 @@ namespace NzbDrone.Core.HealthCheck.Checks
return new HealthCheck(GetType(), return new HealthCheck(GetType(),
HealthCheckResult.Error, HealthCheckResult.Error,
_localizationService.GetLocalizedString("IndexerStatusCheckAllClientMessage"), _localizationService.GetLocalizedString("IndexerStatusCheckAllClientMessage"),
"#indexers-are-unavailable-due-to-failures"); "#indexers_are_unavailable_due_to_failures");
} }
return new HealthCheck(GetType(), return new HealthCheck(GetType(),
HealthCheckResult.Warning, HealthCheckResult.Warning,
string.Format(_localizationService.GetLocalizedString("IndexerStatusCheckSingleClientMessage"), string.Format(_localizationService.GetLocalizedString("IndexerStatusCheckSingleClientMessage"),
string.Join(", ", backOffProviders.Select(v => v.Provider.Definition.Name))), string.Join(", ", backOffProviders.Select(v => v.Provider.Definition.Name))),
"#indexers-are-unavailable-due-to-failures"); "#indexers_are_unavailable_due_to_failures");
} }
} }
} }
@@ -55,7 +55,7 @@ namespace NzbDrone.Core.HealthCheck.Checks
HealthCheckResult.Warning, HealthCheckResult.Warning,
string.Format(_localizationService.GetLocalizedString("NewznabVipCheckExpiringClientMessage"), string.Format(_localizationService.GetLocalizedString("NewznabVipCheckExpiringClientMessage"),
string.Join(", ", expiringProviders.Select(v => v.Definition.Name))), string.Join(", ", expiringProviders.Select(v => v.Definition.Name))),
"#newznab-vip-expiring"); "#newznab_vip_expiring");
} }
if (!expiredProviders.Empty()) if (!expiredProviders.Empty())
@@ -64,7 +64,7 @@ namespace NzbDrone.Core.HealthCheck.Checks
HealthCheckResult.Warning, HealthCheckResult.Warning,
string.Format(_localizationService.GetLocalizedString("NewznabVipCheckExpiredClientMessage"), string.Format(_localizationService.GetLocalizedString("NewznabVipCheckExpiredClientMessage"),
string.Join(", ", expiredProviders.Select(v => v.Definition.Name))), string.Join(", ", expiredProviders.Select(v => v.Definition.Name))),
"#newznab-vip-expired"); "#newznab_vip_expired");
} }
return new HealthCheck(GetType()); return new HealthCheck(GetType());
@@ -38,7 +38,7 @@ namespace NzbDrone.Core.HealthCheck.Checks
HealthCheckResult.Warning, HealthCheckResult.Warning,
string.Format(_localizationService.GetLocalizedString("IndexerObsoleteCheckMessage"), string.Format(_localizationService.GetLocalizedString("IndexerObsoleteCheckMessage"),
string.Join(", ", oldIndexers.Select(v => v.Name))), string.Join(", ", oldIndexers.Select(v => v.Name))),
"#indexers-are-obsolete"); "#indexers_are_obsolete");
} }
public override bool CheckOnSchedule => false; public override bool CheckOnSchedule => false;
@@ -25,10 +25,10 @@ namespace NzbDrone.Core.HealthCheck.Checks
{ {
if (currentBranch == "develop") if (currentBranch == "develop")
{ {
return new HealthCheck(GetType(), HealthCheckResult.Error, string.Format(_localizationService.GetLocalizedString("ReleaseBranchCheckPreviousVersionMessage"), _configFileService.Branch), "#branch-is-for-a-previous-version"); return new HealthCheck(GetType(), HealthCheckResult.Error, string.Format(_localizationService.GetLocalizedString("ReleaseBranchCheckPreviousVersionMessage"), _configFileService.Branch), "#branch_is_for_a_previous_version");
} }
return new HealthCheck(GetType(), HealthCheckResult.Warning, string.Format(_localizationService.GetLocalizedString("ReleaseBranchCheckOfficialBranchMessage"), _configFileService.Branch), "#branch-is-not-a-valid-release-branch"); return new HealthCheck(GetType(), HealthCheckResult.Warning, string.Format(_localizationService.GetLocalizedString("ReleaseBranchCheckOfficialBranchMessage"), _configFileService.Branch), "#branch_is_not_a_valid_release_branch");
} }
return new HealthCheck(GetType()); return new HealthCheck(GetType());
@@ -48,7 +48,7 @@ namespace NzbDrone.Core.HealthCheck.Checks
return new HealthCheck(GetType(), return new HealthCheck(GetType(),
HealthCheckResult.Error, HealthCheckResult.Error,
string.Format(_localizationService.GetLocalizedString("UpdateCheckStartupTranslocationMessage"), startupFolder), string.Format(_localizationService.GetLocalizedString("UpdateCheckStartupTranslocationMessage"), startupFolder),
"#cannot-install-update-because-startup-folder-is-in-an-app-translocation-folder."); "#cannot_install_update_because_startup_folder_is_in_an_app_translocation_folder.");
} }
if (!_diskProvider.FolderWritable(startupFolder)) if (!_diskProvider.FolderWritable(startupFolder))
@@ -56,7 +56,7 @@ namespace NzbDrone.Core.HealthCheck.Checks
return new HealthCheck(GetType(), return new HealthCheck(GetType(),
HealthCheckResult.Error, HealthCheckResult.Error,
string.Format(_localizationService.GetLocalizedString("UpdateCheckStartupNotWritableMessage"), startupFolder, Environment.UserName), string.Format(_localizationService.GetLocalizedString("UpdateCheckStartupNotWritableMessage"), startupFolder, Environment.UserName),
"#cannot-install-update-because-startup-folder-is-not-writable-by-the-user"); "#cannot_install_update_because_startup_folder_is_not_writable_by_the_user");
} }
if (!_diskProvider.FolderWritable(uiFolder)) if (!_diskProvider.FolderWritable(uiFolder))
@@ -64,7 +64,7 @@ namespace NzbDrone.Core.HealthCheck.Checks
return new HealthCheck(GetType(), return new HealthCheck(GetType(),
HealthCheckResult.Error, HealthCheckResult.Error,
string.Format(_localizationService.GetLocalizedString("UpdateCheckUINotWritableMessage"), uiFolder, Environment.UserName), string.Format(_localizationService.GetLocalizedString("UpdateCheckUINotWritableMessage"), uiFolder, Environment.UserName),
"#cannot-install-update-because-ui-folder-is-not-writable-by-the-user"); "#cannot_install_update_because_ui_folder_is_not_writable_by_the_user");
} }
} }
+2 -2
View File
@@ -34,12 +34,12 @@ namespace NzbDrone.Core.HealthCheck
private static string MakeWikiFragment(string message) private static string MakeWikiFragment(string message)
{ {
return "#" + CleanFragmentRegex.Replace(message.ToLower(), string.Empty).Replace(' ', '-'); return "#" + CleanFragmentRegex.Replace(message.ToLower(), string.Empty).Replace(' ', '_');
} }
private static HttpUri MakeWikiUrl(string fragment) private static HttpUri MakeWikiUrl(string fragment)
{ {
return new HttpUri("https://wikijs.servarr.com/prowlarr/system#") + new HttpUri(fragment); return new HttpUri("https://wiki.servarr.com/Prowlarr_System#") + new HttpUri(fragment);
} }
} }
@@ -4,7 +4,6 @@ using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using NLog; using NLog;
using NzbDrone.Common.Instrumentation.Extensions; using NzbDrone.Common.Instrumentation.Extensions;
using NzbDrone.Core.Download;
using NzbDrone.Core.Indexers; using NzbDrone.Core.Indexers;
using NzbDrone.Core.Indexers.Events; using NzbDrone.Core.Indexers.Events;
using NzbDrone.Core.IndexerSearch.Definitions; using NzbDrone.Core.IndexerSearch.Definitions;
@@ -89,8 +89,7 @@ namespace NzbDrone.Core.IndexerVersions
{ {
var req = new HttpRequest($"https://indexers.prowlarr.com/master/{DEFINITION_VERSION}/{id}"); var req = new HttpRequest($"https://indexers.prowlarr.com/master/{DEFINITION_VERSION}/{id}");
var response = _httpClient.Get(req); var response = _httpClient.Get(req);
var definition = _deserializer.Deserialize<CardigannDefinition>(response.Content); return _deserializer.Deserialize<CardigannDefinition>(response.Content);
return CleanIndexerDefinition(definition);
} }
private CardigannDefinition LoadIndexerDef(string fileKey) private CardigannDefinition LoadIndexerDef(string fileKey)
@@ -119,7 +118,42 @@ namespace NzbDrone.Core.IndexerVersions
var definitionString = File.ReadAllText(file.FullName); var definitionString = File.ReadAllText(file.FullName);
var definition = _deserializer.Deserialize<CardigannDefinition>(definitionString); var definition = _deserializer.Deserialize<CardigannDefinition>(definitionString);
return CleanIndexerDefinition(definition); //defaults
if (definition.Settings == null)
{
definition.Settings = new List<SettingsField>
{
new SettingsField { Name = "username", Label = "Username", Type = "text" },
new SettingsField { Name = "password", Label = "Password", Type = "password" }
};
}
if (definition.Encoding == null)
{
definition.Encoding = "UTF-8";
}
if (definition.Login != null && definition.Login.Method == null)
{
definition.Login.Method = "form";
}
if (definition.Search.Paths == null)
{
definition.Search.Paths = new List<SearchPathBlock>();
}
// convert definitions with a single search Path to a Paths entry
if (definition.Search.Path != null)
{
definition.Search.Paths.Add(new SearchPathBlock
{
Path = definition.Search.Path,
Inheritinputs = true
});
}
return definition;
} }
catch (Exception e) catch (Exception e)
{ {
@@ -131,45 +165,6 @@ namespace NzbDrone.Core.IndexerVersions
return GetHttpDefinition(fileKey); return GetHttpDefinition(fileKey);
} }
private CardigannDefinition CleanIndexerDefinition(CardigannDefinition definition)
{
if (definition.Settings == null)
{
definition.Settings = new List<SettingsField>
{
new SettingsField { Name = "username", Label = "Username", Type = "text" },
new SettingsField { Name = "password", Label = "Password", Type = "password" }
};
}
if (definition.Encoding == null)
{
definition.Encoding = "UTF-8";
}
if (definition.Login != null && definition.Login.Method == null)
{
definition.Login.Method = "form";
}
if (definition.Search.Paths == null)
{
definition.Search.Paths = new List<SearchPathBlock>();
}
// convert definitions with a single search Path to a Paths entry
if (definition.Search.Path != null)
{
definition.Search.Paths.Add(new SearchPathBlock
{
Path = definition.Search.Path,
Inheritinputs = true
});
}
return definition;
}
public void Execute(IndexerDefinitionUpdateCommand message) public void Execute(IndexerDefinitionUpdateCommand message)
{ {
UpdateLocalDefinitions(); UpdateLocalDefinitions();
@@ -23,7 +23,7 @@ using NzbDrone.Core.Validation;
namespace NzbDrone.Core.Indexers.Definitions namespace NzbDrone.Core.Indexers.Definitions
{ {
public class AnimeBytes : TorrentIndexerBase<AnimeBytesSettings> public class AnimeBytes : HttpIndexerBase<AnimeBytesSettings>
{ {
public override string Name => "AnimeBytes"; public override string Name => "AnimeBytes";
public override string BaseUrl => "https://animebytes.tv/"; public override string BaseUrl => "https://animebytes.tv/";
@@ -21,7 +21,7 @@ using NzbDrone.Core.Validation;
namespace NzbDrone.Core.Indexers.Definitions namespace NzbDrone.Core.Indexers.Definitions
{ {
public class AnimeTorrents : TorrentIndexerBase<AnimeTorrentsSettings> public class AnimeTorrents : HttpIndexerBase<AnimeTorrentsSettings>
{ {
public override string Name => "AnimeTorrents"; public override string Name => "AnimeTorrents";
@@ -9,7 +9,7 @@ using NzbDrone.Core.Messaging.Events;
namespace NzbDrone.Core.Indexers.Definitions.Avistaz namespace NzbDrone.Core.Indexers.Definitions.Avistaz
{ {
public abstract class AvistazBase : TorrentIndexerBase<AvistazSettings> public abstract class AvistazBase : HttpIndexerBase<AvistazSettings>
{ {
public override DownloadProtocol Protocol => DownloadProtocol.Torrent; public override DownloadProtocol Protocol => DownloadProtocol.Torrent;
public override string BaseUrl => ""; public override string BaseUrl => "";
@@ -21,7 +21,7 @@ using NzbDrone.Core.Validation;
namespace NzbDrone.Core.Indexers.Definitions namespace NzbDrone.Core.Indexers.Definitions
{ {
public class BakaBT : TorrentIndexerBase<BakaBTSettings> public class BakaBT : HttpIndexerBase<BakaBTSettings>
{ {
public override string Name => "BakaBT"; public override string Name => "BakaBT";
@@ -21,7 +21,7 @@ using NzbDrone.Core.Validation;
namespace NzbDrone.Core.Indexers.Definitions namespace NzbDrone.Core.Indexers.Definitions
{ {
public class BeyondHD : TorrentIndexerBase<BeyondHDSettings> public class BeyondHD : HttpIndexerBase<BeyondHDSettings>
{ {
public override string Name => "BeyondHD"; public override string Name => "BeyondHD";
@@ -6,7 +6,7 @@ using NzbDrone.Core.Messaging.Events;
namespace NzbDrone.Core.Indexers.BroadcastheNet namespace NzbDrone.Core.Indexers.BroadcastheNet
{ {
public class BroadcastheNet : TorrentIndexerBase<BroadcastheNetSettings> public class BroadcastheNet : HttpIndexerBase<BroadcastheNetSettings>
{ {
public override string Name => "BroadcasTheNet"; public override string Name => "BroadcasTheNet";
@@ -1,6 +1,5 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Net;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using FluentValidation.Results; using FluentValidation.Results;
@@ -8,7 +7,6 @@ using NLog;
using NzbDrone.Common.Cache; using NzbDrone.Common.Cache;
using NzbDrone.Common.Http; using NzbDrone.Common.Http;
using NzbDrone.Core.Configuration; using NzbDrone.Core.Configuration;
using NzbDrone.Core.Exceptions;
using NzbDrone.Core.IndexerVersions; using NzbDrone.Core.IndexerVersions;
using NzbDrone.Core.Messaging.Events; using NzbDrone.Core.Messaging.Events;
using NzbDrone.Core.ThingiProvider; using NzbDrone.Core.ThingiProvider;
@@ -16,7 +14,7 @@ using NzbDrone.Core.Validation;
namespace NzbDrone.Core.Indexers.Cardigann namespace NzbDrone.Core.Indexers.Cardigann
{ {
public class Cardigann : TorrentIndexerBase<CardigannSettings> public class Cardigann : HttpIndexerBase<CardigannSettings>
{ {
private readonly IIndexerDefinitionUpdateService _definitionService; private readonly IIndexerDefinitionUpdateService _definitionService;
private readonly ICached<CardigannRequestGenerator> _generatorCache; private readonly ICached<CardigannRequestGenerator> _generatorCache;
@@ -149,7 +147,6 @@ namespace NzbDrone.Core.Indexers.Cardigann
if (request.Url.Scheme == "magnet") if (request.Url.Scheme == "magnet")
{ {
ValidateMagnet(request.Url.FullUri);
return Encoding.UTF8.GetBytes(request.Url.FullUri); return Encoding.UTF8.GetBytes(request.Url.FullUri);
} }
@@ -162,36 +159,10 @@ namespace NzbDrone.Core.Indexers.Cardigann
var response = await _httpClient.ExecuteAsync(request); var response = await _httpClient.ExecuteAsync(request);
downloadBytes = response.ResponseData; downloadBytes = response.ResponseData;
} }
catch (HttpException ex)
{
if (ex.Response.StatusCode == HttpStatusCode.NotFound)
{
_logger.Error(ex, "Downloading torrent file for release failed since it no longer exists ({0})", request.Url.FullUri);
throw new ReleaseUnavailableException("Downloading torrent failed", ex);
}
if ((int)ex.Response.StatusCode == 429)
{
_logger.Error("API Grab Limit reached for {0}", request.Url.FullUri);
}
else
{
_logger.Error(ex, "Downloading torrent file for release failed ({0})", request.Url.FullUri);
}
throw new ReleaseDownloadException("Downloading torrent failed", ex);
}
catch (WebException ex)
{
_logger.Error(ex, "Downloading torrent file for release failed ({0})", request.Url.FullUri);
throw new ReleaseDownloadException("Downloading torrent failed", ex);
}
catch (Exception) catch (Exception)
{ {
_indexerStatusService.RecordFailure(Definition.Id); _indexerStatusService.RecordFailure(Definition.Id);
_logger.Error("Downloading torrent failed"); _logger.Error("Download failed");
throw;
} }
return downloadBytes; return downloadBytes;
@@ -21,7 +21,7 @@ using NzbDrone.Core.Validation;
namespace NzbDrone.Core.Indexers.Definitions namespace NzbDrone.Core.Indexers.Definitions
{ {
public class DigitalCore : TorrentIndexerBase<DigitalCoreSettings> public class DigitalCore : HttpIndexerBase<DigitalCoreSettings>
{ {
public override string Name => "DigitalCore"; public override string Name => "DigitalCore";
public override string BaseUrl => "https://digitalcore.club/"; public override string BaseUrl => "https://digitalcore.club/";
@@ -6,7 +6,7 @@ using NzbDrone.Core.Messaging.Events;
namespace NzbDrone.Core.Indexers.FileList namespace NzbDrone.Core.Indexers.FileList
{ {
public class FileList : TorrentIndexerBase<FileListSettings> public class FileList : HttpIndexerBase<FileListSettings>
{ {
public override string Name => "FileList.io"; public override string Name => "FileList.io";
public override string BaseUrl => "https://filelist.io"; public override string BaseUrl => "https://filelist.io";
@@ -7,7 +7,7 @@ using NzbDrone.Core.Messaging.Events;
namespace NzbDrone.Core.Indexers.Gazelle namespace NzbDrone.Core.Indexers.Gazelle
{ {
public abstract class Gazelle : TorrentIndexerBase<GazelleSettings> public abstract class Gazelle : HttpIndexerBase<GazelleSettings>
{ {
public override DownloadProtocol Protocol => DownloadProtocol.Torrent; public override DownloadProtocol Protocol => DownloadProtocol.Torrent;
public override string BaseUrl => ""; public override string BaseUrl => "";
@@ -6,7 +6,7 @@ using NzbDrone.Core.Messaging.Events;
namespace NzbDrone.Core.Indexers.HDBits namespace NzbDrone.Core.Indexers.HDBits
{ {
public class HDBits : TorrentIndexerBase<HDBitsSettings> public class HDBits : HttpIndexerBase<HDBitsSettings>
{ {
public override string Name => "HDBits"; public override string Name => "HDBits";
public override string BaseUrl => "https://hdbits.org"; public override string BaseUrl => "https://hdbits.org";
@@ -20,7 +20,7 @@ using NzbDrone.Core.Validation;
namespace NzbDrone.Core.Indexers.Definitions namespace NzbDrone.Core.Indexers.Definitions
{ {
public class HDTorrents : TorrentIndexerBase<HDTorrentsSettings> public class HDTorrents : HttpIndexerBase<HDTorrentsSettings>
{ {
public override string Name => "HD-Torrents"; public override string Name => "HD-Torrents";
@@ -6,12 +6,11 @@ using FluentValidation.Results;
using NLog; using NLog;
using NzbDrone.Common.Http; using NzbDrone.Common.Http;
using NzbDrone.Core.Configuration; using NzbDrone.Core.Configuration;
using NzbDrone.Core.Download;
using NzbDrone.Core.Messaging.Events; using NzbDrone.Core.Messaging.Events;
namespace NzbDrone.Core.Indexers.Headphones namespace NzbDrone.Core.Indexers.Headphones
{ {
public class Headphones : UsenetIndexerBase<HeadphonesSettings> public class Headphones : HttpIndexerBase<HeadphonesSettings>
{ {
public override string Name => "Headphones VIP"; public override string Name => "Headphones VIP";
@@ -36,8 +35,8 @@ namespace NzbDrone.Core.Indexers.Headphones
return new HeadphonesRssParser(Capabilities.Categories); return new HeadphonesRssParser(Capabilities.Categories);
} }
public Headphones(IHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, IValidateNzbs nzbValidationService, Logger logger) public Headphones(IHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger)
: base(httpClient, eventAggregator, indexerStatusService, configService, nzbValidationService, logger) : base(httpClient, eventAggregator, indexerStatusService, configService, logger)
{ {
} }
@@ -18,7 +18,7 @@ using NzbDrone.Core.Validation;
namespace NzbDrone.Core.Indexers.Definitions namespace NzbDrone.Core.Indexers.Definitions
{ {
public class IPTorrents : TorrentIndexerBase<IPTorrentsSettings> public class IPTorrents : HttpIndexerBase<IPTorrentsSettings>
{ {
public override string Name => "IPTorrents"; public override string Name => "IPTorrents";
@@ -21,7 +21,7 @@ using NzbDrone.Core.Validation;
namespace NzbDrone.Core.Indexers.Definitions namespace NzbDrone.Core.Indexers.Definitions
{ {
public class ImmortalSeed : TorrentIndexerBase<ImmortalSeedSettings> public class ImmortalSeed : HttpIndexerBase<ImmortalSeedSettings>
{ {
public override string Name => "ImmortalSeed"; public override string Name => "ImmortalSeed";
@@ -16,7 +16,7 @@ using NzbDrone.Core.Validation;
namespace NzbDrone.Core.Indexers.Definitions namespace NzbDrone.Core.Indexers.Definitions
{ {
public class Milkie : TorrentIndexerBase<MilkieSettings> public class Milkie : HttpIndexerBase<MilkieSettings>
{ {
public override string Name => "Milkie"; public override string Name => "Milkie";
@@ -18,7 +18,7 @@ using NzbDrone.Core.Validation;
namespace NzbDrone.Core.Indexers.Definitions namespace NzbDrone.Core.Indexers.Definitions
{ {
public class MyAnonamouse : TorrentIndexerBase<MyAnonamouseSettings> public class MyAnonamouse : HttpIndexerBase<MyAnonamouseSettings>
{ {
public override string Name => "MyAnonamouse"; public override string Name => "MyAnonamouse";
@@ -7,14 +7,13 @@ using NLog;
using NzbDrone.Common.Extensions; using NzbDrone.Common.Extensions;
using NzbDrone.Common.Http; using NzbDrone.Common.Http;
using NzbDrone.Core.Configuration; using NzbDrone.Core.Configuration;
using NzbDrone.Core.Download;
using NzbDrone.Core.Messaging.Events; using NzbDrone.Core.Messaging.Events;
using NzbDrone.Core.ThingiProvider; using NzbDrone.Core.ThingiProvider;
using NzbDrone.Core.Validation; using NzbDrone.Core.Validation;
namespace NzbDrone.Core.Indexers.Newznab namespace NzbDrone.Core.Indexers.Newznab
{ {
public class Newznab : UsenetIndexerBase<NewznabSettings> public class Newznab : HttpIndexerBase<NewznabSettings>
{ {
private readonly INewznabCapabilitiesProvider _capabilitiesProvider; private readonly INewznabCapabilitiesProvider _capabilitiesProvider;
@@ -109,8 +108,8 @@ namespace NzbDrone.Core.Indexers.Newznab
} }
} }
public Newznab(INewznabCapabilitiesProvider capabilitiesProvider, IHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, IValidateNzbs nzbValidationService, Logger logger) public Newznab(INewznabCapabilitiesProvider capabilitiesProvider, IHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger)
: base(httpClient, eventAggregator, indexerStatusService, configService, nzbValidationService, logger) : base(httpClient, eventAggregator, indexerStatusService, configService, logger)
{ {
_capabilitiesProvider = capabilitiesProvider; _capabilitiesProvider = capabilitiesProvider;
} }
@@ -7,7 +7,7 @@ using NzbDrone.Core.Messaging.Events;
namespace NzbDrone.Core.Indexers.PassThePopcorn namespace NzbDrone.Core.Indexers.PassThePopcorn
{ {
public class PassThePopcorn : TorrentIndexerBase<PassThePopcornSettings> public class PassThePopcorn : HttpIndexerBase<PassThePopcornSettings>
{ {
public override string Name => "PassThePopcorn"; public override string Name => "PassThePopcorn";
public override string BaseUrl => "https://passthepopcorn.me"; public override string BaseUrl => "https://passthepopcorn.me";
@@ -21,7 +21,7 @@ using NzbDrone.Core.Validation;
namespace NzbDrone.Core.Indexers.Definitions namespace NzbDrone.Core.Indexers.Definitions
{ {
public class PreToMe : TorrentIndexerBase<PreToMeSettings> public class PreToMe : HttpIndexerBase<PreToMeSettings>
{ {
public override string Name => "PreToMe"; public override string Name => "PreToMe";
public override string BaseUrl => "https://pretome.info/"; public override string BaseUrl => "https://pretome.info/";
@@ -11,7 +11,7 @@ using NzbDrone.Core.Validation;
namespace NzbDrone.Core.Indexers.Rarbg namespace NzbDrone.Core.Indexers.Rarbg
{ {
public class Rarbg : TorrentIndexerBase<RarbgSettings> public class Rarbg : HttpIndexerBase<RarbgSettings>
{ {
private readonly IRarbgTokenProvider _tokenProvider; private readonly IRarbgTokenProvider _tokenProvider;
@@ -21,7 +21,7 @@ using NzbDrone.Core.Validation;
namespace NzbDrone.Core.Indexers.Definitions namespace NzbDrone.Core.Indexers.Definitions
{ {
public class RevolutionTT : TorrentIndexerBase<RevolutionTTSettings> public class RevolutionTT : HttpIndexerBase<RevolutionTTSettings>
{ {
public override string Name => "RevolutionTT"; public override string Name => "RevolutionTT";
@@ -18,7 +18,7 @@ using NzbDrone.Core.Validation;
namespace NzbDrone.Core.Indexers.Definitions namespace NzbDrone.Core.Indexers.Definitions
{ {
public class SubsPlease : TorrentIndexerBase<SubsPleaseSettings> public class SubsPlease : HttpIndexerBase<SubsPleaseSettings>
{ {
public override string Name => "SubsPlease"; public override string Name => "SubsPlease";
public override string BaseUrl => "https://subsplease.org/"; public override string BaseUrl => "https://subsplease.org/";
@@ -19,7 +19,7 @@ using NzbDrone.Core.Validation;
namespace NzbDrone.Core.Indexers.Definitions namespace NzbDrone.Core.Indexers.Definitions
{ {
public class SuperBits : TorrentIndexerBase<SuperBitsSettings> public class SuperBits : HttpIndexerBase<SuperBitsSettings>
{ {
public override string Name => "SuperBits"; public override string Name => "SuperBits";
@@ -18,7 +18,7 @@ using NzbDrone.Core.Validation;
namespace NzbDrone.Core.Indexers.Definitions namespace NzbDrone.Core.Indexers.Definitions
{ {
public class ThePirateBay : TorrentIndexerBase<ThePirateBaySettings> public class ThePirateBay : HttpIndexerBase<ThePirateBaySettings>
{ {
public override string Name => "ThePirateBay"; public override string Name => "ThePirateBay";
public override string BaseUrl => "https://thepiratebay.org/"; public override string BaseUrl => "https://thepiratebay.org/";
@@ -17,7 +17,7 @@ using NzbDrone.Core.Validation;
namespace NzbDrone.Core.Indexers.Definitions namespace NzbDrone.Core.Indexers.Definitions
{ {
public class TorrentDay : TorrentIndexerBase<TorrentDaySettings> public class TorrentDay : HttpIndexerBase<TorrentDaySettings>
{ {
public override string Name => "TorrentDay"; public override string Name => "TorrentDay";
@@ -21,7 +21,7 @@ using NzbDrone.Core.Validation;
namespace NzbDrone.Core.Indexers.Definitions namespace NzbDrone.Core.Indexers.Definitions
{ {
public class TorrentLeech : TorrentIndexerBase<TorrentLeechSettings> public class TorrentLeech : HttpIndexerBase<TorrentLeechSettings>
{ {
public override string Name => "TorrentLeech"; public override string Name => "TorrentLeech";
@@ -6,7 +6,7 @@ using NzbDrone.Core.Messaging.Events;
namespace NzbDrone.Core.Indexers.TorrentPotato namespace NzbDrone.Core.Indexers.TorrentPotato
{ {
public class TorrentPotato : TorrentIndexerBase<TorrentPotatoSettings> public class TorrentPotato : HttpIndexerBase<TorrentPotatoSettings>
{ {
public override string Name => "TorrentPotato"; public override string Name => "TorrentPotato";
public override string BaseUrl => "http://127.0.0.1"; public override string BaseUrl => "http://127.0.0.1";
@@ -20,7 +20,7 @@ using NzbDrone.Core.Validation;
namespace NzbDrone.Core.Indexers.Definitions namespace NzbDrone.Core.Indexers.Definitions
{ {
public class TorrentSeeds : TorrentIndexerBase<TorrentSeedsSettings> public class TorrentSeeds : HttpIndexerBase<TorrentSeedsSettings>
{ {
public override string Name => "TorrentSeeds"; public override string Name => "TorrentSeeds";
@@ -14,7 +14,7 @@ using NzbDrone.Core.Validation;
namespace NzbDrone.Core.Indexers.Torznab namespace NzbDrone.Core.Indexers.Torznab
{ {
public class Torznab : TorrentIndexerBase<TorznabSettings> public class Torznab : HttpIndexerBase<TorznabSettings>
{ {
private readonly INewznabCapabilitiesProvider _capabilitiesProvider; private readonly INewznabCapabilitiesProvider _capabilitiesProvider;
@@ -5,7 +5,7 @@ using NzbDrone.Core.Messaging.Events;
namespace NzbDrone.Core.Indexers.Definitions.UNIT3D namespace NzbDrone.Core.Indexers.Definitions.UNIT3D
{ {
public abstract class Unit3dBase : TorrentIndexerBase<Unit3dSettings> public abstract class Unit3dBase : HttpIndexerBase<Unit3dSettings>
{ {
public override DownloadProtocol Protocol => DownloadProtocol.Torrent; public override DownloadProtocol Protocol => DownloadProtocol.Torrent;
public override string BaseUrl => ""; public override string BaseUrl => "";
@@ -25,7 +25,7 @@ using NzbDrone.Core.Validation;
namespace NzbDrone.Core.Indexers.Definitions namespace NzbDrone.Core.Indexers.Definitions
{ {
public class ZonaQ : TorrentIndexerBase<ZonaQSettings> public class ZonaQ : HttpIndexerBase<ZonaQSettings>
{ {
public override string Name => "ZonaQ"; public override string Name => "ZonaQ";
public override string BaseUrl => "https://www.zonaq.pw/"; public override string BaseUrl => "https://www.zonaq.pw/";
@@ -6,7 +6,7 @@ using NzbDrone.Common.Extensions;
using NzbDrone.Core.Configuration; using NzbDrone.Core.Configuration;
using NzbDrone.Core.Security; using NzbDrone.Core.Security;
namespace NzbDrone.Core.Download namespace NzbDrone.Core.Indexers
{ {
public interface IDownloadMappingService public interface IDownloadMappingService
{ {
@@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Net; using System.Net;
using System.Text; using System.Text;
@@ -9,6 +10,7 @@ using NLog;
using NzbDrone.Common.Extensions; using NzbDrone.Common.Extensions;
using NzbDrone.Common.Http; using NzbDrone.Common.Http;
using NzbDrone.Core.Configuration; using NzbDrone.Core.Configuration;
using NzbDrone.Core.Exceptions;
using NzbDrone.Core.Http.CloudFlare; using NzbDrone.Core.Http.CloudFlare;
using NzbDrone.Core.Indexers.Events; using NzbDrone.Core.Indexers.Events;
using NzbDrone.Core.Indexers.Exceptions; using NzbDrone.Core.Indexers.Exceptions;
@@ -103,6 +105,42 @@ namespace NzbDrone.Core.Indexers
return FetchReleases(g => SetCookieFunctions(g).GetSearchRequests(searchCriteria)); return FetchReleases(g => SetCookieFunctions(g).GetSearchRequests(searchCriteria));
} }
public override async Task<byte[]> Download(Uri link)
{
Cookies = GetCookies();
if (link.Scheme == "magnet")
{
return Encoding.UTF8.GetBytes(link.OriginalString);
}
var requestBuilder = new HttpRequestBuilder(link.AbsoluteUri);
if (Cookies != null)
{
requestBuilder.SetCookies(Cookies);
}
var request = requestBuilder.Build();
request.AllowAutoRedirect = FollowRedirect;
var downloadBytes = Array.Empty<byte>();
try
{
var response = await _httpClient.ExecuteAsync(request);
downloadBytes = response.ResponseData;
}
catch (Exception)
{
_indexerStatusService.RecordFailure(Definition.Id);
_logger.Error("Download failed");
throw;
}
return downloadBytes;
}
protected IIndexerRequestGenerator SetCookieFunctions(IIndexerRequestGenerator generator) protected IIndexerRequestGenerator SetCookieFunctions(IIndexerRequestGenerator generator)
{ {
//A func ensures cookies are always updated to the latest. This way, the first page could update the cookies and then can be reused by the second page. //A func ensures cookies are always updated to the latest. This way, the first page could update the cookies and then can be reused by the second page.
+1 -1
View File
@@ -77,7 +77,7 @@ namespace NzbDrone.Core.Indexers
public abstract Task<IndexerPageableQueryResult> Fetch(TvSearchCriteria searchCriteria); public abstract Task<IndexerPageableQueryResult> Fetch(TvSearchCriteria searchCriteria);
public abstract Task<IndexerPageableQueryResult> Fetch(BookSearchCriteria searchCriteria); public abstract Task<IndexerPageableQueryResult> Fetch(BookSearchCriteria searchCriteria);
public abstract Task<IndexerPageableQueryResult> Fetch(BasicSearchCriteria searchCriteria); public abstract Task<IndexerPageableQueryResult> Fetch(BasicSearchCriteria searchCriteria);
public abstract Task<byte[]> Download(Uri link); public abstract Task<byte[]> Download(Uri searchCriteria);
public abstract IndexerCapabilities GetCapabilities(); public abstract IndexerCapabilities GetCapabilities();
@@ -1,90 +0,0 @@
using System;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using MonoTorrent;
using NLog;
using NzbDrone.Common.Http;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.Exceptions;
using NzbDrone.Core.Messaging.Events;
using NzbDrone.Core.ThingiProvider;
namespace NzbDrone.Core.Indexers
{
public abstract class TorrentIndexerBase<TSettings> : HttpIndexerBase<TSettings>
where TSettings : IProviderConfig, new()
{
protected TorrentIndexerBase(IHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger)
: base(httpClient, eventAggregator, indexerStatusService, configService, logger)
{
}
public override async Task<byte[]> Download(Uri link)
{
Cookies = GetCookies();
if (link.Scheme == "magnet")
{
ValidateMagnet(link.OriginalString);
return Encoding.UTF8.GetBytes(link.OriginalString);
}
var requestBuilder = new HttpRequestBuilder(link.AbsoluteUri);
if (Cookies != null)
{
requestBuilder.SetCookies(Cookies);
}
var request = requestBuilder.Build();
request.AllowAutoRedirect = FollowRedirect;
byte[] torrentData;
try
{
var response = await _httpClient.ExecuteAsync(request);
torrentData = response.ResponseData;
}
catch (HttpException ex)
{
if (ex.Response.StatusCode == HttpStatusCode.NotFound)
{
_logger.Error(ex, "Downloading torrent file for release failed since it no longer exists ({0})", link.AbsoluteUri);
throw new ReleaseUnavailableException("Downloading torrent failed", ex);
}
if ((int)ex.Response.StatusCode == 429)
{
_logger.Error("API Grab Limit reached for {0}", link.AbsoluteUri);
}
else
{
_logger.Error(ex, "Downloading torrent file for release failed ({0})", link.AbsoluteUri);
}
throw new ReleaseDownloadException("Downloading torrent failed", ex);
}
catch (WebException ex)
{
_logger.Error(ex, "Downloading torrent file for release failed ({0})", link.AbsoluteUri);
throw new ReleaseDownloadException("Downloading torrent failed", ex);
}
catch (Exception)
{
_indexerStatusService.RecordFailure(Definition.Id);
_logger.Error("Downloading torrent failed");
throw;
}
return torrentData;
}
protected void ValidateMagnet(string link)
{
MagnetLink.Parse(link);
}
}
}
@@ -1,85 +0,0 @@
using System;
using System.Net;
using System.Threading.Tasks;
using NLog;
using NzbDrone.Common.Http;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.Download;
using NzbDrone.Core.Exceptions;
using NzbDrone.Core.Messaging.Events;
using NzbDrone.Core.ThingiProvider;
namespace NzbDrone.Core.Indexers
{
public abstract class UsenetIndexerBase<TSettings> : HttpIndexerBase<TSettings>
where TSettings : IProviderConfig, new()
{
private readonly IValidateNzbs _nzbValidationService;
protected UsenetIndexerBase(IHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, IValidateNzbs nzbValidationService, Logger logger)
: base(httpClient, eventAggregator, indexerStatusService, configService, logger)
{
_nzbValidationService = nzbValidationService;
}
public override async Task<byte[]> Download(Uri link)
{
Cookies = GetCookies();
var requestBuilder = new HttpRequestBuilder(link.AbsoluteUri);
if (Cookies != null)
{
requestBuilder.SetCookies(Cookies);
}
var request = requestBuilder.Build();
request.AllowAutoRedirect = FollowRedirect;
byte[] nzbData;
try
{
var response = await _httpClient.ExecuteAsync(request);
nzbData = response.ResponseData;
_logger.Debug("Downloaded nzb for release finished ({0} bytes from {1})", nzbData.Length, link.AbsoluteUri);
}
catch (HttpException ex)
{
if (ex.Response.StatusCode == HttpStatusCode.NotFound)
{
_logger.Error(ex, "Downloading nzb file for release failed since it no longer exists ({0})", link.AbsoluteUri);
throw new ReleaseUnavailableException("Downloading nzb failed", ex);
}
if ((int)ex.Response.StatusCode == 429)
{
_logger.Error("API Grab Limit reached for {0}", link.AbsoluteUri);
}
else
{
_logger.Error(ex, "Downloading nzb for release failed ({0})", link.AbsoluteUri);
}
throw new ReleaseDownloadException("Downloading nzb failed", ex);
}
catch (WebException ex)
{
_logger.Error(ex, "Downloading nzb for release failed ({0})", link.AbsoluteUri);
throw new ReleaseDownloadException("Downloading nzb failed", ex);
}
catch (Exception)
{
_indexerStatusService.RecordFailure(Definition.Id);
_logger.Error("Downloading nzb failed");
throw;
}
_nzbValidationService.Validate(nzbData);
return nzbData;
}
}
}
+1 -7
View File
@@ -2,6 +2,7 @@
"About": "About", "About": "About",
"AcceptConfirmationModal": "Accept Confirmation Modal", "AcceptConfirmationModal": "Accept Confirmation Modal",
"Actions": "Actions", "Actions": "Actions",
"AppProfile": "App Profile",
"AddAppProfile": "Add App Sync Profile", "AddAppProfile": "Add App Sync Profile",
"Added": "Added", "Added": "Added",
"AddedToDownloadClient": "Release added to client", "AddedToDownloadClient": "Release added to client",
@@ -25,14 +26,11 @@
"ApplyTagsHelpTexts2": "Add: Add the tags the existing list of tags", "ApplyTagsHelpTexts2": "Add: Add the tags the existing list of tags",
"ApplyTagsHelpTexts3": "Remove: Remove the entered tags", "ApplyTagsHelpTexts3": "Remove: Remove the entered tags",
"ApplyTagsHelpTexts4": "Replace: Replace the tags with the entered tags (enter no tags to clear all tags)", "ApplyTagsHelpTexts4": "Replace: Replace the tags with the entered tags (enter no tags to clear all tags)",
"AppProfile": "App Profile",
"AppProfiles": "App Profiles", "AppProfiles": "App Profiles",
"AppProfileSelectHelpText": "App profiles are used to control RSS, Automatic Search and Interactive Search settings on application sync",
"AreYouSureYouWantToResetYourAPIKey": "Are you sure you want to reset your API Key?", "AreYouSureYouWantToResetYourAPIKey": "Are you sure you want to reset your API Key?",
"Authentication": "Authentication", "Authentication": "Authentication",
"AuthenticationMethodHelpText": "Require Username and Password to access Prowlarr", "AuthenticationMethodHelpText": "Require Username and Password to access Prowlarr",
"Automatic": "Automatic", "Automatic": "Automatic",
"AutomaticSearch": "Automatic Search",
"Backup": "Backup", "Backup": "Backup",
"BackupFolderHelpText": "Relative paths will be under Prowlarr's AppData directory", "BackupFolderHelpText": "Relative paths will be under Prowlarr's AppData directory",
"BackupIntervalHelpText": "Interval between automatic backups", "BackupIntervalHelpText": "Interval between automatic backups",
@@ -165,7 +163,6 @@
"IndexerStatusCheckAllClientMessage": "All indexers are unavailable due to failures", "IndexerStatusCheckAllClientMessage": "All indexers are unavailable due to failures",
"IndexerStatusCheckSingleClientMessage": "Indexers unavailable due to failures: {0}", "IndexerStatusCheckSingleClientMessage": "Indexers unavailable due to failures: {0}",
"Info": "Info", "Info": "Info",
"InteractiveSearch": "Interactive Search",
"Interval": "Interval", "Interval": "Interval",
"KeyboardShortcuts": "Keyboard Shortcuts", "KeyboardShortcuts": "Keyboard Shortcuts",
"Language": "Language", "Language": "Language",
@@ -250,8 +247,6 @@
"QualitySettings": "Quality Settings", "QualitySettings": "Quality Settings",
"Queue": "Queue", "Queue": "Queue",
"ReadTheWikiForMoreInformation": "Read the Wiki for more information", "ReadTheWikiForMoreInformation": "Read the Wiki for more information",
"Redirect": "Redirect",
"RedirectHelpText": "Redirect incoming download requests for indexer instead of Proxying using Prowlarr",
"Refresh": "Refresh", "Refresh": "Refresh",
"RefreshMovie": "Refresh movie", "RefreshMovie": "Refresh movie",
"ReleaseBranchCheckOfficialBranchMessage": "Branch {0} is not a valid Prowlarr release branch, you will not receive updates", "ReleaseBranchCheckOfficialBranchMessage": "Branch {0} is not a valid Prowlarr release branch, you will not receive updates",
@@ -272,7 +267,6 @@
"Restrictions": "Restrictions", "Restrictions": "Restrictions",
"Result": "Result", "Result": "Result",
"Retention": "Retention", "Retention": "Retention",
"RSS": "RSS",
"RSSIsNotSupportedWithThisIndexer": "RSS is not supported with this indexer", "RSSIsNotSupportedWithThisIndexer": "RSS is not supported with this indexer",
"Save": "Save", "Save": "Save",
"SaveChanges": "Save Changes", "SaveChanges": "Save Changes",
@@ -0,0 +1,32 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace NzbDrone.Core.Migration
{
public class JackettIndexerConfigDefintion
{
public string id { get; set; }
public string type { get; set; } //DO NOT THINK THIS IS REQUIRED
public string name { get; set; } //Again doubt it's needed
public string value { get; set; }
//BELOW ARE THE DIFFERENT THINGS FROM NIT
//TODO: Update to ensure json matches up with Prowlarrs
public string Cookie { get; set; }
public string Username { get; set; }
public string 2FA { get; set; }
public string Email { get; set; }
public string Pid { get; set; }
public string Pin { get; set; }
public string Password { get; set; }
public string Passkey { get; set; }
public string ApiKey { get; set; }
public string RssKey { get; set; }
public string CaptchaText { get; set; }
public string CaptchaCookie { get; set; }
public string Filters { get; set; }
}
}
@@ -0,0 +1,20 @@
using System.Collections.Generic;
using Newtonsoft.Json;
namespace NzbDrone.Core.Migration
{
public class JackettIndexerDefinition
{
public string Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public string Type { get; set; }
public string Configured { get; set; }
[JsonProperty("site_link")]
public string SiteLink { get; set; }
public List<string> AlternativeSiteLinks { get; set; }
public string language { get; set; }
//ignoring last_error, potatoenabled, caps
}
}
@@ -0,0 +1,81 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Net.Http;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using NLog;
using NzbDrone.Common.Http;
using NzbDrone.Core.Indexers;
namespace NzbDrone.Core.Migration
{
public interface IJackettMigrationService
{
List<JackettIndexerDefinition> GetJackettIndexers(string jackettPath, string jackettApi);
List<JackettIndexerConfigDefintion> GetJackettIndexerConfig(JackettIndexerDefinition jackettIndexerDefinition, string jackettPath, string jackettApi);
void MigrateJackettIndexer(JackettIndexerDefinition jackettIndexer, List<JackettIndexerConfigDefintion> jackettIndexerConfig);
}
public class JackettMigrationService : IJackettMigrationService
{
private readonly IIndexerFactory _indexerFactory;
private readonly Logger _logger;
public IHttpClient HttpClient { get; set; }
public IHttpRequestBuilderFactory RequestBuilder { get; set; }
public JackettMigrationService(IIndexerFactory indexerFactory, Logger logger)
{
_logger = logger;
_indexerFactory = indexerFactory;
}
public List<JackettIndexerDefinition> GetJackettIndexers(string jackettPath, string jackettApi)
{
_logger.Info("Getting all configured Jackett Indexers");
var requestBuilder = BuildRequest(jackettPath, jackettApi, true);
var jsonResponse = JsonConvert.DeserializeObject<List<JackettIndexerDefinition>>(HttpClient.Execute(requestBuilder.Build()).Content);
return jsonResponse;
}
public List<JackettIndexerConfigDefintion> GetJackettIndexerConfig(JackettIndexerDefinition jackettIndexerDefinition, string jackettPath, string jackettApi)
{
_logger.Debug($"Getting config from Jackett for {jackettIndexerDefinition.Name}");
var requestBuilder = BuildRequest(jackettPath, jackettApi, jackettIndexerDefinition.Id);
var jsonResponse = JsonConvert.DeserializeObject<List<JackettIndexerConfigDefintion>>(HttpClient.Execute(requestBuilder.Build()).Content);
return jsonResponse;
}
public void MigrateJackettIndexer(JackettIndexerDefinition jackettIndexer, List<JackettIndexerConfigDefintion> jackettIndexerConfig)
{
_logger.Info($"Creating {jackettIndexer.Name} in Prowlarr");
//TODO: Creation Logic of indexers
}
protected HttpRequestBuilder BuildRequest(string jackettPath, string jackettApi, bool configuredIndexers)
{
var url = jackettPath + "api/v2.0/indexers";
var requestBuilder = new HttpRequestBuilder(url);
requestBuilder.AddQueryParam("apiKey", jackettApi);
requestBuilder.AddQueryParam("configured", configuredIndexers);
return requestBuilder;
}
protected HttpRequestBuilder BuildRequest(string jackettPath, string jackettApi, string indexerId)
{
var url = jackettPath + "api/v2.0/indexers/" + indexerId + "/config";
var requestBuilder = new HttpRequestBuilder(url);
requestBuilder.AddQueryParam("apiKey", jackettApi);
return requestBuilder;
}
}
}
@@ -27,7 +27,7 @@ namespace NzbDrone.Core.Notifications.CustomScript
public override string Name => "Custom Script"; public override string Name => "Custom Script";
public override string Link => "https://wikijs.servarr.com/prowlarr/settings#connections"; public override string Link => "https://wiki.servarr.com/Prowlarr_Settings#Connections";
public override ProviderMessage Message => new ProviderMessage("Testing will execute the script with the EventType set to Test, ensure your script handles this correctly", ProviderMessageType.Warning); public override ProviderMessage Message => new ProviderMessage("Testing will execute the script with the EventType set to Test, ensure your script handles this correctly", ProviderMessageType.Warning);
@@ -38,10 +38,10 @@ namespace NzbDrone.Core.Notifications.Twitter
AuthorizeNotification = "startOAuth"; AuthorizeNotification = "startOAuth";
} }
[FieldDefinition(0, Label = "Consumer Key", Privacy = PrivacyLevel.ApiKey, HelpText = "Consumer key from a Twitter application", HelpLink = "https://wikijs.servarr.com/useful-tools#twitter-connect")] [FieldDefinition(0, Label = "Consumer Key", Privacy = PrivacyLevel.ApiKey, HelpText = "Consumer key from a Twitter application", HelpLink = "https://wiki.servarr.com/Useful_Tools#Twitter_Connect")]
public string ConsumerKey { get; set; } public string ConsumerKey { get; set; }
[FieldDefinition(1, Label = "Consumer Secret", Privacy = PrivacyLevel.ApiKey, HelpText = "Consumer secret from a Twitter application", HelpLink = "https://wikijs.servarr.com/useful-tools#twitter-connect")] [FieldDefinition(1, Label = "Consumer Secret", Privacy = PrivacyLevel.ApiKey, HelpText = "Consumer secret from a Twitter application", HelpLink = "https://wiki.servarr.com/Useful_Tools#Twitter_Connect")]
public string ConsumerSecret { get; set; } public string ConsumerSecret { get; set; }
[FieldDefinition(2, Label = "Access Token", Privacy = PrivacyLevel.ApiKey, Advanced = true)] [FieldDefinition(2, Label = "Access Token", Privacy = PrivacyLevel.ApiKey, Advanced = true)]
@@ -14,7 +14,7 @@ namespace NzbDrone.Core.Notifications.Webhook
_proxy = proxy; _proxy = proxy;
} }
public override string Link => "https://wikijs.servarr.com/prowlarr/settings#connect"; public override string Link => "https://wiki.servarr.com/Prowlarr_Settings#Connect";
public override void OnHealthIssue(HealthCheck.HealthCheck healthCheck) public override void OnHealthIssue(HealthCheck.HealthCheck healthCheck)
{ {
@@ -0,0 +1,32 @@
using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNetCore.Mvc;
using NzbDrone.Core.Migration;
using Prowlarr.Http;
namespace Prowlarr.Api.V1.Migration
{
[V1ApiController]
public class MigrationController : Controller
{
private readonly IJackettMigrationService _jackettMigrationService;
public MigrationController(IJackettMigrationService jackettMigrationService)
{
_jackettMigrationService = jackettMigrationService;
}
//TODO: Make this work with the frontend (e.g. passing params for jackettPath and jackettApi
[HttpPost("jackettmigration")]
public void JackettMigration()
{
var jackettIndexers = _jackettMigrationService.GetJackettIndexers(jackettPath, jackettApi);
foreach (var jackettIndexer in jackettIndexers)
{
var indexerconfig = _jackettMigrationService.GetJackettIndexerConfig(jackettIndexer, jackettPath, jackettApi);
_jackettMigrationService.MigrateJackettIndexer(jackettIndexer, indexerconfig);
}
}
}
}
@@ -0,0 +1,4 @@
namespace Prowlarr.Api.V1.Migration
{
}
+1 -1
View File
@@ -39,7 +39,7 @@ namespace Prowlarr.Api.V1
Fields = SchemaBuilder.ToSchema(definition.Settings), Fields = SchemaBuilder.ToSchema(definition.Settings),
//Radarr_Supported_{0} are custom build redirect pages; if passing a new var, create a new redirect //Radarr_Supported_{0} are custom build redirect pages; if passing a new var, create a new redirect
InfoLink = string.Format("https://wikijs.servarr.com/prowlarr/supported_{0}", InfoLink = string.Format("https://wiki.servarr.com/Prowlarr_Supported_{0}",
definition.Implementation.ToLower()) definition.Implementation.ToLower())
}; };
} }
@@ -97,15 +97,7 @@ namespace Prowlarr.Api.V1.Search
{ {
try try
{ {
var request = new NewznabRequest var request = new NewznabRequest { q = query, source = "Prowlarr", t = "search", cat = string.Join(",", categories), server = Request.GetServerUrl() };
{
q = query, source = "Prowlarr",
t = "search",
cat = string.Join(",", categories),
server = Request.GetServerUrl(),
host = Request.GetHostName()
};
var result = await _nzbSearhService.Search(request, indexerIds, true); var result = await _nzbSearhService.Search(request, indexerIds, true);
var decisions = result.Releases; var decisions = result.Releases;
+2 -2
View File
@@ -766,13 +766,13 @@
"source": "ImportMechanismCheck", "source": "ImportMechanismCheck",
"type": "warning", "type": "warning",
"message": "Enable Completed Download Handling", "message": "Enable Completed Download Handling",
"wikiUrl": "https://wikijs.servarr.com/prowlarr/system#completed.2FFailed_Download_Handling" "wikiUrl": "https://wiki.servarr.com/Prowlarr_System#Completed.2FFailed_Download_Handling"
}, },
{ {
"source": "DownloadClientCheck", "source": "DownloadClientCheck",
"type": "error", "type": "error",
"message": "Unable to communicate with qBittorrent. Failed to connect to qBittorrent, check your settings.", "message": "Unable to communicate with qBittorrent. Failed to connect to qBittorrent, check your settings.",
"wikiUrl": "https://wikijs.servarr.com/prowlarr/system#download-clients" "wikiUrl": "https://wiki.servarr.com/Prowlarr_System#Download_Clients"
} }
] ]
} }

Some files were not shown because too many files have changed in this diff Show More