Compare commits

...

10 Commits

Author SHA1 Message Date
Robin Dadswell
b3fb640969 New: App Profile help text on addition and edit of Indexers and other misc translations 2021-06-05 21:58:53 +01:00
Qstick
8b0a8e82b5 Wikijs Links 2021-06-05 16:37:49 -04:00
Robin Dadswell
f25998959e New: Added all Arr donation links 2021-06-05 21:15:06 +01:00
Qstick
f56ce129e6 Fixed: Push to client for Cardigann magnet links 2021-06-05 14:55:43 -04:00
Qstick
ae9930a03f Update ZonaQ.cs 2021-06-05 10:20:54 -04:00
Qstick
85be0be455 Fixed: Push Downloads to client fails for download overrides 2021-06-05 09:32:40 -04:00
Qstick
cf1c44ed75 Fixed: Normalize definitions when serving local and remote 2021-06-04 19:27:50 -04:00
Robin Dadswell
f062fafe82 Fixed: Error when trying to parse the value 'Unknown' as an IP Address 2021-06-05 00:18:12 +01:00
Qstick
1032d8b3ab Fixed: Slash on ProwlarrURL causes App failures 2021-06-04 19:13:01 -04:00
Qstick
99c4ed7dbc Fixed: Host not set for UI Search 2021-06-03 19:40:53 -04:00
96 changed files with 906 additions and 321 deletions

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)
[![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://wiki.servarr.com/Prowlarr_Installation#Docker)
[![Docker Pulls](https://img.shields.io/docker/pulls/hotio/prowlarr.svg)](https://wikijs.servarr.com/prowlarr/installation#docker)
![Github Downloads](https://img.shields.io/github/downloads/Prowlarr/Prowlarr/total.svg)
[![Backers on Open Collective](https://opencollective.com/Prowlarr/backers/badge.svg)](#backers)
[![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)
[![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)
[![Wiki](https://img.shields.io/badge/servarr-wiki-181717.svg?maxAge=60)](https://wiki.servarr.com/Prowlarr)
[![Wiki](https://img.shields.io/badge/servarr-wiki-181717.svg?maxAge=60)](https://wikijs.servarr.com/prowlarr)
## Feature Requests

View File

@@ -129,7 +129,7 @@ class FileBrowserModalContent extends Component {
className={styles.mappedDrivesWarning}
kind={kinds.WARNING}
>
<Link to="https://wiki.servarr.com/Prowlarr_FAQ#Why_cant_Prowlarr_see_my_files_on_a_remote_server">
<Link to="https://wikijs.servarr.com/prowlarr/faq#why-cant-prowlarr-see-my-files-on-a-remote-server">
{translate('MappedDrivesRunningAsService')}
</Link>
</Alert>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@@ -99,7 +99,7 @@ function EditIndexerModalContent(props) {
<FormInputGroup
type={inputTypes.CHECK}
name="redirect"
helpText={'Redirect incoming download requests for indexer instead of Proxying using Prowlarr'}
helpText={translate('RedirectHelpText')}
isDisabled={!supportsRedirect.value}
{...redirect}
onChange={onInputChange}
@@ -113,6 +113,7 @@ function EditIndexerModalContent(props) {
type={inputTypes.APP_PROFILE_SELECT}
name="appProfileId"
{...appProfileId}
helpText={translate('AppProfileSelectHelpText')}
onChange={onInputChange}
/>
</FormGroup>

View File

@@ -55,7 +55,7 @@ function UpdateSettings(props) {
type={inputTypes.TEXT}
name="branch"
helpText={usingExternalUpdateMechanism ? translate('BranchUpdateMechanism') : translate('BranchUpdate')}
helpLink="https://wiki.servarr.com/Prowlarr_Settings#Updates"
helpLink="https://wikijs.servarr.com/prowlarr/settings#updates"
{...branch}
onChange={onInputChange}
readOnly={usingExternalUpdateMechanism}
@@ -92,7 +92,7 @@ function UpdateSettings(props) {
name="updateMechanism"
values={updateOptions}
helpText={translate('UpdateMechanismHelpText')}
helpLink="https://wiki.servarr.com/Prowlarr_Settings#Updates"
helpLink="https://wikijs.servarr.com/prowlarr/settings#updates"
onChange={onInputChange}
{...updateMechanism}
/>

View File

@@ -26,7 +26,7 @@ function NotificationEventItems(props) {
<div>
<FormInputHelpText
text={translate('NotifcationTriggersHelpText')}
link="https://wiki.servarr.com/Prowlarr_Settings#Connections"
link="https://wikijs.servarr.com/prowlarr/settings#connections"
/>
<div className={styles.events}>
<div>

View File

@@ -13,7 +13,7 @@ function createHealthCheckSelector() {
source: 'UI',
type: 'warning',
message: translate('CouldNotConnectSignalR'),
wikiUrl: 'https://wiki.servarr.com/Prowlarr_System#Could_not_connect_to_signalR'
wikiUrl: 'https://wikijs.servarr.com/prowlarr/system#could-not-connect-to-signalr'
});
}

View File

@@ -0,0 +1,64 @@
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;

View File

@@ -27,7 +27,7 @@ class MoreInfo extends Component {
<DescriptionListItemTitle>Wiki</DescriptionListItemTitle>
<DescriptionListItemDescription>
<Link to="https://wiki.servarr.com/Prowlarr">wiki.servarr.com/Prowlarr</Link>
<Link to="https://wikijs.servarr.com/prowlarr">wikijs.servarr.com/prowlarr</Link>
</DescriptionListItemDescription>
<DescriptionListItemTitle>Donations</DescriptionListItemTitle>

View File

@@ -3,6 +3,7 @@ import PageContent from 'Components/Page/PageContent';
import PageContentBody from 'Components/Page/PageContentBody';
import translate from 'Utilities/String/translate';
import AboutConnector from './About/AboutConnector';
import Donations from './Donations/Donations';
import HealthConnector from './Health/HealthConnector';
import MoreInfo from './MoreInfo/MoreInfo';
@@ -18,6 +19,7 @@ class Status extends Component {
<HealthConnector />
<AboutConnector />
<MoreInfo />
<Donations />
</PageContentBody>
</PageContent>
);

View File

@@ -0,0 +1,17 @@
.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;
}

View File

@@ -252,7 +252,7 @@
</span>
<a
href="https://wiki.servarr.com/Prowlarr_FAQ#Help_I_have_locked_my_self_out_of_Prowlarr_and_do_not_know_the_password"
href="https://wikijs.servarr.com/prowlarr/faq#help-i-have-locked-myself-out"
class="forgot-password"
>Forgot your password?</a
>

View File

@@ -0,0 +1,227 @@
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);
}
}
}

View File

@@ -0,0 +1,161 @@
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));
}
}
}

View File

@@ -1,4 +1,4 @@
using FluentAssertions;
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Core.HealthCheck;
using NzbDrone.Core.Test.Framework;
@@ -8,11 +8,11 @@ namespace NzbDrone.Core.Test.HealthCheck
[TestFixture]
public class HealthCheckFixture : CoreTest
{
private const string WikiRoot = "https://wiki.servarr.com/";
private const string WikiRoot = "https://wikijs.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", "#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 + "Custom-Page#my_health_check")]
[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", "custom-page#my-health-check", WikiRoot + "prowlarr/custom-page#my-health-check")]
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);

View File

@@ -1,12 +1,13 @@
using NLog;
using NzbDrone.Common.Http;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.Download;
using NzbDrone.Core.Indexers;
using NzbDrone.Core.Messaging.Events;
namespace NzbDrone.Core.Test.IndexerTests
{
public class TestIndexer : HttpIndexerBase<TestIndexerSettings>
public class TestIndexer : UsenetIndexerBase<TestIndexerSettings>
{
public override string Name => "Test Indexer";
public override string BaseUrl => "http://testindexer.com";
@@ -18,8 +19,8 @@ namespace NzbDrone.Core.Test.IndexerTests
public int _supportedPageSize;
public override int PageSize => _supportedPageSize;
public TestIndexer(IHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger)
: base(httpClient, eventAggregator, indexerStatusService, configService, logger)
public TestIndexer(IHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, IValidateNzbs nzbValidationService, Logger logger)
: base(httpClient, eventAggregator, indexerStatusService, configService, nzbValidationService, logger)
{
}

View File

@@ -146,7 +146,7 @@ namespace NzbDrone.Core.Applications.Lidarr
Fields = schema.Fields,
};
lidarrIndexer.Fields.FirstOrDefault(x => x.Name == "baseUrl").Value = $"{Settings.ProwlarrUrl}/{indexer.Id}/";
lidarrIndexer.Fields.FirstOrDefault(x => x.Name == "baseUrl").Value = $"{Settings.ProwlarrUrl.TrimEnd('/')}/{indexer.Id}/";
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 == "categories").Value = JArray.FromObject(indexer.Capabilities.Categories.SupportedCategories(Settings.SyncCategories.ToArray()));

View File

@@ -146,7 +146,7 @@ namespace NzbDrone.Core.Applications.Radarr
Fields = schema.Fields,
};
radarrIndexer.Fields.FirstOrDefault(x => x.Name == "baseUrl").Value = $"{Settings.ProwlarrUrl}/{indexer.Id}/";
radarrIndexer.Fields.FirstOrDefault(x => x.Name == "baseUrl").Value = $"{Settings.ProwlarrUrl.TrimEnd('/')}/{indexer.Id}/";
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 == "categories").Value = JArray.FromObject(indexer.Capabilities.Categories.SupportedCategories(Settings.SyncCategories.ToArray()));

View File

@@ -146,7 +146,7 @@ namespace NzbDrone.Core.Applications.Readarr
Fields = schema.Fields,
};
readarrIndexer.Fields.FirstOrDefault(x => x.Name == "baseUrl").Value = $"{Settings.ProwlarrUrl}/{indexer.Id}/";
readarrIndexer.Fields.FirstOrDefault(x => x.Name == "baseUrl").Value = $"{Settings.ProwlarrUrl.TrimEnd('/')}/{indexer.Id}/";
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 == "categories").Value = JArray.FromObject(indexer.Capabilities.Categories.SupportedCategories(Settings.SyncCategories.ToArray()));

View File

@@ -146,7 +146,7 @@ namespace NzbDrone.Core.Applications.Sonarr
Fields = schema.Fields,
};
sonarrIndexer.Fields.FirstOrDefault(x => x.Name == "baseUrl").Value = $"{Settings.ProwlarrUrl}/{indexer.Id}/";
sonarrIndexer.Fields.FirstOrDefault(x => x.Name == "baseUrl").Value = $"{Settings.ProwlarrUrl.TrimEnd('/')}/{indexer.Id}/";
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 == "categories").Value = JArray.FromObject(indexer.Capabilities.Categories.SupportedCategories(Settings.SyncCategories.ToArray()));

View File

@@ -108,10 +108,10 @@ namespace NzbDrone.Core.Datastore
if (OsInfo.IsOsx)
{
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-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_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://wikijs.servarr.com/prowlarr/faq#i-am-getting-an-error-database-disk-image-is-malformed", e, fileName);
}
catch (Exception e)
{

View File

@@ -31,9 +31,8 @@ namespace NzbDrone.Core.Download.Clients.DownloadStation
IHttpClient httpClient,
IConfigService configService,
IDiskProvider diskProvider,
IValidateNzbs nzbValidationService,
Logger logger)
: base(httpClient, configService, diskProvider, nzbValidationService, logger)
: base(httpClient, configService, diskProvider, logger)
{
_dsInfoProxy = dsInfoProxy;
_dsTaskProxy = dsTaskProxy;

View File

@@ -20,9 +20,8 @@ namespace NzbDrone.Core.Download.Clients.NzbVortex
IHttpClient httpClient,
IConfigService configService,
IDiskProvider diskProvider,
IValidateNzbs nzbValidationService,
Logger logger)
: base(httpClient, configService, diskProvider, nzbValidationService, logger)
: base(httpClient, configService, diskProvider, logger)
{
_proxy = proxy;
}

View File

@@ -25,9 +25,8 @@ namespace NzbDrone.Core.Download.Clients.Nzbget
IHttpClient httpClient,
IConfigService configService,
IDiskProvider diskProvider,
IValidateNzbs nzbValidationService,
Logger logger)
: base(httpClient, configService, diskProvider, nzbValidationService, logger)
: base(httpClient, configService, diskProvider, logger)
{
_proxy = proxy;
}

View File

@@ -1,5 +1,7 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using FluentValidation.Results;
using NLog;
using NzbDrone.Common.Disk;
@@ -14,24 +16,20 @@ namespace NzbDrone.Core.Download.Clients.Pneumatic
{
public class Pneumatic : DownloadClientBase<PneumaticSettings>
{
private readonly IHttpClient _httpClient;
public Pneumatic(IHttpClient httpClient,
IConfigService configService,
public Pneumatic(IConfigService configService,
IDiskProvider diskProvider,
Logger logger)
: base(configService, diskProvider, logger)
{
_httpClient = httpClient;
}
public override string Name => "Pneumatic";
public override DownloadProtocol Protocol => DownloadProtocol.Usenet;
public override string Download(ReleaseInfo release, bool redirect)
public override async Task<string> Download(ReleaseInfo release, bool redirect, IIndexer indexer)
{
var url = release.DownloadUrl;
var url = new Uri(release.DownloadUrl);
var title = release.Title;
title = StringUtil.CleanFileName(title);
@@ -40,7 +38,10 @@ namespace NzbDrone.Core.Download.Clients.Pneumatic
var nzbFile = Path.Combine(Settings.NzbFolder, title + ".nzb");
_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);

View File

@@ -22,9 +22,8 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd
IHttpClient httpClient,
IConfigService configService,
IDiskProvider diskProvider,
IValidateNzbs nzbValidationService,
Logger logger)
: base(httpClient, configService, diskProvider, nzbValidationService, logger)
: base(httpClient, configService, diskProvider, logger)
{
_proxy = proxy;
}

View File

@@ -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);
throw new ReleaseDownloadException(release, "Downloading torrent failed");
throw new ReleaseDownloadException("Downloading torrent failed");
}
return hash;

View File

@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using FluentValidation.Results;
using NLog;
using NzbDrone.Common.Disk;
@@ -54,7 +55,7 @@ namespace NzbDrone.Core.Download
get;
}
public abstract string Download(ReleaseInfo release, bool redirect);
public abstract Task<string> Download(ReleaseInfo release, bool redirect, IIndexer indexer);
public ValidationResult Test()
{

View File

@@ -6,7 +6,7 @@ using NzbDrone.Common.Extensions;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.Security;
namespace NzbDrone.Core.Indexers
namespace NzbDrone.Core.Download
{
public interface IDownloadMappingService
{

View File

@@ -17,7 +17,7 @@ namespace NzbDrone.Core.Download
{
public interface IDownloadService
{
void SendReportToClient(ReleaseInfo release, string source, string host, bool redirect);
Task SendReportToClient(ReleaseInfo release, string source, string host, bool redirect);
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);
}
@@ -49,7 +49,7 @@ namespace NzbDrone.Core.Download
_logger = logger;
}
public void SendReportToClient(ReleaseInfo release, string source, string host, bool redirect)
public async Task SendReportToClient(ReleaseInfo release, string source, string host, bool redirect)
{
var downloadTitle = release.Title;
var downloadClient = _downloadClientProvider.GetDownloadClient(release.DownloadProtocol);
@@ -69,10 +69,12 @@ namespace NzbDrone.Core.Download
_rateLimitService.WaitAndPulse(url.Host, TimeSpan.FromSeconds(2));
}
var indexer = _indexerFactory.GetInstance(_indexerFactory.Get(release.IndexerId));
string downloadClientId;
try
{
downloadClientId = downloadClient.Download(release, redirect);
downloadClientId = await downloadClient.Download(release, redirect, indexer);
_downloadClientStatusService.RecordSuccess(downloadClient.Definition.Id);
_indexerStatusService.RecordSuccess(release.IndexerId);
}

View File

@@ -1,3 +1,4 @@
using System.Threading.Tasks;
using NzbDrone.Core.Indexers;
using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.ThingiProvider;
@@ -7,6 +8,6 @@ namespace NzbDrone.Core.Download
public interface IDownloadClient : IProvider
{
DownloadProtocol Protocol { get; }
string Download(ReleaseInfo release, bool redirect);
Task<string> Download(ReleaseInfo release, bool redirect, IIndexer indexer);
}
}

View File

@@ -8,12 +8,12 @@ namespace NzbDrone.Core.Download
{
public interface IValidateNzbs
{
void Validate(string filename, byte[] fileContent);
void Validate(byte[] fileContent);
}
public class NzbValidationService : IValidateNzbs
{
public void Validate(string filename, byte[] fileContent)
public void Validate(byte[] fileContent)
{
var reader = new StreamReader(new MemoryStream(fileContent));
@@ -24,7 +24,7 @@ namespace NzbDrone.Core.Download
if (nzb == null)
{
throw new InvalidNzbException("Invalid NZB: No Root element [{0}]", filename);
throw new InvalidNzbException("Invalid NZB: No Root element");
}
// 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"))
{
throw new InvalidNzbException("Invalid NZB: Unexpected root element. Expected 'nzb' found '{0}' [{1}]", nzb.Name.LocalName, filename);
throw new InvalidNzbException("Invalid NZB: Unexpected root element. Expected 'nzb' found '{0}'", nzb.Name.LocalName);
}
var ns = nzb.Name.Namespace;
@@ -45,7 +45,7 @@ namespace NzbDrone.Core.Download
if (files.Empty())
{
throw new InvalidNzbException("Invalid NZB: No files [{0}]", filename);
throw new InvalidNzbException("Invalid NZB: No files");
}
}
}

View File

@@ -1,5 +1,7 @@
using System;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using MonoTorrent;
using NLog;
using NzbDrone.Common.Disk;
@@ -39,7 +41,7 @@ namespace NzbDrone.Core.Download
protected abstract string AddFromTorrentFile(ReleaseInfo release, string hash, string filename, byte[] fileContent);
protected abstract string AddFromTorrentLink(ReleaseInfo release, string hash, string torrentLink);
public override string Download(ReleaseInfo release, bool redirect)
public override async Task<string> Download(ReleaseInfo release, bool redirect, IIndexer indexer)
{
var torrentInfo = release as TorrentInfo;
@@ -66,7 +68,7 @@ namespace NzbDrone.Core.Download
{
try
{
return DownloadFromWebUrl(release, torrentUrl);
return await DownloadFromWebUrl(release, indexer, torrentUrl);
}
catch (Exception ex)
{
@@ -87,7 +89,7 @@ namespace NzbDrone.Core.Download
}
catch (NotSupportedException ex)
{
throw new ReleaseDownloadException(release, "Magnet not supported by download client. ({0})", ex.Message);
throw new ReleaseDownloadException("Magnet not supported by download client. ({0})", ex.Message);
}
}
}
@@ -103,7 +105,7 @@ namespace NzbDrone.Core.Download
{
if (torrentUrl.IsNullOrWhiteSpace())
{
throw new ReleaseDownloadException(release, "Magnet not supported by download client. ({0})", ex.Message);
throw new ReleaseDownloadException("Magnet not supported by download client. ({0})", ex.Message);
}
_logger.Debug("Magnet not supported by download client, trying torrent. ({0})", ex.Message);
@@ -112,74 +114,31 @@ namespace NzbDrone.Core.Download
if (torrentUrl.IsNotNullOrWhiteSpace())
{
return DownloadFromWebUrl(release, torrentUrl);
return await DownloadFromWebUrl(release, indexer, torrentUrl);
}
}
return null;
}
private string DownloadFromWebUrl(ReleaseInfo release, string torrentUrl)
private async Task<string> DownloadFromWebUrl(ReleaseInfo release, IIndexer indexer, string torrentUrl)
{
byte[] torrentFile = null;
try
torrentFile = await indexer.Download(new Uri(torrentUrl));
// 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 request = new HttpRequest(torrentUrl);
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 magnetUrl = Encoding.UTF8.GetString(torrentFile);
return DownloadFromMagnetUrl(release, magnetUrl);
}
var filename = string.Format("{0}.torrent", StringUtil.CleanFileName(release.Title));

View File

@@ -1,9 +1,9 @@
using System.Net;
using System;
using System.Threading.Tasks;
using NLog;
using NzbDrone.Common.Disk;
using NzbDrone.Common.Http;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.Exceptions;
using NzbDrone.Core.Indexers;
using NzbDrone.Core.Parser;
using NzbDrone.Core.Parser.Model;
@@ -15,17 +15,14 @@ namespace NzbDrone.Core.Download
where TSettings : IProviderConfig, new()
{
protected readonly IHttpClient _httpClient;
private readonly IValidateNzbs _nzbValidationService;
protected UsenetClientBase(IHttpClient httpClient,
IConfigService configService,
IDiskProvider diskProvider,
IValidateNzbs nzbValidationService,
Logger logger)
: base(configService, diskProvider, logger)
{
_httpClient = httpClient;
_nzbValidationService = nzbValidationService;
}
public override DownloadProtocol Protocol => DownloadProtocol.Usenet;
@@ -33,9 +30,9 @@ namespace NzbDrone.Core.Download
protected abstract string AddFromNzbFile(ReleaseInfo release, string filename, byte[] fileContents);
protected abstract string AddFromLink(ReleaseInfo release);
public override string Download(ReleaseInfo release, bool redirect)
public override async Task<string> Download(ReleaseInfo release, bool redirect, IIndexer indexer)
{
var url = release.DownloadUrl;
var url = new Uri(release.DownloadUrl);
if (redirect)
{
@@ -46,40 +43,7 @@ namespace NzbDrone.Core.Download
byte[] nzbData;
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);
nzbData = await indexer.Download(url);
_logger.Info("Adding report [{0}] to the queue.", release.Title);
return AddFromNzbFile(release, filename, nzbData);

View File

@@ -1,28 +1,33 @@
using System;
using System;
using NzbDrone.Core.Parser.Model;
namespace NzbDrone.Core.Exceptions
{
public class DownloadClientRejectedReleaseException : ReleaseDownloadException
{
public ReleaseInfo Release { get; set; }
public DownloadClientRejectedReleaseException(ReleaseInfo release, string message, params object[] args)
: base(release, message, args)
: base(message, args)
{
Release = release;
}
public DownloadClientRejectedReleaseException(ReleaseInfo release, string message)
: base(release, message)
: base(message)
{
Release = release;
}
public DownloadClientRejectedReleaseException(ReleaseInfo release, string message, Exception innerException, params object[] args)
: base(release, message, innerException, args)
: base(message, innerException, args)
{
Release = release;
}
public DownloadClientRejectedReleaseException(ReleaseInfo release, string message, Exception innerException)
: base(release, message, innerException)
: base(message, innerException)
{
Release = release;
}
}
}

View File

@@ -1,4 +1,4 @@
using System;
using System;
using NzbDrone.Common.Exceptions;
using NzbDrone.Core.Parser.Model;
@@ -6,30 +6,24 @@ namespace NzbDrone.Core.Exceptions
{
public class ReleaseDownloadException : NzbDroneException
{
public ReleaseInfo Release { get; set; }
public ReleaseDownloadException(ReleaseInfo release, string message, params object[] args)
public ReleaseDownloadException(string message, params object[] args)
: base(message, args)
{
Release = release;
}
public ReleaseDownloadException(ReleaseInfo release, string message)
public ReleaseDownloadException(string message)
: base(message)
{
Release = release;
}
public ReleaseDownloadException(ReleaseInfo release, string message, Exception innerException, params object[] args)
public ReleaseDownloadException(string message, Exception innerException, params object[] args)
: base(message, innerException, args)
{
Release = release;
}
public ReleaseDownloadException(ReleaseInfo release, string message, Exception innerException)
public ReleaseDownloadException(string message, Exception innerException)
: base(message, innerException)
{
Release = release;
}
}
}

View File

@@ -1,27 +1,27 @@
using System;
using System;
using NzbDrone.Core.Parser.Model;
namespace NzbDrone.Core.Exceptions
{
public class ReleaseUnavailableException : ReleaseDownloadException
{
public ReleaseUnavailableException(ReleaseInfo release, string message, params object[] args)
: base(release, message, args)
public ReleaseUnavailableException(string message, params object[] args)
: base(message, args)
{
}
public ReleaseUnavailableException(ReleaseInfo release, string message)
: base(release, message)
public ReleaseUnavailableException(string message)
: base(message)
{
}
public ReleaseUnavailableException(ReleaseInfo release, string message, Exception innerException, params object[] args)
: base(release, message, innerException, args)
public ReleaseUnavailableException(string message, Exception innerException, params object[] args)
: base(message, innerException, args)
{
}
public ReleaseUnavailableException(ReleaseInfo release, string message, Exception innerException)
: base(release, message, innerException)
public ReleaseUnavailableException(string message, Exception innerException)
: base(message, innerException)
{
}
}

View File

@@ -44,14 +44,14 @@ namespace NzbDrone.Core.HealthCheck.Checks
return new HealthCheck(GetType(),
HealthCheckResult.Error,
_localizationService.GetLocalizedString("ApplicationStatusCheckAllClientMessage"),
"#applications_are_unavailable_due_to_failures");
"#applications-are-unavailable-due-to-failures");
}
return new HealthCheck(GetType(),
HealthCheckResult.Warning,
string.Format(_localizationService.GetLocalizedString("ApplicationStatusCheckSingleClientMessage"),
string.Join(", ", backOffProviders.Select(v => v.Provider.Definition.Name))),
"#applications_are_unavailable_due_to_failures");
"#applications-are-unavailable-due-to-failures");
}
}
}

