diff --git a/src/NzbDrone.Core.Test/Download/DownloadClientTests/QBittorrentTests/QBittorrentFixture.cs b/src/NzbDrone.Core.Test/Download/DownloadClientTests/QBittorrentTests/QBittorrentFixture.cs index 94ce08bba..0c2d9c96c 100644 --- a/src/NzbDrone.Core.Test/Download/DownloadClientTests/QBittorrentTests/QBittorrentFixture.cs +++ b/src/NzbDrone.Core.Test/Download/DownloadClientTests/QBittorrentTests/QBittorrentFixture.cs @@ -9,6 +9,7 @@ using NzbDrone.Core.Download; using NzbDrone.Core.Download.Clients.QBittorrent; using NzbDrone.Test.Common; +using NzbDrone.Core.Exceptions; namespace NzbDrone.Core.Test.Download.DownloadClientTests.QBittorrentTests { @@ -99,15 +100,18 @@ protected void GivenHighPriority() Subject.Definition.Settings.As().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() - .Setup(s => s.GetConfig(It.IsAny())) - .Returns(new QBittorrentPreferences - { - RemoveOnMaxRatio = removeOnMaxRatio, - MaxRatio = maxRatio - }); + .Setup(s => s.GetConfig(It.IsAny())) + .Returns(new QBittorrentPreferences + { + RemoveOnMaxRatio = removeOnMaxRatio, + MaxRatio = maxRatio, + MaxRatioEnabled = maxRatio >= 0, + MaxSeedingTime = maxSeedingTime, + MaxSeedingTimeEnabled = maxSeedingTime >= 0 + }); } protected virtual void GivenTorrents(List torrents) @@ -277,17 +281,33 @@ public void Download_should_get_hash_from_magnet_url(string magnetUrl, string ex 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() .Setup(s => s.GetConfig(It.IsAny())) .Returns(new QBittorrentPreferences() { DhtEnabled = false }); var remoteEpisode = CreateRemoteEpisode(); - remoteEpisode.Release.DownloadUrl = "magnet:?xt=urn:btih:ZPBPA2P6ROZPKRHK44D5OW6NHXU5Z6KR&tr=udp"; + remoteEpisode.Release.DownloadUrl = "magnet:?xt=urn:btih:ZPBPA2P6ROZPKRHK44D5OW6NHXU5Z6KR"; - Assert.Throws(() => Subject.Download(remoteEpisode)); + Assert.Throws(() => Subject.Download(remoteEpisode)); + } + + [Test] + public void Download_should_accept_magnet_if_trackers_provided_and_dht_is_disabled() + { + Mocker.GetMock() + .Setup(s => s.GetConfig(It.IsAny())) + .Returns(new QBittorrentPreferences() { DhtEnabled = false }); + + var remoteEpisode = CreateRemoteEpisode(); + remoteEpisode.Release.DownloadUrl = "magnet:?xt=urn:btih:ZPBPA2P6ROZPKRHK44D5OW6NHXU5Z6KR&tr=udp://abc"; + + Assert.DoesNotThrow(() => Subject.Download(remoteEpisode)); + + Mocker.GetMock() + .Verify(s => s.AddTorrentFromUrl(It.IsAny(), It.IsAny()), Times.Once()); } [Test] @@ -371,7 +391,7 @@ public void Download_should_handle_http_redirect_to_torrent() [Test] 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 { @@ -392,11 +412,11 @@ public void should_not_be_removable_and_should_not_allow_move_files_if_max_ratio item.CanMoveFiles.Should().BeFalse(); } - [Test] - public void should_not_be_removable_and_should_not_allow_move_files_if_max_ratio_reached_and_not_paused() + protected virtual QBittorrentTorrent GivenCompletedTorrent( + string state = "pausedUP", + float ratio = 0.1f, float ratioLimit = -2, + int seedingTime = 1, int seedingTimeLimit = -2) { - GivenMaxRatio(1.0f); - var torrent = new QBittorrentTorrent { Hash = "HASH", @@ -404,12 +424,32 @@ public void should_not_be_removable_and_should_not_allow_move_files_if_max_ratio Size = 1000, Progress = 1.0, Eta = 8640000, - State = "uploading", + State = state, Label = "", SavePath = "", - Ratio = 1.0f + Ratio = ratio, + RatioLimit = ratioLimit, + SeedingTimeLimit = seedingTimeLimit }; - GivenTorrents(new List { torrent }); + + GivenTorrents(new List() { torrent }); + + Mocker.GetMock() + .Setup(s => s.GetTorrentProperties("HASH", It.IsAny())) + .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(); item.CanBeRemoved.Should().BeFalse(); @@ -419,21 +459,8 @@ public void should_not_be_removable_and_should_not_allow_move_files_if_max_ratio [Test] public void should_not_be_removable_and_should_not_allow_move_files_if_max_ratio_is_not_set() { - GivenMaxRatio(1.0f, false); - - 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 { torrent }); + GivenGlobalSeedLimits(-1); + GivenCompletedTorrent("pausedUP", ratio: 1.0f); var item = Subject.GetItems().Single(); item.CanBeRemoved.Should().BeFalse(); @@ -443,21 +470,8 @@ public void should_not_be_removable_and_should_not_allow_move_files_if_max_ratio [Test] public void should_be_removable_and_should_allow_move_files_if_max_ratio_reached_and_paused() { - GivenMaxRatio(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 { torrent }); + GivenGlobalSeedLimits(1.0f); + GivenCompletedTorrent("pausedUP", ratio: 1.0f); var item = Subject.GetItems().Single(); item.CanBeRemoved.Should().BeTrue(); @@ -467,22 +481,8 @@ public void should_be_removable_and_should_allow_move_files_if_max_ratio_reached [Test] public void should_be_removable_and_should_allow_move_files_if_overridden_max_ratio_reached_and_paused() { - GivenMaxRatio(2.0f); - - 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 { torrent }); + GivenGlobalSeedLimits(2.0f); + GivenCompletedTorrent("pausedUP", ratio: 1.0f, ratioLimit: 0.8f); var item = Subject.GetItems().Single(); item.CanBeRemoved.Should().BeTrue(); @@ -492,33 +492,75 @@ public void should_be_removable_and_should_allow_move_files_if_overridden_max_ra [Test] 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 torrent = new QBittorrentTorrent - { - Hash = "HASH", - Name = _title, - Size = 1000, - Progress = 1.0, - Eta = 8640000, - State = "pausedUP", - Label = "", - SavePath = "", - Ratio = 0.5f, - RatioLimit = 0.8f - }; - GivenTorrents(new List { torrent }); + var item = Subject.GetItems().Single(); + item.CanBeRemoved.Should().BeFalse(); + item.CanMoveFiles.Should().BeFalse(); + } + + + [Test] + public void should_not_be_removable_and_should_not_allow_move_files_if_max_seedingtime_reached_and_not_paused() + { + GivenGlobalSeedLimits(-1, 20); + GivenCompletedTorrent("uploading", ratio: 2.0f, seedingTime: 30); 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_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] public void should_get_category_from_the_category_if_set() { const string category = "tv-sonarr"; - GivenMaxRatio(1.0f); + GivenGlobalSeedLimits(1.0f); var torrent = new QBittorrentTorrent { @@ -543,7 +585,7 @@ public void should_get_category_from_the_category_if_set() public void should_get_category_from_the_label_if_the_category_is_not_available() { const string category = "tv-sonarr"; - GivenMaxRatio(1.0f); + GivenGlobalSeedLimits(1.0f); var torrent = new QBittorrentTorrent { diff --git a/src/NzbDrone.Core/Download/Clients/Deluge/Deluge.cs b/src/NzbDrone.Core/Download/Clients/Deluge/Deluge.cs index 038ffbacf..7a55bedc8 100644 --- a/src/NzbDrone.Core/Download/Clients/Deluge/Deluge.cs +++ b/src/NzbDrone.Core/Download/Clients/Deluge/Deluge.cs @@ -198,7 +198,7 @@ public override DownloadClientInfo GetStatus() protected override void Test(List failures) { failures.AddIfNotNull(TestConnection()); - if (failures.Any()) return; + if (failures.HasErrors()) return; failures.AddIfNotNull(TestCategory()); failures.AddIfNotNull(TestGetTorrents()); } diff --git a/src/NzbDrone.Core/Download/Clients/DownloadStation/TorrentDownloadStation.cs b/src/NzbDrone.Core/Download/Clients/DownloadStation/TorrentDownloadStation.cs index a272e5560..15508af90 100644 --- a/src/NzbDrone.Core/Download/Clients/DownloadStation/TorrentDownloadStation.cs +++ b/src/NzbDrone.Core/Download/Clients/DownloadStation/TorrentDownloadStation.cs @@ -194,7 +194,7 @@ protected override string AddFromTorrentFile(RemoteEpisode remoteEpisode, string protected override void Test(List failures) { failures.AddIfNotNull(TestConnection()); - if (failures.Any()) return; + if (failures.HasErrors()) return; failures.AddIfNotNull(TestOutputPath()); failures.AddIfNotNull(TestGetTorrents()); } diff --git a/src/NzbDrone.Core/Download/Clients/DownloadStation/UsenetDownloadStation.cs b/src/NzbDrone.Core/Download/Clients/DownloadStation/UsenetDownloadStation.cs index 2e6cf3d08..fda9c339d 100644 --- a/src/NzbDrone.Core/Download/Clients/DownloadStation/UsenetDownloadStation.cs +++ b/src/NzbDrone.Core/Download/Clients/DownloadStation/UsenetDownloadStation.cs @@ -189,7 +189,7 @@ protected override string AddFromNzbFile(RemoteEpisode remoteEpisode, string fil protected override void Test(List failures) { failures.AddIfNotNull(TestConnection()); - if (failures.Any()) return; + if (failures.HasErrors()) return; failures.AddIfNotNull(TestOutputPath()); failures.AddIfNotNull(TestGetNZB()); } diff --git a/src/NzbDrone.Core/Download/Clients/Hadouken/Hadouken.cs b/src/NzbDrone.Core/Download/Clients/Hadouken/Hadouken.cs index 1c2d660da..41473fa33 100644 --- a/src/NzbDrone.Core/Download/Clients/Hadouken/Hadouken.cs +++ b/src/NzbDrone.Core/Download/Clients/Hadouken/Hadouken.cs @@ -130,7 +130,7 @@ public override DownloadClientInfo GetStatus() protected override void Test(List failures) { failures.AddIfNotNull(TestConnection()); - if (failures.Any()) return; + if (failures.HasErrors()) return; failures.AddIfNotNull(TestGetTorrents()); } diff --git a/src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrent.cs b/src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrent.cs index 05ae7bdad..645acf386 100644 --- a/src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrent.cs +++ b/src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrent.cs @@ -35,9 +35,9 @@ public QBittorrent(IQBittorrentProxySelector proxySelector, 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); @@ -133,7 +133,6 @@ public override IEnumerable GetItems() // 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). item.CanMoveFiles = item.CanBeRemoved = (torrent.State == "pausedUP" && HasReachedSeedLimit(torrent, config)); - if (!item.OutputPath.IsEmpty && item.OutputPath.FileName != torrent.Name) { @@ -216,7 +215,7 @@ public override DownloadClientInfo GetStatus() protected override void Test(List failures) { failures.AddIfNotNull(TestConnection()); - if (failures.Any()) return; + if (failures.HasErrors()) return; failures.AddIfNotNull(TestPrioritySupport()); failures.AddIfNotNull(TestGetTorrents()); } @@ -387,23 +386,40 @@ protected bool HasReachedSeedLimit(QBittorrentTorrent torrent, QBittorrentPrefer { 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) { - if (torrent.Ratio < config.MaxRatio) return false; + if (torrent.Ratio >= config.MaxRatio) return true; } 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; } } } diff --git a/src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrentProxySelector.cs b/src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrentProxySelector.cs index d9322bdc5..41e9719c6 100644 --- a/src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrentProxySelector.cs +++ b/src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrentProxySelector.cs @@ -16,6 +16,7 @@ public interface IQBittorrentProxy string GetVersion(QBittorrentSettings settings); QBittorrentPreferences GetConfig(QBittorrentSettings settings); List GetTorrents(QBittorrentSettings settings); + QBittorrentTorrentProperties GetTorrentProperties(string hash, QBittorrentSettings settings); void AddTorrentFromUrl(string torrentUrl, QBittorrentSettings settings); void AddTorrentFromFile(string fileName, Byte[] fileContent, QBittorrentSettings settings); diff --git a/src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrentProxyV1.cs b/src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrentProxyV1.cs index 3588a2e11..6d1b8d1c2 100644 --- a/src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrentProxyV1.cs +++ b/src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrentProxyV1.cs @@ -87,14 +87,25 @@ public QBittorrentPreferences GetConfig(QBittorrentSettings settings) public List GetTorrents(QBittorrentSettings settings) { - var request = BuildRequest(settings).Resource("/query/torrents") - .AddQueryParam("label", settings.TvCategory) - .AddQueryParam("category", settings.TvCategory); + var request = BuildRequest(settings).Resource("/query/torrents"); + if (settings.TvCategory.IsNotNullOrWhiteSpace()) + { + request.AddQueryParam("label", settings.TvCategory); + request.AddQueryParam("category", settings.TvCategory); + } var response = ProcessRequest>(request, settings); return response; } + public QBittorrentTorrentProperties GetTorrentProperties(string hash, QBittorrentSettings settings) + { + var request = BuildRequest(settings).Resource($"/query/propertiesGeneral/{hash}"); + var response = ProcessRequest(request, settings); + + return response; + } + public void AddTorrentFromUrl(string torrentUrl, QBittorrentSettings settings) { var request = BuildRequest(settings).Resource("/command/download") diff --git a/src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrentProxyV2.cs b/src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrentProxyV2.cs index 52d3c823e..6792c55ba 100644 --- a/src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrentProxyV2.cs +++ b/src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrentProxyV2.cs @@ -86,13 +86,25 @@ public QBittorrentPreferences GetConfig(QBittorrentSettings settings) public List GetTorrents(QBittorrentSettings settings) { - var request = BuildRequest(settings).Resource("/api/v2/torrents/info") - .AddQueryParam("category", settings.TvCategory); + var request = BuildRequest(settings).Resource("/api/v2/torrents/info"); + if (settings.TvCategory.IsNotNullOrWhiteSpace()) + { + request.AddQueryParam("category", settings.TvCategory); + } var response = ProcessRequest>(request, settings); return response; } + public QBittorrentTorrentProperties GetTorrentProperties(string hash, QBittorrentSettings settings) + { + var request = BuildRequest(settings).Resource("/api/v2/torrents/properties") + .AddQueryParam("hash", hash); + var response = ProcessRequest(request, settings); + + return response; + } + public void AddTorrentFromUrl(string torrentUrl, QBittorrentSettings settings) { var request = BuildRequest(settings).Resource("/api/v2/torrents/add") diff --git a/src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrentTorrent.cs b/src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrentTorrent.cs index c1f3b0d09..63e93f523 100644 --- a/src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrentTorrent.cs +++ b/src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrentTorrent.cs @@ -30,10 +30,17 @@ public class QBittorrentTorrent public float RatioLimit { get; set; } = -2; [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) 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 } } diff --git a/src/NzbDrone.Core/Download/Clients/Transmission/TransmissionBase.cs b/src/NzbDrone.Core/Download/Clients/Transmission/TransmissionBase.cs index dd4d8eea4..e583edaa7 100644 --- a/src/NzbDrone.Core/Download/Clients/Transmission/TransmissionBase.cs +++ b/src/NzbDrone.Core/Download/Clients/Transmission/TransmissionBase.cs @@ -165,7 +165,7 @@ protected override string AddFromTorrentFile(RemoteEpisode remoteEpisode, string protected override void Test(List failures) { failures.AddIfNotNull(TestConnection()); - if (failures.Any()) return; + if (failures.HasErrors()) return; failures.AddIfNotNull(TestGetTorrents()); } diff --git a/src/NzbDrone.Core/Download/Clients/rTorrent/RTorrent.cs b/src/NzbDrone.Core/Download/Clients/rTorrent/RTorrent.cs index 535bcd588..8ff087b90 100644 --- a/src/NzbDrone.Core/Download/Clients/rTorrent/RTorrent.cs +++ b/src/NzbDrone.Core/Download/Clients/rTorrent/RTorrent.cs @@ -89,11 +89,11 @@ public override IEnumerable GetItems() foreach (RTorrentTorrent torrent in torrents) { // 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(".")) { - 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(); @@ -163,7 +163,7 @@ public override DownloadClientInfo GetStatus() protected override void Test(List failures) { failures.AddIfNotNull(TestConnection()); - if (failures.Any()) return; + if (failures.HasErrors()) return; failures.AddIfNotNull(TestGetTorrents()); failures.AddIfNotNull(TestDirectory()); } diff --git a/src/NzbDrone.Core/Download/Clients/uTorrent/UTorrent.cs b/src/NzbDrone.Core/Download/Clients/uTorrent/UTorrent.cs index 40da8f483..664c9ba36 100644 --- a/src/NzbDrone.Core/Download/Clients/uTorrent/UTorrent.cs +++ b/src/NzbDrone.Core/Download/Clients/uTorrent/UTorrent.cs @@ -224,7 +224,7 @@ public override DownloadClientInfo GetStatus() protected override void Test(List failures) { failures.AddIfNotNull(TestConnection()); - if (failures.Any()) return; + if (failures.HasErrors()) return; failures.AddIfNotNull(TestGetTorrents()); } diff --git a/src/NzbDrone.Core/Indexers/Newznab/Newznab.cs b/src/NzbDrone.Core/Indexers/Newznab/Newznab.cs index 29a355d44..6805bc1e7 100644 --- a/src/NzbDrone.Core/Indexers/Newznab/Newznab.cs +++ b/src/NzbDrone.Core/Indexers/Newznab/Newznab.cs @@ -8,6 +8,7 @@ using NzbDrone.Core.Configuration; using NzbDrone.Core.Parser; using NzbDrone.Core.ThingiProvider; +using NzbDrone.Core.Validation; namespace NzbDrone.Core.Indexers.Newznab { @@ -103,7 +104,7 @@ private NewznabSettings GetSettings(string url, string apiPath = null, int[] cat protected override void Test(List failures) { base.Test(failures); - if (failures.Any()) return; + if (failures.HasErrors()) return; failures.AddIfNotNull(TestCapabilities()); } diff --git a/src/NzbDrone.Core/Indexers/Torznab/Torznab.cs b/src/NzbDrone.Core/Indexers/Torznab/Torznab.cs index 0756ae1b8..f65d2966a 100644 --- a/src/NzbDrone.Core/Indexers/Torznab/Torznab.cs +++ b/src/NzbDrone.Core/Indexers/Torznab/Torznab.cs @@ -9,6 +9,7 @@ using NzbDrone.Core.Indexers.Newznab; using NzbDrone.Core.Parser; using NzbDrone.Core.ThingiProvider; +using NzbDrone.Core.Validation; namespace NzbDrone.Core.Indexers.Torznab { @@ -92,7 +93,7 @@ private TorznabSettings GetSettings(string url, string apiPath = null, int[] cat protected override void Test(List failures) { base.Test(failures); - if (failures.Any()) return; + if (failures.HasErrors()) return; failures.AddIfNotNull(TestCapabilities()); } diff --git a/src/NzbDrone.Core/Validation/NzbDroneValidationExtensions.cs b/src/NzbDrone.Core/Validation/NzbDroneValidationExtensions.cs index f66aed768..648e0316c 100644 --- a/src/NzbDrone.Core/Validation/NzbDroneValidationExtensions.cs +++ b/src/NzbDrone.Core/Validation/NzbDroneValidationExtensions.cs @@ -1,5 +1,7 @@ +using System.Collections.Generic; using System.Linq; using FluentValidation; +using FluentValidation.Results; namespace NzbDrone.Core.Validation { @@ -19,5 +21,21 @@ public static void ThrowOnError(this NzbDroneValidationResult result) throw new ValidationException(result.Errors); } } + + public static bool HasErrors(this List list) + { + foreach (var item in list) + { + var extended = item as NzbDroneValidationFailure; + if (extended != null && extended.IsWarning) + { + continue; + } + + return true; + } + + return false; + } } }