Merge branch 'develop' into phantom-develop

pull/3019/head
Taloth Saldono 6 years ago
commit f6bcadfeec

@ -9,6 +9,7 @@ using NzbDrone.Core.MediaFiles.TorrentInfo;
using NzbDrone.Core.Download; using NzbDrone.Core.Download;
using NzbDrone.Core.Download.Clients.QBittorrent; using NzbDrone.Core.Download.Clients.QBittorrent;
using NzbDrone.Test.Common; using NzbDrone.Test.Common;
using NzbDrone.Core.Exceptions;
namespace NzbDrone.Core.Test.Download.DownloadClientTests.QBittorrentTests namespace NzbDrone.Core.Test.Download.DownloadClientTests.QBittorrentTests
{ {
@ -99,15 +100,18 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.QBittorrentTests
Subject.Definition.Settings.As<QBittorrentSettings>().RecentTvPriority = (int)QBittorrentPriority.First; Subject.Definition.Settings.As<QBittorrentSettings>().RecentTvPriority = (int)QBittorrentPriority.First;
} }
protected void GivenMaxRatio(float maxRatio, bool removeOnMaxRatio = true) protected void GivenGlobalSeedLimits(float maxRatio, int maxSeedingTime = -1, bool removeOnMaxRatio = false)
{ {
Mocker.GetMock<IQBittorrentProxy>() Mocker.GetMock<IQBittorrentProxy>()
.Setup(s => s.GetConfig(It.IsAny<QBittorrentSettings>())) .Setup(s => s.GetConfig(It.IsAny<QBittorrentSettings>()))
.Returns(new QBittorrentPreferences .Returns(new QBittorrentPreferences
{ {
RemoveOnMaxRatio = removeOnMaxRatio, RemoveOnMaxRatio = removeOnMaxRatio,
MaxRatio = maxRatio MaxRatio = maxRatio,
}); MaxRatioEnabled = maxRatio >= 0,
MaxSeedingTime = maxSeedingTime,
MaxSeedingTimeEnabled = maxSeedingTime >= 0
});
} }
protected virtual void GivenTorrents(List<QBittorrentTorrent> torrents) protected virtual void GivenTorrents(List<QBittorrentTorrent> torrents)
@ -277,17 +281,33 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.QBittorrentTests
id.Should().Be(expectedHash); id.Should().Be(expectedHash);
} }
public void Download_should_refuse_magnet_if_dht_is_disabled() [Test]
public void Download_should_refuse_magnet_if_no_trackers_provided_and_dht_is_disabled()
{ {
Mocker.GetMock<IQBittorrentProxy>()
.Setup(s => s.GetConfig(It.IsAny<QBittorrentSettings>()))
.Returns(new QBittorrentPreferences() { DhtEnabled = false });
var remoteEpisode = CreateRemoteEpisode();
remoteEpisode.Release.DownloadUrl = "magnet:?xt=urn:btih:ZPBPA2P6ROZPKRHK44D5OW6NHXU5Z6KR";
Assert.Throws<ReleaseDownloadException>(() => Subject.Download(remoteEpisode));
}
[Test]
public void Download_should_accept_magnet_if_trackers_provided_and_dht_is_disabled()
{
Mocker.GetMock<IQBittorrentProxy>() Mocker.GetMock<IQBittorrentProxy>()
.Setup(s => s.GetConfig(It.IsAny<QBittorrentSettings>())) .Setup(s => s.GetConfig(It.IsAny<QBittorrentSettings>()))
.Returns(new QBittorrentPreferences() { DhtEnabled = false }); .Returns(new QBittorrentPreferences() { DhtEnabled = false });
var remoteEpisode = CreateRemoteEpisode(); var remoteEpisode = CreateRemoteEpisode();
remoteEpisode.Release.DownloadUrl = "magnet:?xt=urn:btih:ZPBPA2P6ROZPKRHK44D5OW6NHXU5Z6KR&tr=udp"; remoteEpisode.Release.DownloadUrl = "magnet:?xt=urn:btih:ZPBPA2P6ROZPKRHK44D5OW6NHXU5Z6KR&tr=udp://abc";
Assert.DoesNotThrow(() => Subject.Download(remoteEpisode));
Assert.Throws<NotSupportedException>(() => Subject.Download(remoteEpisode)); Mocker.GetMock<IQBittorrentProxy>()
.Verify(s => s.AddTorrentFromUrl(It.IsAny<string>(), It.IsAny<QBittorrentSettings>()), Times.Once());
} }
[Test] [Test]
@ -371,7 +391,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.QBittorrentTests
[Test] [Test]
public void should_not_be_removable_and_should_not_allow_move_files_if_max_ratio_not_reached() public void should_not_be_removable_and_should_not_allow_move_files_if_max_ratio_not_reached()
{ {
GivenMaxRatio(1.0f); GivenGlobalSeedLimits(1.0f);
var torrent = new QBittorrentTorrent var torrent = new QBittorrentTorrent
{ {
@ -392,11 +412,11 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.QBittorrentTests
item.CanMoveFiles.Should().BeFalse(); item.CanMoveFiles.Should().BeFalse();
} }
[Test] protected virtual QBittorrentTorrent GivenCompletedTorrent(
public void should_not_be_removable_and_should_not_allow_move_files_if_max_ratio_reached_and_not_paused() string state = "pausedUP",
float ratio = 0.1f, float ratioLimit = -2,
int seedingTime = 1, int seedingTimeLimit = -2)
{ {
GivenMaxRatio(1.0f);
var torrent = new QBittorrentTorrent var torrent = new QBittorrentTorrent
{ {
Hash = "HASH", Hash = "HASH",
@ -404,12 +424,32 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.QBittorrentTests
Size = 1000, Size = 1000,
Progress = 1.0, Progress = 1.0,
Eta = 8640000, Eta = 8640000,
State = "uploading", State = state,
Label = "", Label = "",
SavePath = "", SavePath = "",
Ratio = 1.0f Ratio = ratio,
RatioLimit = ratioLimit,
SeedingTimeLimit = seedingTimeLimit
}; };
GivenTorrents(new List<QBittorrentTorrent> { torrent });
GivenTorrents(new List<QBittorrentTorrent>() { torrent });
Mocker.GetMock<IQBittorrentProxy>()
.Setup(s => s.GetTorrentProperties("HASH", It.IsAny<QBittorrentSettings>()))
.Returns(new QBittorrentTorrentProperties
{
Hash = "HASH",
SeedingTime = seedingTime
});
return torrent;
}
[Test]
public void should_not_be_removable_and_should_not_allow_move_files_if_max_ratio_reached_and_not_paused()
{
GivenGlobalSeedLimits(1.0f);
GivenCompletedTorrent("uploading", ratio: 1.0f);
var item = Subject.GetItems().Single(); var item = Subject.GetItems().Single();
item.CanBeRemoved.Should().BeFalse(); item.CanBeRemoved.Should().BeFalse();
@ -419,21 +459,8 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.QBittorrentTests
[Test] [Test]
public void should_not_be_removable_and_should_not_allow_move_files_if_max_ratio_is_not_set() public void should_not_be_removable_and_should_not_allow_move_files_if_max_ratio_is_not_set()
{ {
GivenMaxRatio(1.0f, false); GivenGlobalSeedLimits(-1);
GivenCompletedTorrent("pausedUP", ratio: 1.0f);
var torrent = new QBittorrentTorrent
{
Hash = "HASH",
Name = _title,
Size = 1000,
Progress = 1.0,
Eta = 8640000,
State = "uploading",
Label = "",
SavePath = "",
Ratio = 1.0f
};
GivenTorrents(new List<QBittorrentTorrent> { torrent });
var item = Subject.GetItems().Single(); var item = Subject.GetItems().Single();
item.CanBeRemoved.Should().BeFalse(); item.CanBeRemoved.Should().BeFalse();
@ -443,21 +470,8 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.QBittorrentTests
[Test] [Test]
public void should_be_removable_and_should_allow_move_files_if_max_ratio_reached_and_paused() public void should_be_removable_and_should_allow_move_files_if_max_ratio_reached_and_paused()
{ {
GivenMaxRatio(1.0f); GivenGlobalSeedLimits(1.0f);
GivenCompletedTorrent("pausedUP", ratio: 1.0f);
var torrent = new QBittorrentTorrent
{
Hash = "HASH",
Name = _title,
Size = 1000,
Progress = 1.0,
Eta = 8640000,
State = "pausedUP",
Label = "",
SavePath = "",
Ratio = 1.0f
};
GivenTorrents(new List<QBittorrentTorrent> { torrent });
var item = Subject.GetItems().Single(); var item = Subject.GetItems().Single();
item.CanBeRemoved.Should().BeTrue(); item.CanBeRemoved.Should().BeTrue();
@ -467,22 +481,8 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.QBittorrentTests
[Test] [Test]
public void should_be_removable_and_should_allow_move_files_if_overridden_max_ratio_reached_and_paused() public void should_be_removable_and_should_allow_move_files_if_overridden_max_ratio_reached_and_paused()
{ {
GivenMaxRatio(2.0f); GivenGlobalSeedLimits(2.0f);
GivenCompletedTorrent("pausedUP", ratio: 1.0f, ratioLimit: 0.8f);
var torrent = new QBittorrentTorrent
{
Hash = "HASH",
Name = _title,
Size = 1000,
Progress = 1.0,
Eta = 8640000,
State = "pausedUP",
Label = "",
SavePath = "",
Ratio = 1.0f,
RatioLimit = 0.8f
};
GivenTorrents(new List<QBittorrentTorrent> { torrent });
var item = Subject.GetItems().Single(); var item = Subject.GetItems().Single();
item.CanBeRemoved.Should().BeTrue(); item.CanBeRemoved.Should().BeTrue();
@ -492,33 +492,75 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.QBittorrentTests
[Test] [Test]
public void should_not_be_removable_if_overridden_max_ratio_not_reached_and_paused() public void should_not_be_removable_if_overridden_max_ratio_not_reached_and_paused()
{ {
GivenMaxRatio(0.2f); GivenGlobalSeedLimits(0.2f);
GivenCompletedTorrent("pausedUP", ratio: 0.5f, ratioLimit: 0.8f);
var item = Subject.GetItems().Single();
item.CanBeRemoved.Should().BeFalse();
item.CanMoveFiles.Should().BeFalse();
}
var torrent = new QBittorrentTorrent
{ [Test]
Hash = "HASH", public void should_not_be_removable_and_should_not_allow_move_files_if_max_seedingtime_reached_and_not_paused()
Name = _title, {
Size = 1000, GivenGlobalSeedLimits(-1, 20);
Progress = 1.0, GivenCompletedTorrent("uploading", ratio: 2.0f, seedingTime: 30);
Eta = 8640000,
State = "pausedUP",
Label = "",
SavePath = "",
Ratio = 0.5f,
RatioLimit = 0.8f
};
GivenTorrents(new List<QBittorrentTorrent> { torrent });
var item = Subject.GetItems().Single(); var item = Subject.GetItems().Single();
item.CanBeRemoved.Should().BeFalse(); item.CanBeRemoved.Should().BeFalse();
item.CanMoveFiles.Should().BeFalse(); item.CanMoveFiles.Should().BeFalse();
} }
[Test]
public void should_be_removable_and_should_allow_move_files_if_max_seedingtime_reached_and_paused()
{
GivenGlobalSeedLimits(-1, 20);
GivenCompletedTorrent("pausedUP", ratio: 2.0f, seedingTime: 20);
var item = Subject.GetItems().Single();
item.CanBeRemoved.Should().BeTrue();
item.CanMoveFiles.Should().BeTrue();
}
[Test]
public void should_be_removable_and_should_allow_move_files_if_overridden_max_seedingtime_reached_and_paused()
{
GivenGlobalSeedLimits(-1, 40);
GivenCompletedTorrent("pausedUP", ratio: 2.0f, seedingTime: 20, seedingTimeLimit: 10);
var item = Subject.GetItems().Single();
item.CanBeRemoved.Should().BeTrue();
item.CanMoveFiles.Should().BeTrue();
}
[Test]
public void should_not_be_removable_if_overridden_max_seedingtime_not_reached_and_paused()
{
GivenGlobalSeedLimits(-1, 20);
GivenCompletedTorrent("pausedUP", ratio: 2.0f, seedingTime: 30, seedingTimeLimit: 40);
var item = Subject.GetItems().Single();
item.CanBeRemoved.Should().BeFalse();
item.CanMoveFiles.Should().BeFalse();
}
[Test]
public void should_be_removable_and_should_allow_move_files_if_max_seedingtime_reached_but_ratio_not_and_paused()
{
GivenGlobalSeedLimits(2.0f, 20);
GivenCompletedTorrent("pausedUP", ratio: 1.0f, seedingTime: 30);
var item = Subject.GetItems().Single();
item.CanBeRemoved.Should().BeTrue();
item.CanMoveFiles.Should().BeTrue();
}
[Test] [Test]
public void should_get_category_from_the_category_if_set() public void should_get_category_from_the_category_if_set()
{ {
const string category = "tv-sonarr"; const string category = "tv-sonarr";
GivenMaxRatio(1.0f); GivenGlobalSeedLimits(1.0f);
var torrent = new QBittorrentTorrent var torrent = new QBittorrentTorrent
{ {
@ -543,7 +585,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.QBittorrentTests
public void should_get_category_from_the_label_if_the_category_is_not_available() public void should_get_category_from_the_label_if_the_category_is_not_available()
{ {
const string category = "tv-sonarr"; const string category = "tv-sonarr";
GivenMaxRatio(1.0f); GivenGlobalSeedLimits(1.0f);
var torrent = new QBittorrentTorrent var torrent = new QBittorrentTorrent
{ {

@ -198,7 +198,7 @@ namespace NzbDrone.Core.Download.Clients.Deluge
protected override void Test(List<ValidationFailure> failures) protected override void Test(List<ValidationFailure> failures)
{ {
failures.AddIfNotNull(TestConnection()); failures.AddIfNotNull(TestConnection());
if (failures.Any()) return; if (failures.HasErrors()) return;
failures.AddIfNotNull(TestCategory()); failures.AddIfNotNull(TestCategory());
failures.AddIfNotNull(TestGetTorrents()); failures.AddIfNotNull(TestGetTorrents());
} }

@ -194,7 +194,7 @@ namespace NzbDrone.Core.Download.Clients.DownloadStation
protected override void Test(List<ValidationFailure> failures) protected override void Test(List<ValidationFailure> failures)
{ {
failures.AddIfNotNull(TestConnection()); failures.AddIfNotNull(TestConnection());
if (failures.Any()) return; if (failures.HasErrors()) return;
failures.AddIfNotNull(TestOutputPath()); failures.AddIfNotNull(TestOutputPath());
failures.AddIfNotNull(TestGetTorrents()); failures.AddIfNotNull(TestGetTorrents());
} }

@ -189,7 +189,7 @@ namespace NzbDrone.Core.Download.Clients.DownloadStation
protected override void Test(List<ValidationFailure> failures) protected override void Test(List<ValidationFailure> failures)
{ {
failures.AddIfNotNull(TestConnection()); failures.AddIfNotNull(TestConnection());
if (failures.Any()) return; if (failures.HasErrors()) return;
failures.AddIfNotNull(TestOutputPath()); failures.AddIfNotNull(TestOutputPath());
failures.AddIfNotNull(TestGetNZB()); failures.AddIfNotNull(TestGetNZB());
} }

@ -130,7 +130,7 @@ namespace NzbDrone.Core.Download.Clients.Hadouken
protected override void Test(List<ValidationFailure> failures) protected override void Test(List<ValidationFailure> failures)
{ {
failures.AddIfNotNull(TestConnection()); failures.AddIfNotNull(TestConnection());
if (failures.Any()) return; if (failures.HasErrors()) return;
failures.AddIfNotNull(TestGetTorrents()); failures.AddIfNotNull(TestGetTorrents());
} }

@ -35,9 +35,9 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent
protected override string AddFromMagnetLink(RemoteEpisode remoteEpisode, string hash, string magnetLink) protected override string AddFromMagnetLink(RemoteEpisode remoteEpisode, string hash, string magnetLink)
{ {
if (!Proxy.GetConfig(Settings).DhtEnabled) if (!Proxy.GetConfig(Settings).DhtEnabled && !magnetLink.Contains("&tr="))
{ {
throw new NotSupportedException("Magnet Links not supported if DHT is disabled"); throw new NotSupportedException("Magnet Links without trackers not supported if DHT is disabled");
} }
Proxy.AddTorrentFromUrl(magnetLink, Settings); Proxy.AddTorrentFromUrl(magnetLink, Settings);
@ -133,7 +133,6 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent
// Avoid removing torrents that haven't reached the global max ratio. // Avoid removing torrents that haven't reached the global max ratio.
// Removal also requires the torrent to be paused, in case a higher max ratio was set on the torrent itself (which is not exposed by the api). // Removal also requires the torrent to be paused, in case a higher max ratio was set on the torrent itself (which is not exposed by the api).
item.CanMoveFiles = item.CanBeRemoved = (torrent.State == "pausedUP" && HasReachedSeedLimit(torrent, config)); item.CanMoveFiles = item.CanBeRemoved = (torrent.State == "pausedUP" && HasReachedSeedLimit(torrent, config));
if (!item.OutputPath.IsEmpty && item.OutputPath.FileName != torrent.Name) if (!item.OutputPath.IsEmpty && item.OutputPath.FileName != torrent.Name)
{ {
@ -216,7 +215,7 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent
protected override void Test(List<ValidationFailure> failures) protected override void Test(List<ValidationFailure> failures)
{ {
failures.AddIfNotNull(TestConnection()); failures.AddIfNotNull(TestConnection());
if (failures.Any()) return; if (failures.HasErrors()) return;
failures.AddIfNotNull(TestPrioritySupport()); failures.AddIfNotNull(TestPrioritySupport());
failures.AddIfNotNull(TestGetTorrents()); failures.AddIfNotNull(TestGetTorrents());
} }
@ -387,23 +386,40 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent
{ {
if (torrent.RatioLimit >= 0) if (torrent.RatioLimit >= 0)
{ {
if (torrent.Ratio < torrent.RatioLimit) return false; if (torrent.Ratio >= torrent.RatioLimit) return true;
} }
else if (torrent.RatioLimit == -2 && config.MaxRatioEnabled) else if (torrent.RatioLimit == -2 && config.MaxRatioEnabled)
{ {
if (torrent.Ratio < config.MaxRatio) return false; if (torrent.Ratio >= config.MaxRatio) return true;
} }
if (torrent.SeedingTimeLimit >= 0) if (torrent.SeedingTimeLimit >= 0)
{ {
if (torrent.SeedingTime < torrent.SeedingTimeLimit) return false; if (!torrent.SeedingTime.HasValue)
{
FetchTorrentDetails(torrent);
}
if (torrent.SeedingTime >= torrent.SeedingTimeLimit) return true;
} }
else if (torrent.RatioLimit == -2 && config.MaxSeedingTimeEnabled) else if (torrent.SeedingTimeLimit == -2 && config.MaxSeedingTimeEnabled)
{ {
if (torrent.SeedingTime < config.MaxSeedingTime) return false; if (!torrent.SeedingTime.HasValue)
{
FetchTorrentDetails(torrent);
}
if (torrent.SeedingTime >= config.MaxSeedingTime) return true;
} }
return true; return false;
}
protected void FetchTorrentDetails(QBittorrentTorrent torrent)
{
var torrentProperties = Proxy.GetTorrentProperties(torrent.Hash, Settings);
torrent.SeedingTime = torrentProperties.SeedingTime;
} }
} }
} }

@ -16,6 +16,7 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent
string GetVersion(QBittorrentSettings settings); string GetVersion(QBittorrentSettings settings);
QBittorrentPreferences GetConfig(QBittorrentSettings settings); QBittorrentPreferences GetConfig(QBittorrentSettings settings);
List<QBittorrentTorrent> GetTorrents(QBittorrentSettings settings); List<QBittorrentTorrent> GetTorrents(QBittorrentSettings settings);
QBittorrentTorrentProperties GetTorrentProperties(string hash, QBittorrentSettings settings);
void AddTorrentFromUrl(string torrentUrl, QBittorrentSettings settings); void AddTorrentFromUrl(string torrentUrl, QBittorrentSettings settings);
void AddTorrentFromFile(string fileName, Byte[] fileContent, QBittorrentSettings settings); void AddTorrentFromFile(string fileName, Byte[] fileContent, QBittorrentSettings settings);

@ -87,14 +87,25 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent
public List<QBittorrentTorrent> GetTorrents(QBittorrentSettings settings) public List<QBittorrentTorrent> GetTorrents(QBittorrentSettings settings)
{ {
var request = BuildRequest(settings).Resource("/query/torrents") var request = BuildRequest(settings).Resource("/query/torrents");
.AddQueryParam("label", settings.TvCategory) if (settings.TvCategory.IsNotNullOrWhiteSpace())
.AddQueryParam("category", settings.TvCategory); {
request.AddQueryParam("label", settings.TvCategory);
request.AddQueryParam("category", settings.TvCategory);
}
var response = ProcessRequest<List<QBittorrentTorrent>>(request, settings); var response = ProcessRequest<List<QBittorrentTorrent>>(request, settings);
return response; return response;
} }
public QBittorrentTorrentProperties GetTorrentProperties(string hash, QBittorrentSettings settings)
{
var request = BuildRequest(settings).Resource($"/query/propertiesGeneral/{hash}");
var response = ProcessRequest<QBittorrentTorrentProperties>(request, settings);
return response;
}
public void AddTorrentFromUrl(string torrentUrl, QBittorrentSettings settings) public void AddTorrentFromUrl(string torrentUrl, QBittorrentSettings settings)
{ {
var request = BuildRequest(settings).Resource("/command/download") var request = BuildRequest(settings).Resource("/command/download")

@ -86,13 +86,25 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent
public List<QBittorrentTorrent> GetTorrents(QBittorrentSettings settings) public List<QBittorrentTorrent> GetTorrents(QBittorrentSettings settings)
{ {
var request = BuildRequest(settings).Resource("/api/v2/torrents/info") var request = BuildRequest(settings).Resource("/api/v2/torrents/info");
.AddQueryParam("category", settings.TvCategory); if (settings.TvCategory.IsNotNullOrWhiteSpace())
{
request.AddQueryParam("category", settings.TvCategory);
}
var response = ProcessRequest<List<QBittorrentTorrent>>(request, settings); var response = ProcessRequest<List<QBittorrentTorrent>>(request, settings);
return response; return response;
} }
public QBittorrentTorrentProperties GetTorrentProperties(string hash, QBittorrentSettings settings)
{
var request = BuildRequest(settings).Resource("/api/v2/torrents/properties")
.AddQueryParam("hash", hash);
var response = ProcessRequest<QBittorrentTorrentProperties>(request, settings);
return response;
}
public void AddTorrentFromUrl(string torrentUrl, QBittorrentSettings settings) public void AddTorrentFromUrl(string torrentUrl, QBittorrentSettings settings)
{ {
var request = BuildRequest(settings).Resource("/api/v2/torrents/add") var request = BuildRequest(settings).Resource("/api/v2/torrents/add")

@ -30,10 +30,17 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent
public float RatioLimit { get; set; } = -2; public float RatioLimit { get; set; } = -2;
[JsonProperty(PropertyName = "seeding_time")] [JsonProperty(PropertyName = "seeding_time")]
public long SeedingTime { get; set; } // Torrent seeding time public long? SeedingTime { get; set; } // Torrent seeding time (not provided by the list api)
[JsonProperty(PropertyName = "seeding_time_limit")] // Per torrent seeding time limit (-2 = use global, -1 = unlimited) [JsonProperty(PropertyName = "seeding_time_limit")] // Per torrent seeding time limit (-2 = use global, -1 = unlimited)
public long SeedingTimeLimit { get; set; } = -2; public long SeedingTimeLimit { get; set; } = -2;
}
public class QBittorrentTorrentProperties
{
public string Hash { get; set; } // Torrent hash
[JsonProperty(PropertyName = "seeding_time")]
public long SeedingTime { get; set; } // Torrent seeding time
} }
} }

@ -165,7 +165,7 @@ namespace NzbDrone.Core.Download.Clients.Transmission
protected override void Test(List<ValidationFailure> failures) protected override void Test(List<ValidationFailure> failures)
{ {
failures.AddIfNotNull(TestConnection()); failures.AddIfNotNull(TestConnection());
if (failures.Any()) return; if (failures.HasErrors()) return;
failures.AddIfNotNull(TestGetTorrents()); failures.AddIfNotNull(TestGetTorrents());
} }

@ -89,11 +89,11 @@ namespace NzbDrone.Core.Download.Clients.RTorrent
foreach (RTorrentTorrent torrent in torrents) foreach (RTorrentTorrent torrent in torrents)
{ {
// Don't concern ourselves with categories other than specified // Don't concern ourselves with categories other than specified
if (torrent.Category != Settings.TvCategory) continue; if (Settings.TvCategory.IsNotNullOrWhiteSpace() && torrent.Category != Settings.TvCategory) continue;
if (torrent.Path.StartsWith(".")) if (torrent.Path.StartsWith("."))
{ {
throw new DownloadClientException("Download paths paths must be absolute. Please specify variable \"directory\" in rTorrent."); throw new DownloadClientException("Download paths must be absolute. Please specify variable \"directory\" in rTorrent.");
} }
var item = new DownloadClientItem(); var item = new DownloadClientItem();
@ -163,7 +163,7 @@ namespace NzbDrone.Core.Download.Clients.RTorrent
protected override void Test(List<ValidationFailure> failures) protected override void Test(List<ValidationFailure> failures)
{ {
failures.AddIfNotNull(TestConnection()); failures.AddIfNotNull(TestConnection());
if (failures.Any()) return; if (failures.HasErrors()) return;
failures.AddIfNotNull(TestGetTorrents()); failures.AddIfNotNull(TestGetTorrents());
failures.AddIfNotNull(TestDirectory()); failures.AddIfNotNull(TestDirectory());
} }

@ -224,7 +224,7 @@ namespace NzbDrone.Core.Download.Clients.UTorrent
protected override void Test(List<ValidationFailure> failures) protected override void Test(List<ValidationFailure> failures)
{ {
failures.AddIfNotNull(TestConnection()); failures.AddIfNotNull(TestConnection());
if (failures.Any()) return; if (failures.HasErrors()) return;
failures.AddIfNotNull(TestGetTorrents()); failures.AddIfNotNull(TestGetTorrents());
} }

@ -8,6 +8,7 @@ using NzbDrone.Common.Http;
using NzbDrone.Core.Configuration; using NzbDrone.Core.Configuration;
using NzbDrone.Core.Parser; using NzbDrone.Core.Parser;
using NzbDrone.Core.ThingiProvider; using NzbDrone.Core.ThingiProvider;
using NzbDrone.Core.Validation;
namespace NzbDrone.Core.Indexers.Newznab namespace NzbDrone.Core.Indexers.Newznab
{ {
@ -103,7 +104,7 @@ namespace NzbDrone.Core.Indexers.Newznab
protected override void Test(List<ValidationFailure> failures) protected override void Test(List<ValidationFailure> failures)
{ {
base.Test(failures); base.Test(failures);
if (failures.Any()) return; if (failures.HasErrors()) return;
failures.AddIfNotNull(TestCapabilities()); failures.AddIfNotNull(TestCapabilities());
} }

@ -9,6 +9,7 @@ using NzbDrone.Core.Configuration;
using NzbDrone.Core.Indexers.Newznab; using NzbDrone.Core.Indexers.Newznab;
using NzbDrone.Core.Parser; using NzbDrone.Core.Parser;
using NzbDrone.Core.ThingiProvider; using NzbDrone.Core.ThingiProvider;
using NzbDrone.Core.Validation;
namespace NzbDrone.Core.Indexers.Torznab namespace NzbDrone.Core.Indexers.Torznab
{ {
@ -92,7 +93,7 @@ namespace NzbDrone.Core.Indexers.Torznab
protected override void Test(List<ValidationFailure> failures) protected override void Test(List<ValidationFailure> failures)
{ {
base.Test(failures); base.Test(failures);
if (failures.Any()) return; if (failures.HasErrors()) return;
failures.AddIfNotNull(TestCapabilities()); failures.AddIfNotNull(TestCapabilities());
} }

@ -1,5 +1,7 @@
using System.Collections.Generic;
using System.Linq; using System.Linq;
using FluentValidation; using FluentValidation;
using FluentValidation.Results;
namespace NzbDrone.Core.Validation namespace NzbDrone.Core.Validation
{ {
@ -19,5 +21,21 @@ namespace NzbDrone.Core.Validation
throw new ValidationException(result.Errors); throw new ValidationException(result.Errors);
} }
} }
public static bool HasErrors(this List<ValidationFailure> list)
{
foreach (var item in list)
{
var extended = item as NzbDroneValidationFailure;
if (extended != null && extended.IsWarning)
{
continue;
}
return true;
}
return false;
}
} }
} }

Loading…
Cancel
Save