View File

@@ -37,10 +37,10 @@ namespace NzbDrone.Core.HealthCheck.Checks
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");
}
}
}

View File

@@ -46,14 +46,14 @@ namespace NzbDrone.Core.HealthCheck.Checks
return new HealthCheck(GetType(),
HealthCheckResult.Error,
_localizationService.GetLocalizedString("IndexerLongTermStatusCheckAllClientMessage"),
"#indexers_are_unavailable_due_to_failures");
"#indexers-are-unavailable-due-to-failures");
}
return new HealthCheck(GetType(),
HealthCheckResult.Warning,
string.Format(_localizationService.GetLocalizedString("IndexerLongTermStatusCheckSingleClientMessage"),
string.Join(", ", backOffProviders.Select(v => v.Provider.Definition.Name))),
"#indexers_are_unavailable_due_to_failures");
"#indexers-are-unavailable-due-to-failures");
}
}
}

View File

@@ -44,14 +44,14 @@ namespace NzbDrone.Core.HealthCheck.Checks
return new HealthCheck(GetType(),
HealthCheckResult.Error,
_localizationService.GetLocalizedString("IndexerStatusCheckAllClientMessage"),
"#indexers_are_unavailable_due_to_failures");
"#indexers-are-unavailable-due-to-failures");
}
return new HealthCheck(GetType(),
HealthCheckResult.Warning,
string.Format(_localizationService.GetLocalizedString("IndexerStatusCheckSingleClientMessage"),
string.Join(", ", backOffProviders.Select(v => v.Provider.Definition.Name))),
"#indexers_are_unavailable_due_to_failures");
"#indexers-are-unavailable-due-to-failures");
}
}
}

