mirror of
https://github.com/Prowlarr/Prowlarr.git
synced 2026-03-14 16:04:26 -04:00
Compare commits
10 Commits
jackettmig
...
v0.1.0.361
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b3fb640969 | ||
|
|
8b0a8e82b5 | ||
|
|
f25998959e | ||
|
|
f56ce129e6 | ||
|
|
ae9930a03f | ||
|
|
85be0be455 | ||
|
|
cf1c44ed75 | ||
|
|
f062fafe82 | ||
|
|
1032d8b3ab | ||
|
|
99c4ed7dbc |
@@ -2,7 +2,7 @@
|
||||
|
||||
[](https://dev.azure.com/Prowlarr/Prowlarr/_build/latest?definitionId=1&branchName=develop)
|
||||
[](https://translate.servarr.com/engage/prowlarr/?utm_source=widget)
|
||||
[](https://wiki.servarr.com/Prowlarr_Installation#Docker)
|
||||
[](https://wikijs.servarr.com/prowlarr/installation#docker)
|
||||

|
||||
[](#backers)
|
||||
[](#sponsors)
|
||||
@@ -23,7 +23,7 @@ Note: Prowlarr is currently early in life, thus bugs should be expected
|
||||
[](https://prowlarr.com/discord)
|
||||
[](https://www.reddit.com/r/Prowlarr)
|
||||
[](https://github.com/Prowlarr/Prowlarr/issues)
|
||||
[](https://wiki.servarr.com/Prowlarr)
|
||||
[](https://wikijs.servarr.com/prowlarr)
|
||||
|
||||
## Feature Requests
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
BIN
frontend/src/Content/Images/Icons/logo-lidarr.png
Normal file
BIN
frontend/src/Content/Images/Icons/logo-lidarr.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.3 KiB |
BIN
frontend/src/Content/Images/Icons/logo-prowlarr.png
Normal file
BIN
frontend/src/Content/Images/Icons/logo-prowlarr.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.4 KiB |
BIN
frontend/src/Content/Images/Icons/logo-radarr.png
Normal file
BIN
frontend/src/Content/Images/Icons/logo-radarr.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.6 KiB |
BIN
frontend/src/Content/Images/Icons/logo-readarr.png
Normal file
BIN
frontend/src/Content/Images/Icons/logo-readarr.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.3 KiB |
BIN
frontend/src/Content/Images/Icons/logo-sonarr.png
Normal file
BIN
frontend/src/Content/Images/Icons/logo-sonarr.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.9 KiB |
@@ -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>
|
||||
|
||||
@@ -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}
|
||||
/>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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'
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
64
frontend/src/System/Status/Donations/Donations.js
Normal file
64
frontend/src/System/Status/Donations/Donations.js
Normal 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;
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
|
||||
17
frontend/src/System/Status/styles.css
Normal file
17
frontend/src/System/Status/styles.css
Normal 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;
|
||||
}
|
||||
@@ -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
|
||||
>
|
||||
|
||||
227
src/NzbDrone.Core.Test/Download/DownloadClientProviderFixture.cs
Normal file
227
src/NzbDrone.Core.Test/Download/DownloadClientProviderFixture.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -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()));
|
||||
|
||||
@@ -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()));
|
||||
|
||||
@@ -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()));
|
||||
|
||||
@@ -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()));
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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()
|
||||
{
|
||||
|
||||
@@ -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
|
||||
{
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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/";
|
||||
|
||||
@@ -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";
|
||||
|
||||
|
||||
@@ -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 => "";
|
||||
|
||||
@@ -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";
|
||||
|
||||
|
||||
@@ -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";
|
||||
|
||||
|
||||
@@ -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";
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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/";
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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 => "";
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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";
|
||||
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -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";
|
||||
|
||||
|
||||
@@ -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";
|
||||
|
||||
|
||||
@@ -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";
|
||||
|
||||
|
||||
@@ -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";
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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/";
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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";
|
||||
|
||||
|
||||
@@ -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/";
|
||||
|
||||
@@ -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";
|
||||
|
||||
|
||||
@@ -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/";
|
||||
|
||||
@@ -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";
|
||||
|
||||
|
||||
@@ -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";
|
||||
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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";
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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 => "";
|
||||
|
||||
@@ -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/";
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
90
src/NzbDrone.Core/Indexers/TorrentIndexerBase.cs
Normal file
90
src/NzbDrone.Core/Indexers/TorrentIndexerBase.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
85
src/NzbDrone.Core/Indexers/UsenetIndexerBase.cs
Normal file
85
src/NzbDrone.Core/Indexers/UsenetIndexerBase.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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",
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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)]
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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())
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user