View File

@@ -55,7 +55,7 @@ namespace NzbDrone.Core.HealthCheck.Checks
HealthCheckResult.Warning,
string.Format(_localizationService.GetLocalizedString("NewznabVipCheckExpiringClientMessage"),
string.Join(", ", expiringProviders.Select(v => v.Definition.Name))),
"#newznab_vip_expiring");
"#newznab-vip-expiring");
}
if (!expiredProviders.Empty())
@@ -64,7 +64,7 @@ namespace NzbDrone.Core.HealthCheck.Checks
HealthCheckResult.Warning,
string.Format(_localizationService.GetLocalizedString("NewznabVipCheckExpiredClientMessage"),
string.Join(", ", expiredProviders.Select(v => v.Definition.Name))),
"#newznab_vip_expired");
"#newznab-vip-expired");
}
return new HealthCheck(GetType());

View File

@@ -38,7 +38,7 @@ namespace NzbDrone.Core.HealthCheck.Checks
HealthCheckResult.Warning,
string.Format(_localizationService.GetLocalizedString("IndexerObsoleteCheckMessage"),
string.Join(", ", oldIndexers.Select(v => v.Name))),
"#indexers_are_obsolete");
"#indexers-are-obsolete");
}
public override bool CheckOnSchedule => false;

View File

@@ -25,10 +25,10 @@ namespace NzbDrone.Core.HealthCheck.Checks
{
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());

View File

@@ -48,7 +48,7 @@ namespace NzbDrone.Core.HealthCheck.Checks
return new HealthCheck(GetType(),
HealthCheckResult.Error,
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))
@@ -56,7 +56,7 @@ namespace NzbDrone.Core.HealthCheck.Checks
return new HealthCheck(GetType(),
HealthCheckResult.Error,
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))
@@ -64,7 +64,7 @@ namespace NzbDrone.Core.HealthCheck.Checks
return new HealthCheck(GetType(),
HealthCheckResult.Error,
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");
}
}

View File

@@ -34,12 +34,12 @@ namespace NzbDrone.Core.HealthCheck
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)
{
return new HttpUri("https://wiki.servarr.com/Prowlarr_System#") + new HttpUri(fragment);
return new HttpUri("https://wikijs.servarr.com/prowlarr/system#") + new HttpUri(fragment);
}
}

View File

@@ -4,6 +4,7 @@ using System.Linq;
using System.Threading.Tasks;
using NLog;
using NzbDrone.Common.Instrumentation.Extensions;
using NzbDrone.Core.Download;
using NzbDrone.Core.Indexers;
using NzbDrone.Core.Indexers.Events;
using NzbDrone.Core.IndexerSearch.Definitions;

View File

@@ -89,7 +89,8 @@ namespace NzbDrone.Core.IndexerVersions
{
var req = new HttpRequest($"https://indexers.prowlarr.com/master/{DEFINITION_VERSION}/{id}");
var response = _httpClient.Get(req);
return _deserializer.Deserialize<CardigannDefinition>(response.Content);
var definition = _deserializer.Deserialize<CardigannDefinition>(response.Content);
return CleanIndexerDefinition(definition);
}
private CardigannDefinition LoadIndexerDef(string fileKey)
@@ -118,42 +119,7 @@ namespace NzbDrone.Core.IndexerVersions
var definitionString = File.ReadAllText(file.FullName);
var definition = _deserializer.Deserialize<CardigannDefinition>(definitionString);
//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;
return CleanIndexerDefinition(definition);
}
catch (Exception e)
{
@@ -165,6 +131,45 @@ namespace NzbDrone.Core.IndexerVersions
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)
{
UpdateLocalDefinitions();

View File

@@ -23,7 +23,7 @@ using NzbDrone.Core.Validation;
namespace NzbDrone.Core.Indexers.Definitions
{
public class AnimeBytes : HttpIndexerBase<AnimeBytesSettings>
public class AnimeBytes : TorrentIndexerBase<AnimeBytesSettings>
{
public override string Name => "AnimeBytes";
public override string BaseUrl => "https://animebytes.tv/";

View File

@@ -21,7 +21,7 @@ using NzbDrone.Core.Validation;
namespace NzbDrone.Core.Indexers.Definitions
{
public class AnimeTorrents : HttpIndexerBase<AnimeTorrentsSettings>
public class AnimeTorrents : TorrentIndexerBase<AnimeTorrentsSettings>
{
public override string Name => "AnimeTorrents";

View File

@@ -9,7 +9,7 @@ using NzbDrone.Core.Messaging.Events;
namespace NzbDrone.Core.Indexers.Definitions.Avistaz
{
public abstract class AvistazBase : HttpIndexerBase<AvistazSettings>
public abstract class AvistazBase : TorrentIndexerBase<AvistazSettings>
{
public override DownloadProtocol Protocol => DownloadProtocol.Torrent;
public override string BaseUrl => "";

View File

@@ -21,7 +21,7 @@ using NzbDrone.Core.Validation;
namespace NzbDrone.Core.Indexers.Definitions
{
public class BakaBT : HttpIndexerBase<BakaBTSettings>
public class BakaBT : TorrentIndexerBase<BakaBTSettings>
{
public override string Name => "BakaBT";

View File

@@ -21,7 +21,7 @@ using NzbDrone.Core.Validation;
namespace NzbDrone.Core.Indexers.Definitions
{
public class BeyondHD : HttpIndexerBase<BeyondHDSettings>
public class BeyondHD : TorrentIndexerBase<BeyondHDSettings>
{
public override string Name => "BeyondHD";

View File

@@ -6,7 +6,7 @@ using NzbDrone.Core.Messaging.Events;
namespace NzbDrone.Core.Indexers.BroadcastheNet
{
public class BroadcastheNet : HttpIndexerBase<BroadcastheNetSettings>
public class BroadcastheNet : TorrentIndexerBase<BroadcastheNetSettings>
{
public override string Name => "BroadcasTheNet";

View File

@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using FluentValidation.Results;
@@ -7,6 +8,7 @@ using NLog;
using NzbDrone.Common.Cache;
using NzbDrone.Common.Http;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.Exceptions;
using NzbDrone.Core.IndexerVersions;
using NzbDrone.Core.Messaging.Events;
using NzbDrone.Core.ThingiProvider;
@@ -14,7 +16,7 @@ using NzbDrone.Core.Validation;
namespace NzbDrone.Core.Indexers.Cardigann
{
public class Cardigann : HttpIndexerBase<CardigannSettings>
public class Cardigann : TorrentIndexerBase<CardigannSettings>
{
private readonly IIndexerDefinitionUpdateService _definitionService;
private readonly ICached<CardigannRequestGenerator> _generatorCache;
@@ -147,6 +149,7 @@ namespace NzbDrone.Core.Indexers.Cardigann
if (request.Url.Scheme == "magnet")
{
ValidateMagnet(request.Url.FullUri);
return Encoding.UTF8.GetBytes(request.Url.FullUri);
}
@@ -159,10 +162,36 @@ namespace NzbDrone.Core.Indexers.Cardigann
var response = await _httpClient.ExecuteAsync(request);
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)
{
_indexerStatusService.RecordFailure(Definition.Id);
_logger.Error("Download failed");
_logger.Error("Downloading torrent failed");
throw;
}
return downloadBytes;

View File

@@ -21,7 +21,7 @@ using NzbDrone.Core.Validation;
namespace NzbDrone.Core.Indexers.Definitions
{
public class DigitalCore : HttpIndexerBase<DigitalCoreSettings>
public class DigitalCore : TorrentIndexerBase<DigitalCoreSettings>
{
public override string Name => "DigitalCore";
public override string BaseUrl => "https://digitalcore.club/";

View File

@@ -6,7 +6,7 @@ using NzbDrone.Core.Messaging.Events;
namespace NzbDrone.Core.Indexers.FileList
{
public class FileList : HttpIndexerBase<FileListSettings>
public class FileList : TorrentIndexerBase<FileListSettings>
{
public override string Name => "FileList.io";
public override string BaseUrl => "https://filelist.io";

View File

@@ -7,7 +7,7 @@ using NzbDrone.Core.Messaging.Events;
namespace NzbDrone.Core.Indexers.Gazelle
{
public abstract class Gazelle : HttpIndexerBase<GazelleSettings>
public abstract class Gazelle : TorrentIndexerBase<GazelleSettings>
{
public override DownloadProtocol Protocol => DownloadProtocol.Torrent;
public override string BaseUrl => "";

View File

@@ -6,7 +6,7 @@ using NzbDrone.Core.Messaging.Events;
namespace NzbDrone.Core.Indexers.HDBits
{
public class HDBits : HttpIndexerBase<HDBitsSettings>
public class HDBits : TorrentIndexerBase<HDBitsSettings>
{
public override string Name => "HDBits";
public override string BaseUrl => "https://hdbits.org";

View File

@@ -20,7 +20,7 @@ using NzbDrone.Core.Validation;
namespace NzbDrone.Core.Indexers.Definitions
{
public class HDTorrents : HttpIndexerBase<HDTorrentsSettings>
public class HDTorrents : TorrentIndexerBase<HDTorrentsSettings>
{
public override string Name => "HD-Torrents";

View File

@@ -6,11 +6,12 @@ using FluentValidation.Results;
using NLog;
using NzbDrone.Common.Http;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.Download;
using NzbDrone.Core.Messaging.Events;
namespace NzbDrone.Core.Indexers.Headphones
{
public class Headphones : HttpIndexerBase<HeadphonesSettings>
public class Headphones : UsenetIndexerBase<HeadphonesSettings>
{
public override string Name => "Headphones VIP";
@@ -35,8 +36,8 @@ namespace NzbDrone.Core.Indexers.Headphones
return new HeadphonesRssParser(Capabilities.Categories);
}
public Headphones(IHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger)
: base(httpClient, eventAggregator, indexerStatusService, configService, logger)
public Headphones(IHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, IValidateNzbs nzbValidationService, Logger logger)
: base(httpClient, eventAggregator, indexerStatusService, configService, nzbValidationService, logger)
{
}

View File

@@ -18,7 +18,7 @@ using NzbDrone.Core.Validation;
namespace NzbDrone.Core.Indexers.Definitions
{
public class IPTorrents : HttpIndexerBase<IPTorrentsSettings>
public class IPTorrents : TorrentIndexerBase<IPTorrentsSettings>
{
public override string Name => "IPTorrents";

View File

@@ -21,7 +21,7 @@ using NzbDrone.Core.Validation;
namespace NzbDrone.Core.Indexers.Definitions
{
public class ImmortalSeed : HttpIndexerBase<ImmortalSeedSettings>
public class ImmortalSeed : TorrentIndexerBase<ImmortalSeedSettings>
{
public override string Name => "ImmortalSeed";

View File

@@ -16,7 +16,7 @@ using NzbDrone.Core.Validation;
namespace NzbDrone.Core.Indexers.Definitions
{
public class Milkie : HttpIndexerBase<MilkieSettings>
public class Milkie : TorrentIndexerBase<MilkieSettings>
{
public override string Name => "Milkie";

View File

@@ -18,7 +18,7 @@ using NzbDrone.Core.Validation;
namespace NzbDrone.Core.Indexers.Definitions
{
public class MyAnonamouse : HttpIndexerBase<MyAnonamouseSettings>
public class MyAnonamouse : TorrentIndexerBase<MyAnonamouseSettings>
{
public override string Name => "MyAnonamouse";

View File

@@ -7,13 +7,14 @@ using NLog;
using NzbDrone.Common.Extensions;
using NzbDrone.Common.Http;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.Download;
using NzbDrone.Core.Messaging.Events;
using NzbDrone.Core.ThingiProvider;
using NzbDrone.Core.Validation;
namespace NzbDrone.Core.Indexers.Newznab
{
public class Newznab : HttpIndexerBase<NewznabSettings>
public class Newznab : UsenetIndexerBase<NewznabSettings>
{
private readonly INewznabCapabilitiesProvider _capabilitiesProvider;
@@ -108,8 +109,8 @@ namespace NzbDrone.Core.Indexers.Newznab
}
}
public Newznab(INewznabCapabilitiesProvider capabilitiesProvider, IHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger)
: base(httpClient, eventAggregator, indexerStatusService, configService, logger)
public Newznab(INewznabCapabilitiesProvider capabilitiesProvider, IHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, IValidateNzbs nzbValidationService, Logger logger)
: base(httpClient, eventAggregator, indexerStatusService, configService, nzbValidationService, logger)
{
_capabilitiesProvider = capabilitiesProvider;
}

View File

@@ -7,7 +7,7 @@ using NzbDrone.Core.Messaging.Events;
namespace NzbDrone.Core.Indexers.PassThePopcorn
{
public class PassThePopcorn : HttpIndexerBase<PassThePopcornSettings>
public class PassThePopcorn : TorrentIndexerBase<PassThePopcornSettings>
{
public override string Name => "PassThePopcorn";
public override string BaseUrl => "https://passthepopcorn.me";

View File

@@ -21,7 +21,7 @@ using NzbDrone.Core.Validation;
namespace NzbDrone.Core.Indexers.Definitions
{
public class PreToMe : HttpIndexerBase<PreToMeSettings>
public class PreToMe : TorrentIndexerBase<PreToMeSettings>
{
public override string Name => "PreToMe";
public override string BaseUrl => "https://pretome.info/";

View File

@@ -11,7 +11,7 @@ using NzbDrone.Core.Validation;
namespace NzbDrone.Core.Indexers.Rarbg
{
public class Rarbg : HttpIndexerBase<RarbgSettings>
public class Rarbg : TorrentIndexerBase<RarbgSettings>
{
private readonly IRarbgTokenProvider _tokenProvider;

View File

@@ -21,7 +21,7 @@ using NzbDrone.Core.Validation;
namespace NzbDrone.Core.Indexers.Definitions
{
public class RevolutionTT : HttpIndexerBase<RevolutionTTSettings>
public class RevolutionTT : TorrentIndexerBase<RevolutionTTSettings>
{
public override string Name => "RevolutionTT";

View File

@@ -18,7 +18,7 @@ using NzbDrone.Core.Validation;
namespace NzbDrone.Core.Indexers.Definitions
{
public class SubsPlease : HttpIndexerBase<SubsPleaseSettings>
public class SubsPlease : TorrentIndexerBase<SubsPleaseSettings>
{
public override string Name => "SubsPlease";
public override string BaseUrl => "https://subsplease.org/";

View File

@@ -19,7 +19,7 @@ using NzbDrone.Core.Validation;
namespace NzbDrone.Core.Indexers.Definitions
{
public class SuperBits : HttpIndexerBase<SuperBitsSettings>
public class SuperBits : TorrentIndexerBase<SuperBitsSettings>
{
public override string Name => "SuperBits";

View File

@@ -18,7 +18,7 @@ using NzbDrone.Core.Validation;
namespace NzbDrone.Core.Indexers.Definitions
{
public class ThePirateBay : HttpIndexerBase<ThePirateBaySettings>
public class ThePirateBay : TorrentIndexerBase<ThePirateBaySettings>
{
public override string Name => "ThePirateBay";
public override string BaseUrl => "https://thepiratebay.org/";

View File

@@ -17,7 +17,7 @@ using NzbDrone.Core.Validation;
namespace NzbDrone.Core.Indexers.Definitions
{
public class TorrentDay : HttpIndexerBase<TorrentDaySettings>
public class TorrentDay : TorrentIndexerBase<TorrentDaySettings>
{
public override string Name => "TorrentDay";

View File

@@ -21,7 +21,7 @@ using NzbDrone.Core.Validation;
namespace NzbDrone.Core.Indexers.Definitions
{
public class TorrentLeech : HttpIndexerBase<TorrentLeechSettings>
public class TorrentLeech : TorrentIndexerBase<TorrentLeechSettings>
{
public override string Name => "TorrentLeech";

View File

@@ -6,7 +6,7 @@ using NzbDrone.Core.Messaging.Events;
namespace NzbDrone.Core.Indexers.TorrentPotato
{
public class TorrentPotato : HttpIndexerBase<TorrentPotatoSettings>
public class TorrentPotato : TorrentIndexerBase<TorrentPotatoSettings>
{
public override string Name => "TorrentPotato";
public override string BaseUrl => "http://127.0.0.1";

View File

@@ -20,7 +20,7 @@ using NzbDrone.Core.Validation;
namespace NzbDrone.Core.Indexers.Definitions
{
public class TorrentSeeds : HttpIndexerBase<TorrentSeedsSettings>
public class TorrentSeeds : TorrentIndexerBase<TorrentSeedsSettings>
{
public override string Name => "TorrentSeeds";

View File

@@ -14,7 +14,7 @@ using NzbDrone.Core.Validation;
namespace NzbDrone.Core.Indexers.Torznab
{
public class Torznab : HttpIndexerBase<TorznabSettings>
public class Torznab : TorrentIndexerBase<TorznabSettings>
{
private readonly INewznabCapabilitiesProvider _capabilitiesProvider;

View File

@@ -5,7 +5,7 @@ using NzbDrone.Core.Messaging.Events;
namespace NzbDrone.Core.Indexers.Definitions.UNIT3D
{
public abstract class Unit3dBase : HttpIndexerBase<Unit3dSettings>
public abstract class Unit3dBase : TorrentIndexerBase<Unit3dSettings>
{
public override DownloadProtocol Protocol => DownloadProtocol.Torrent;
public override string BaseUrl => "";

View File

@@ -25,7 +25,7 @@ using NzbDrone.Core.Validation;
namespace NzbDrone.Core.Indexers.Definitions
{
public class ZonaQ : HttpIndexerBase<ZonaQSettings>
public class ZonaQ : TorrentIndexerBase<ZonaQSettings>
{
public override string Name => "ZonaQ";
public override string BaseUrl => "https://www.zonaq.pw/";

View File

@@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net;
using System.Text;
@@ -10,7 +9,6 @@ using NLog;
using NzbDrone.Common.Extensions;
using NzbDrone.Common.Http;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.Exceptions;
using NzbDrone.Core.Http.CloudFlare;
using NzbDrone.Core.Indexers.Events;
using NzbDrone.Core.Indexers.Exceptions;
@@ -105,42 +103,6 @@ namespace NzbDrone.Core.Indexers
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)
{
//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.

View File

@@ -77,7 +77,7 @@ namespace NzbDrone.Core.Indexers
public abstract Task<IndexerPageableQueryResult> Fetch(TvSearchCriteria searchCriteria);
public abstract Task<IndexerPageableQueryResult> Fetch(BookSearchCriteria searchCriteria);
public abstract Task<IndexerPageableQueryResult> Fetch(BasicSearchCriteria searchCriteria);
public abstract Task<byte[]> Download(Uri searchCriteria);
public abstract Task<byte[]> Download(Uri link);
public abstract IndexerCapabilities GetCapabilities();

View File

@@ -0,0 +1,90 @@
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);
}
}
}

View File

@@ -0,0 +1,85 @@
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;
}
}
}

View File

@@ -2,7 +2,6 @@
"About": "About",
"AcceptConfirmationModal": "Accept Confirmation Modal",
"Actions": "Actions",
"AppProfile": "App Profile",
"AddAppProfile": "Add App Sync Profile",
"Added": "Added",
"AddedToDownloadClient": "Release added to client",
@@ -26,11 +25,14 @@
"ApplyTagsHelpTexts2": "Add: Add the tags the existing list of tags",
"ApplyTagsHelpTexts3": "Remove: Remove the entered tags",
"ApplyTagsHelpTexts4": "Replace: Replace the tags with the entered tags (enter no tags to clear all tags)",
"AppProfile": "App Profile",
"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?",
"Authentication": "Authentication",
"AuthenticationMethodHelpText": "Require Username and Password to access Prowlarr",
"Automatic": "Automatic",
"AutomaticSearch": "Automatic Search",
"Backup": "Backup",
"BackupFolderHelpText": "Relative paths will be under Prowlarr's AppData directory",
"BackupIntervalHelpText": "Interval between automatic backups",
@@ -163,6 +165,7 @@
"IndexerStatusCheckAllClientMessage": "All indexers are unavailable due to failures",
"IndexerStatusCheckSingleClientMessage": "Indexers unavailable due to failures: {0}",
"Info": "Info",
"InteractiveSearch": "Interactive Search",
"Interval": "Interval",
"KeyboardShortcuts": "Keyboard Shortcuts",
"Language": "Language",
@@ -247,6 +250,8 @@
"QualitySettings": "Quality Settings",
"Queue": "Queue",
"ReadTheWikiForMoreInformation": "Read the Wiki for more information",
"Redirect": "Redirect",
"RedirectHelpText": "Redirect incoming download requests for indexer instead of Proxying using Prowlarr",
"Refresh": "Refresh",
"RefreshMovie": "Refresh movie",
"ReleaseBranchCheckOfficialBranchMessage": "Branch {0} is not a valid Prowlarr release branch, you will not receive updates",
@@ -267,6 +272,7 @@
"Restrictions": "Restrictions",
"Result": "Result",
"Retention": "Retention",
"RSS": "RSS",
"RSSIsNotSupportedWithThisIndexer": "RSS is not supported with this indexer",
"Save": "Save",
"SaveChanges": "Save Changes",

View File

@@ -27,7 +27,7 @@ namespace NzbDrone.Core.Notifications.CustomScript
public override string Name => "Custom Script";
public override string Link => "https://wiki.servarr.com/Prowlarr_Settings#Connections";
public override string Link => "https://wikijs.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);

View File

@@ -38,10 +38,10 @@ namespace NzbDrone.Core.Notifications.Twitter
AuthorizeNotification = "startOAuth";
}
[FieldDefinition(0, Label = "Consumer Key", Privacy = PrivacyLevel.ApiKey, HelpText = "Consumer key from a Twitter application", HelpLink = "https://wiki.servarr.com/Useful_Tools#Twitter_Connect")]
[FieldDefinition(0, Label = "Consumer Key", Privacy = PrivacyLevel.ApiKey, HelpText = "Consumer key from a Twitter application", HelpLink = "https://wikijs.servarr.com/useful-tools#twitter-connect")]
public string ConsumerKey { get; set; }
[FieldDefinition(1, Label = "Consumer Secret", Privacy = PrivacyLevel.ApiKey, HelpText = "Consumer secret from a Twitter application", HelpLink = "https://wiki.servarr.com/Useful_Tools#Twitter_Connect")]
[FieldDefinition(1, Label = "Consumer Secret", Privacy = PrivacyLevel.ApiKey, HelpText = "Consumer secret from a Twitter application", HelpLink = "https://wikijs.servarr.com/useful-tools#twitter-connect")]
public string ConsumerSecret { get; set; }
[FieldDefinition(2, Label = "Access Token", Privacy = PrivacyLevel.ApiKey, Advanced = true)]

View File

@@ -14,7 +14,7 @@ namespace NzbDrone.Core.Notifications.Webhook
_proxy = proxy;
}
public override string Link => "https://wiki.servarr.com/Prowlarr_Settings#Connect";
public override string Link => "https://wikijs.servarr.com/prowlarr/settings#connect";
public override void OnHealthIssue(HealthCheck.HealthCheck healthCheck)
{

View File

@@ -39,7 +39,7 @@ namespace Prowlarr.Api.V1
Fields = SchemaBuilder.ToSchema(definition.Settings),
//Radarr_Supported_{0} are custom build redirect pages; if passing a new var, create a new redirect
InfoLink = string.Format("https://wiki.servarr.com/Prowlarr_Supported_{0}",
InfoLink = string.Format("https://wikijs.servarr.com/prowlarr/supported_{0}",
definition.Implementation.ToLower())
};
}

View File

@@ -97,7 +97,15 @@ namespace Prowlarr.Api.V1.Search
{
try
{
var request = new NewznabRequest { q = query, source = "Prowlarr", t = "search", cat = string.Join(",", categories), server = Request.GetServerUrl() };
var request = new NewznabRequest
{
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 decisions = result.Releases;

View File

@@ -766,13 +766,13 @@
"source": "ImportMechanismCheck",
"type": "warning",
"message": "Enable Completed Download Handling",
"wikiUrl": "https://wiki.servarr.com/Prowlarr_System#Completed.2FFailed_Download_Handling"
"wikiUrl": "https://wikijs.servarr.com/prowlarr/system#completed.2FFailed_Download_Handling"
},
{
"source": "DownloadClientCheck",
"type": "error",
"message": "Unable to communicate with qBittorrent. Failed to connect to qBittorrent, check your settings.",
"wikiUrl": "https://wiki.servarr.com/Prowlarr_System#Download_Clients"
"wikiUrl": "https://wikijs.servarr.com/prowlarr/system#download-clients"
}
]
}

View File

@@ -164,9 +164,10 @@ namespace Prowlarr.Http.Extensions
public static string GetHostName(this HttpRequest request)
{
string ip = request.GetRemoteIP();
IPAddress myIP = IPAddress.Parse(ip);
try
{
IPAddress myIP = IPAddress.Parse(ip);
IPHostEntry getIPHost = Dns.GetHostEntry(myIP);
List<string> compName = getIPHost.HostName.ToString().Split('.').ToList();
return compName.First();