1
0
mirror of https://github.com/Sonarr/Sonarr.git synced 2026-03-05 13:20:20 -05:00

New: Return error if invalid indexer or download client provided for pushed releases

This commit is contained in:
Mark McDowall
2025-12-29 12:45:46 -08:00
parent 4bf02221e6
commit b2f2c21a61
11 changed files with 628 additions and 284 deletions

View File

@@ -0,0 +1,91 @@
using System.Collections.Generic;
using System.Linq;
using FizzWare.NBuilder;
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Core.Download;
using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test.Download
{
[TestFixture]
public class ResolveDownloadClientFixture : CoreTest<DownloadClientFactory>
{
private List<DownloadClientDefinition> _downloadClients;
[SetUp]
public void SetUp()
{
_downloadClients = Builder<DownloadClientDefinition>.CreateListOfSize(3)
.TheFirst(2)
.With(v => v.Enable = true)
.TheNext(1)
.With(v => v.Enable = false)
.Build()
.ToList();
Mocker.GetMock<IDownloadClientRepository>()
.Setup(v => v.All())
.Returns(_downloadClients);
}
[Test]
public void should_throw_if_download_client_with_id_cannot_be_found()
{
Assert.Throws<ResolveDownloadClientException>(() => Subject.ResolveDownloadClient(10, null));
}
[Test]
public void should_throw_if_download_client_with_name_cannot_be_found()
{
Assert.Throws<ResolveDownloadClientException>(() => Subject.ResolveDownloadClient(null, "Not a Real Client"));
}
[Test]
public void should_throw_if_download_client_with_id_does_not_match_download_client_with_name()
{
Assert.Throws<ResolveDownloadClientException>(() => Subject.ResolveDownloadClient(_downloadClients[0].Id, _downloadClients[1].Name));
}
[Test]
public void should_throw_if_download_client_is_not_enabled()
{
Assert.Throws<ResolveDownloadClientException>(() => Subject.ResolveDownloadClient(_downloadClients[2].Id, null));
}
[Test]
public void should_return_download_client_when_only_id_is_provided()
{
var result = Subject.ResolveDownloadClient(_downloadClients[0].Id, null);
result.Should().NotBeNull();
result.Should().Be(_downloadClients[0]);
}
[Test]
public void should_return_download_client_when_only_name_is_provided()
{
var result = Subject.ResolveDownloadClient(null, _downloadClients[0].Name);
result.Should().NotBeNull();
result.Should().Be(_downloadClients[0]);
}
[Test]
public void should_return_download_client_when_id_and_name_provided_for_the_same_download_client()
{
var result = Subject.ResolveDownloadClient(_downloadClients[0].Id, _downloadClients[0].Name);
result.Should().NotBeNull();
result.Should().Be(_downloadClients[0]);
}
[Test]
public void should_return_null_if_both_id_and_name_are_not_provided()
{
var result = Subject.ResolveDownloadClient(null, null);
result.Should().BeNull();
}
}
}

View File

@@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
using System.Linq;
using FizzWare.NBuilder;
using FluentAssertions;
using Moq;
@@ -13,7 +12,7 @@ using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test.Download
{
[TestFixture]
public class DownloadClientProviderFixture : CoreTest<DownloadClientProvider>
public class GetDownloadClientFixture : CoreTest<DownloadClientProvider>
{
private List<IDownloadClient> _downloadClients;
private List<DownloadClientStatus> _blockedProviders;
@@ -326,207 +325,5 @@ namespace NzbDrone.Core.Test.Download
Assert.Throws<DownloadClientUnavailableException>(() => Subject.GetDownloadClient(DownloadProtocol.Torrent, 1));
}
[Test]
public void should_return_all_available_clients_for_protocol()
{
WithUsenetClient();
WithTorrentClient();
WithTorrentClient();
WithTorrentClient();
var clients = Subject.GetDownloadClients(DownloadProtocol.Torrent);
clients.Should().HaveCount(3);
clients.Select(c => c.Definition.Id).Should().BeEquivalentTo(new[] { 2, 3, 4 });
}
[Test]
public void should_return_empty_when_no_clients_available_for_protocol()
{
WithUsenetClient();
WithUsenetClient();
var clients = Subject.GetDownloadClients(DownloadProtocol.Torrent);
clients.Should().BeEmpty();
}
[Test]
public void should_return_clients_ordered_by_priority_then_by_last_used()
{
WithTorrentClient(priority: 1);
WithTorrentClient(priority: 0);
WithTorrentClient(priority: 0);
WithTorrentClient(priority: 2);
var clients = Subject.GetDownloadClients(DownloadProtocol.Torrent);
clients.Should().HaveCount(4);
var clientIds = clients.Select(c => c.Definition.Id).ToArray();
clientIds[0].Should().Be(2);
clientIds[1].Should().Be(3);
clientIds[2].Should().Be(1);
clientIds[3].Should().Be(4);
}
[Test]
public void should_rotate_clients_within_same_priority_based_on_last_used()
{
WithTorrentClient(priority: 0);
WithTorrentClient(priority: 0);
WithTorrentClient(priority: 0);
var clients1 = Subject.GetDownloadClients(DownloadProtocol.Torrent);
clients1.First().Definition.Id.Should().Be(1);
Subject.ReportSuccessfulDownloadClient(DownloadProtocol.Torrent, 2);
var clients2 = Subject.GetDownloadClients(DownloadProtocol.Torrent);
var clientIds = clients2.Select(c => c.Definition.Id).ToArray();
clientIds[0].Should().Be(3);
clientIds[1].Should().Be(1);
clientIds[2].Should().Be(2);
}
[Test]
public void should_filter_clients_by_tags()
{
var seriesTags = new HashSet<int> { 1, 2 };
WithTorrentClient(tags: new HashSet<int> { 1 });
WithTorrentClient(tags: new HashSet<int> { 3 });
WithTorrentClient(tags: new HashSet<int> { 2 });
WithTorrentClient();
var clients = Subject.GetDownloadClients(DownloadProtocol.Torrent, tags: seriesTags);
clients.Should().HaveCount(2);
clients.Select(c => c.Definition.Id).Should().BeEquivalentTo(new[] { 1, 3 });
}
[Test]
public void should_return_non_tagged_clients_when_no_matching_tags()
{
var seriesTags = new HashSet<int> { 5 };
WithTorrentClient(tags: new HashSet<int> { 1 });
WithTorrentClient(tags: new HashSet<int> { 2 });
WithTorrentClient();
WithTorrentClient();
var clients = Subject.GetDownloadClients(DownloadProtocol.Torrent, tags: seriesTags);
clients.Should().HaveCount(2);
clients.Select(c => c.Definition.Id).Should().BeEquivalentTo(new[] { 3, 4 });
}
[Test]
public void should_throw_when_all_clients_have_non_matching_tags()
{
var seriesTags = new HashSet<int> { 5 };
WithTorrentClient(tags: new HashSet<int> { 1 });
WithTorrentClient(tags: new HashSet<int> { 2 });
Assert.Throws<DownloadClientUnavailableException>(() => Subject.GetDownloadClients(DownloadProtocol.Torrent, tags: seriesTags));
}
[Test]
public void should_return_indexer_specific_client_when_specified()
{
WithTorrentClient();
WithTorrentClient();
WithTorrentClient();
WithTorrentIndexer(2);
var clients = Subject.GetDownloadClients(DownloadProtocol.Torrent, indexerId: 1);
clients.Should().HaveCount(1);
clients.First().Definition.Id.Should().Be(2);
}
[Test]
public void should_throw_when_indexer_client_does_not_exist()
{
WithTorrentClient();
WithTorrentClient();
WithTorrentIndexer(5);
Assert.Throws<DownloadClientUnavailableException>(() => Subject.GetDownloadClients(DownloadProtocol.Torrent, indexerId: 1));
}
[Test]
public void should_filter_blocked_clients_when_requested()
{
WithTorrentClient();
WithTorrentClient();
WithTorrentClient();
GivenBlockedClient(2);
var clients = Subject.GetDownloadClients(DownloadProtocol.Torrent, filterBlockedClients: true);
clients.Should().HaveCount(2);
clients.Select(c => c.Definition.Id).Should().BeEquivalentTo(new[] { 1, 3 });
}
[Test]
public void should_throw_when_all_clients_blocked_and_filter_enabled()
{
WithTorrentClient();
WithTorrentClient();
GivenBlockedClient(1);
GivenBlockedClient(2);
Assert.Throws<DownloadClientUnavailableException>(() => Subject.GetDownloadClients(DownloadProtocol.Torrent, filterBlockedClients: true));
}
[Test]
public void should_return_blocked_clients_when_filter_disabled()
{
WithTorrentClient();
WithTorrentClient();
GivenBlockedClient(1);
GivenBlockedClient(2);
var clients = Subject.GetDownloadClients(DownloadProtocol.Torrent, filterBlockedClients: false);
clients.Should().HaveCount(2);
clients.Select(c => c.Definition.Id).Should().BeEquivalentTo(new[] { 1, 2 });
}
[Test]
public void should_throw_when_indexer_client_is_blocked_and_filter_enabled()
{
WithTorrentClient();
WithTorrentClient();
WithTorrentIndexer(2);
GivenBlockedClient(2);
Assert.Throws<DownloadClientUnavailableException>(() => Subject.GetDownloadClients(DownloadProtocol.Torrent, indexerId: 1, filterBlockedClients: true));
}
[Test]
public void should_combine_tags_and_priority_filtering()
{
var seriesTags = new HashSet<int> { 1 };
WithTorrentClient(priority: 1, tags: new HashSet<int> { 1 });
WithTorrentClient(priority: 0, tags: new HashSet<int> { 1 });
WithTorrentClient(priority: 0, tags: new HashSet<int> { 2 });
WithTorrentClient(priority: 2);
var clients = Subject.GetDownloadClients(DownloadProtocol.Torrent, tags: seriesTags);
clients.Should().HaveCount(2);
var clientIds = clients.Select(c => c.Definition.Id).ToArray();
clientIds[0].Should().Be(2);
clientIds[1].Should().Be(1);
}
}
}

View File

@@ -0,0 +1,296 @@
using System;
using System.Collections.Generic;
using System.Linq;
using FizzWare.NBuilder;
using FluentAssertions;
using Moq;
using NUnit.Framework;
using NzbDrone.Core.Download;
using NzbDrone.Core.Download.Clients;
using NzbDrone.Core.Indexers;
using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test.Download
{
[TestFixture]
public class GetDownloadClientsFixture : 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, HashSet<int> tags = null)
{
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)
.With(v => v.Tags = tags ?? new HashSet<int>())
.Build());
_downloadClients.Add(mock.Object);
mock.SetupGet(v => v.Protocol).Returns(DownloadProtocol.Usenet);
return mock;
}
private Mock<IDownloadClient> WithTorrentClient(int priority = 0, HashSet<int> tags = null)
{
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)
.With(v => v.Tags = tags ?? new HashSet<int>())
.Build());
_downloadClients.Add(mock.Object);
mock.SetupGet(v => v.Protocol).Returns(DownloadProtocol.Torrent);
return mock;
}
private void WithTorrentIndexer(int downloadClientId)
{
Mocker.GetMock<IIndexerFactory>()
.Setup(v => v.Find(It.IsAny<int>()))
.Returns(Builder<IndexerDefinition>
.CreateNew()
.With(v => v.Id = _nextId++)
.With(v => v.DownloadClientId = downloadClientId)
.Build());
}
private void GivenBlockedClient(int id)
{
_blockedProviders.Add(new DownloadClientStatus
{
ProviderId = id,
DisabledTill = DateTime.UtcNow.AddHours(3)
});
}
[Test]
public void should_return_all_available_clients_for_protocol()
{
WithUsenetClient();
WithTorrentClient();
WithTorrentClient();
WithTorrentClient();
var clients = Subject.GetDownloadClients(DownloadProtocol.Torrent);
clients.Should().HaveCount(3);
clients.Select(c => c.Definition.Id).Should().BeEquivalentTo(new[] { 2, 3, 4 });
}
[Test]
public void should_return_empty_when_no_clients_available_for_protocol()
{
WithUsenetClient();
WithUsenetClient();
var clients = Subject.GetDownloadClients(DownloadProtocol.Torrent);
clients.Should().BeEmpty();
}
[Test]
public void should_return_clients_ordered_by_priority_then_by_last_used()
{
WithTorrentClient(priority: 1);
WithTorrentClient(priority: 0);
WithTorrentClient(priority: 0);
WithTorrentClient(priority: 2);
var clients = Subject.GetDownloadClients(DownloadProtocol.Torrent);
clients.Should().HaveCount(4);
var clientIds = clients.Select(c => c.Definition.Id).ToArray();
clientIds[0].Should().Be(2);
clientIds[1].Should().Be(3);
clientIds[2].Should().Be(1);
clientIds[3].Should().Be(4);
}
[Test]
public void should_rotate_clients_within_same_priority_based_on_last_used()
{
WithTorrentClient(priority: 0);
WithTorrentClient(priority: 0);
WithTorrentClient(priority: 0);
var clients1 = Subject.GetDownloadClients(DownloadProtocol.Torrent);
clients1.First().Definition.Id.Should().Be(1);
Subject.ReportSuccessfulDownloadClient(DownloadProtocol.Torrent, 2);
var clients2 = Subject.GetDownloadClients(DownloadProtocol.Torrent);
var clientIds = clients2.Select(c => c.Definition.Id).ToArray();
clientIds[0].Should().Be(3);
clientIds[1].Should().Be(1);
clientIds[2].Should().Be(2);
}
[Test]
public void should_filter_clients_by_tags()
{
var seriesTags = new HashSet<int> { 1, 2 };
WithTorrentClient(tags: new HashSet<int> { 1 });
WithTorrentClient(tags: new HashSet<int> { 3 });
WithTorrentClient(tags: new HashSet<int> { 2 });
WithTorrentClient();
var clients = Subject.GetDownloadClients(DownloadProtocol.Torrent, tags: seriesTags);
clients.Should().HaveCount(2);
clients.Select(c => c.Definition.Id).Should().BeEquivalentTo(new[] { 1, 3 });
}
[Test]
public void should_return_non_tagged_clients_when_no_matching_tags()
{
var seriesTags = new HashSet<int> { 5 };
WithTorrentClient(tags: new HashSet<int> { 1 });
WithTorrentClient(tags: new HashSet<int> { 2 });
WithTorrentClient();
WithTorrentClient();
var clients = Subject.GetDownloadClients(DownloadProtocol.Torrent, tags: seriesTags);
clients.Should().HaveCount(2);
clients.Select(c => c.Definition.Id).Should().BeEquivalentTo(new[] { 3, 4 });
}
[Test]
public void should_throw_when_all_clients_have_non_matching_tags()
{
var seriesTags = new HashSet<int> { 5 };
WithTorrentClient(tags: new HashSet<int> { 1 });
WithTorrentClient(tags: new HashSet<int> { 2 });
Assert.Throws<DownloadClientUnavailableException>(() => Subject.GetDownloadClients(DownloadProtocol.Torrent, tags: seriesTags));
}
[Test]
public void should_return_indexer_specific_client_when_specified()
{
WithTorrentClient();
WithTorrentClient();
WithTorrentClient();
WithTorrentIndexer(2);
var clients = Subject.GetDownloadClients(DownloadProtocol.Torrent, indexerId: 1);
clients.Should().HaveCount(1);
clients.First().Definition.Id.Should().Be(2);
}
[Test]
public void should_throw_when_indexer_client_does_not_exist()
{
WithTorrentClient();
WithTorrentClient();
WithTorrentIndexer(5);
Assert.Throws<DownloadClientUnavailableException>(() => Subject.GetDownloadClients(DownloadProtocol.Torrent, indexerId: 1));
}
[Test]
public void should_filter_blocked_clients_when_requested()
{
WithTorrentClient();
WithTorrentClient();
WithTorrentClient();
GivenBlockedClient(2);
var clients = Subject.GetDownloadClients(DownloadProtocol.Torrent, filterBlockedClients: true);
clients.Should().HaveCount(2);
clients.Select(c => c.Definition.Id).Should().BeEquivalentTo(new[] { 1, 3 });
}
[Test]
public void should_throw_when_all_clients_blocked_and_filter_enabled()
{
WithTorrentClient();
WithTorrentClient();
GivenBlockedClient(1);
GivenBlockedClient(2);
Assert.Throws<DownloadClientUnavailableException>(() => Subject.GetDownloadClients(DownloadProtocol.Torrent, filterBlockedClients: true));
}
[Test]
public void should_return_blocked_clients_when_filter_disabled()
{
WithTorrentClient();
WithTorrentClient();
GivenBlockedClient(1);
GivenBlockedClient(2);
var clients = Subject.GetDownloadClients(DownloadProtocol.Torrent, filterBlockedClients: false);
clients.Should().HaveCount(2);
clients.Select(c => c.Definition.Id).Should().BeEquivalentTo(new[] { 1, 2 });
}
[Test]
public void should_throw_when_indexer_client_is_blocked_and_filter_enabled()
{
WithTorrentClient();
WithTorrentClient();
WithTorrentIndexer(2);
GivenBlockedClient(2);
Assert.Throws<DownloadClientUnavailableException>(() => Subject.GetDownloadClients(DownloadProtocol.Torrent, indexerId: 1, filterBlockedClients: true));
}
[Test]
public void should_combine_tags_and_priority_filtering()
{
var seriesTags = new HashSet<int> { 1 };
WithTorrentClient(priority: 1, tags: new HashSet<int> { 1 });
WithTorrentClient(priority: 0, tags: new HashSet<int> { 1 });
WithTorrentClient(priority: 0, tags: new HashSet<int> { 2 });
WithTorrentClient(priority: 2);
var clients = Subject.GetDownloadClients(DownloadProtocol.Torrent, tags: seriesTags);
clients.Should().HaveCount(2);
var clientIds = clients.Select(c => c.Definition.Id).ToArray();
clientIds[0].Should().Be(2);
clientIds[1].Should().Be(1);
}
}
}

View File

@@ -0,0 +1,81 @@
using System.Collections.Generic;
using System.Linq;
using FizzWare.NBuilder;
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Core.Indexers;
using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test.Indexer
{
[TestFixture]
public class ResolveIndexerFixture : CoreTest<IndexerFactory>
{
private List<IndexerDefinition> _indexers;
[SetUp]
public void SetUp()
{
_indexers = Builder<IndexerDefinition>.CreateListOfSize(3)
.Build()
.ToList();
Mocker.GetMock<IIndexerRepository>()
.Setup(v => v.All())
.Returns(_indexers);
}
[Test]
public void should_throw_if_indexer_with_id_cannot_be_found()
{
Assert.Throws<ResolveIndexerException>(() => Subject.ResolveIndexer(10, null));
}
[Test]
public void should_throw_if_indexer_with_name_cannot_be_found()
{
Assert.Throws<ResolveIndexerException>(() => Subject.ResolveIndexer(null, "Not a Real Client"));
}
[Test]
public void should_throw_if_indexer_with_id_does_not_match_indexer_with_name()
{
Assert.Throws<ResolveIndexerException>(() => Subject.ResolveIndexer(_indexers[0].Id, _indexers[1].Name));
}
[Test]
public void should_return_indexer_when_only_id_is_provided()
{
var result = Subject.ResolveIndexer(_indexers[0].Id, null);
result.Should().NotBeNull();
result.Should().Be(_indexers[0]);
}
[Test]
public void should_return_indexer_when_only_name_is_provided()
{
var result = Subject.ResolveIndexer(null, _indexers[0].Name);
result.Should().NotBeNull();
result.Should().Be(_indexers[0]);
}
[Test]
public void should_return_indexer_when_id_and_name_provided_for_the_same_indexer()
{
var result = Subject.ResolveIndexer(_indexers[0].Id, _indexers[0].Name);
result.Should().NotBeNull();
result.Should().Be(_indexers[0]);
}
[Test]
public void should_return_null_if_both_id_and_name_are_not_provided()
{
var result = Subject.ResolveIndexer(null, null);
result.Should().BeNull();
}
}
}

View File

@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Linq;
using FluentValidation.Results;
using NLog;
using NzbDrone.Common.Extensions;
using NzbDrone.Core.Messaging.Events;
using NzbDrone.Core.ThingiProvider;
@@ -11,6 +12,7 @@ namespace NzbDrone.Core.Download
public interface IDownloadClientFactory : IProviderFactory<IDownloadClient, DownloadClientDefinition>
{
List<IDownloadClient> DownloadHandlingEnabled(bool filterBlockedClients = true);
DownloadClientDefinition ResolveDownloadClient(int? id, string name);
}
public class DownloadClientFactory : ProviderFactory<IDownloadClient, DownloadClientDefinition>, IDownloadClientFactory
@@ -54,6 +56,42 @@ namespace NzbDrone.Core.Download
return enabledClients.ToList();
}
public DownloadClientDefinition ResolveDownloadClient(int? id, string name)
{
var all = All();
var clientByName = name.IsNullOrWhiteSpace() ? null : all.FirstOrDefault(c => c.Name.EqualsIgnoreCase(name));
var clientById = id.HasValue ? all.FirstOrDefault(c => c.Id == id.Value) : null;
if (id.HasValue && clientById == null)
{
throw new ResolveDownloadClientException("Download client with ID '{0}' could not be found", id.Value);
}
if (name.IsNotNullOrWhiteSpace() && clientByName == null)
{
throw new ResolveDownloadClientException("Download client with name '{0}' could not be found", name);
}
if (clientByName == null && clientById == null)
{
return null;
}
if (clientByName != null && clientById != null && clientByName.Id != clientById.Id)
{
throw new ResolveDownloadClientException("Download client with name '{0}' does not match download client with ID '{1}'", name, id.Value);
}
var client = clientById ?? clientByName;
if (!client.Enable)
{
throw new ResolveDownloadClientException("Download client '{0}' ({1}) is not enabled", client.Name, id);
}
return client;
}
private IEnumerable<IDownloadClient> FilterBlockedClients(IEnumerable<IDownloadClient> clients)
{
var blockedClients = _downloadClientStatusService.GetBlockedProviders().ToDictionary(v => v.ProviderId, v => v);

View File

@@ -121,6 +121,42 @@ namespace NzbDrone.Core.Download
_lastUsedDownloadClient.Set(downloadProtocol.ToString(), downloadClientId);
}
public DownloadClientDefinition ResolveDownloadClient(int? downloadClientId, string downloadClientName)
{
var all = _downloadClientFactory.All();
var clientByName = downloadClientName.IsNullOrWhiteSpace() ? null : all.FirstOrDefault(c => c.Name.EqualsIgnoreCase(downloadClientName));
var clientById = downloadClientId.HasValue ? all.FirstOrDefault(c => c.Id == downloadClientId.Value) : null;
if (downloadClientId.HasValue && clientById == null)
{
throw new ResolveDownloadClientException("Download client with ID '{0}' could not be found", downloadClientId.Value);
}
if (downloadClientName.IsNotNullOrWhiteSpace() && clientByName == null)
{
throw new ResolveDownloadClientException("Download client with name '{0}' could not be found", downloadClientName);
}
if (clientByName == null && clientById == null)
{
return null;
}
if (clientByName != null && clientById != null && clientByName.Id != clientById.Id)
{
throw new ResolveDownloadClientException("Download client with name '{0}' does not match download client with ID '{1}'", downloadClientName, downloadClientId.Value);
}
var client = clientById ?? clientByName;
if (!client.Enable)
{
throw new ResolveDownloadClientException("Download client '{0}' ({1}) is not enabled", client.Name, downloadClientId);
}
return clientById ?? clientByName;
}
private IEnumerable<IDownloadClient> FilterBlockedDownloadClients(IEnumerable<IDownloadClient> clients)
{
var blockedClients = _downloadClientStatusService.GetBlockedProviders().ToDictionary(v => v.ProviderId, v => v);

View File

@@ -0,0 +1,12 @@
using System.Net;
using NzbDrone.Core.Exceptions;
namespace NzbDrone.Core.Download;
public class ResolveDownloadClientException : NzbDroneClientException
{
public ResolveDownloadClientException(string message, params object[] args)
: base(HttpStatusCode.BadRequest, message, args)
{
}
}

View File

@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Linq;
using FluentValidation.Results;
using NLog;
using NzbDrone.Common.Extensions;
using NzbDrone.Core.Messaging.Events;
using NzbDrone.Core.ThingiProvider;
@@ -14,6 +15,7 @@ namespace NzbDrone.Core.Indexers
List<IIndexer> AutomaticSearchEnabled(bool filterBlockedIndexers = true);
List<IIndexer> InteractiveSearchEnabled(bool filterBlockedIndexers = true);
IndexerDefinition FindByName(string name);
IndexerDefinition ResolveIndexer(int? id, string name);
}
public class IndexerFactory : ProviderFactory<IIndexer, IndexerDefinition>, IIndexerFactory
@@ -90,6 +92,35 @@ namespace NzbDrone.Core.Indexers
return _indexerRepository.FindByName(name);
}
public IndexerDefinition ResolveIndexer(int? id, string name)
{
var all = All();
var clientByName = name.IsNullOrWhiteSpace() ? null : all.FirstOrDefault(c => c.Name.EqualsIgnoreCase(name));
var clientById = id.HasValue ? all.FirstOrDefault(c => c.Id == id.Value) : null;
if (id.HasValue && clientById == null)
{
throw new ResolveIndexerException("Indexer with ID '{0}' could not be found", id.Value);
}
if (name.IsNotNullOrWhiteSpace() && clientByName == null)
{
throw new ResolveIndexerException("Indexer with name '{0}' could not be found", name);
}
if (clientByName == null && clientById == null)
{
return null;
}
if (clientByName != null && clientById != null && clientByName.Id != clientById.Id)
{
throw new ResolveIndexerException("Indexer with name '{0}' does not match Indexerwith ID '{1}'", name, id.Value);
}
return clientById ?? clientByName;
}
private IEnumerable<IIndexer> FilterBlockedIndexers(IEnumerable<IIndexer> indexers)
{
var blockedIndexers = _indexerStatusService.GetBlockedProviders().ToDictionary(v => v.ProviderId, v => v);

View File

@@ -0,0 +1,12 @@
using System.Net;
using NzbDrone.Core.Exceptions;
namespace NzbDrone.Core.Indexers;
public class ResolveIndexerException : NzbDroneClientException
{
public ResolveIndexerException(string message, params object[] args)
: base(HttpStatusCode.BadRequest, message, args)
{
}
}

View File

@@ -5,7 +5,6 @@ using FluentValidation.Results;
using Microsoft.AspNetCore.Mvc;
using NLog;
using NzbDrone.Common.Extensions;
using NzbDrone.Core.Datastore;
using NzbDrone.Core.DecisionEngine;
using NzbDrone.Core.Download;
using NzbDrone.Core.Indexers;
@@ -84,59 +83,35 @@ namespace Sonarr.Api.V3.Indexers
private void ResolveIndexer(ReleaseInfo release)
{
if (release.IndexerId == 0 && release.Indexer.IsNotNullOrWhiteSpace())
{
var indexer = _indexerFactory.All().FirstOrDefault(v => v.Name.EqualsIgnoreCase(release.Indexer));
var indexer = _indexerFactory.ResolveIndexer(release.IndexerId, release.Indexer);
if (indexer != null)
{
release.IndexerId = indexer.Id;
_logger.Debug("Push Release {0} associated with indexer {1} - {2}.", release.Title, release.IndexerId, release.Indexer);
}
else
{
_logger.Debug("Push Release {0} not associated with known indexer {1}.", release.Title, release.Indexer);
}
}
else if (release.IndexerId != 0 && release.Indexer.IsNullOrWhiteSpace())
if (indexer == null)
{
try
{
var indexer = _indexerFactory.Get(release.IndexerId);
release.Indexer = indexer.Name;
_logger.Debug("Push Release {0} associated with indexer {1} - {2}.", release.Title, release.IndexerId, release.Indexer);
}
catch (ModelNotFoundException)
{
_logger.Debug("Push Release {0} not associated with known indexer {1}.", release.Title, release.IndexerId);
release.IndexerId = 0;
}
_logger.Debug("Push Release {0} not associated with an indexer.", release.Title);
}
else
{
_logger.Debug("Push Release {0} not associated with an indexer.", release.Title);
_logger.Debug("Push Release {0} associated with indexer '{1} ({2})", release.Title, indexer.Name, indexer.Id);
release.IndexerId = indexer.Id;
release.Indexer = indexer.Name;
}
}
private int? ResolveDownloadClientId(ReleaseResource release)
{
var downloadClientId = release.DownloadClientId.GetValueOrDefault();
var downloadClient = _downloadClientFactory.ResolveDownloadClient(release.DownloadClientId, release.DownloadClient);
if (downloadClientId == 0 && release.DownloadClient.IsNotNullOrWhiteSpace())
if (downloadClient == null)
{
var downloadClient = _downloadClientFactory.All().FirstOrDefault(v => v.Name.EqualsIgnoreCase(release.DownloadClient));
if (downloadClient != null)
{
_logger.Debug("Push Release {0} associated with download client {1} - {2}.", release.Title, downloadClientId, release.DownloadClient);
return downloadClient.Id;
}
_logger.Debug("Push Release {0} not associated with known download client {1}.", release.Title, release.DownloadClient);
_logger.Debug("Push Release {0} not associated with a download client.", release.Title);
}
else
{
_logger.Debug("Push Release {0} associated with download client '{1} ({2})", release.Title, downloadClient.Name, downloadClient.Id);
}
return release.DownloadClientId;
return downloadClient?.Id;
}
}
}

View File

@@ -3,7 +3,6 @@ using FluentValidation.Results;
using Microsoft.AspNetCore.Mvc;
using NLog;
using NzbDrone.Common.Extensions;
using NzbDrone.Core.Datastore;
using NzbDrone.Core.DecisionEngine;
using NzbDrone.Core.Download;
using NzbDrone.Core.Indexers;
@@ -86,58 +85,34 @@ public class ReleasePushController : RestController<ReleasePushResource>
private void ResolveIndexer(ReleaseInfo release)
{
if (release.IndexerId == 0 && release.Indexer.IsNotNullOrWhiteSpace())
{
var indexer = _indexerFactory.All().FirstOrDefault(v => v.Name.EqualsIgnoreCase(release.Indexer));
var indexer = _indexerFactory.ResolveIndexer(release.IndexerId, release.Indexer);
if (indexer != null)
{
release.IndexerId = indexer.Id;
_logger.Debug("Push Release {0} associated with indexer {1} - {2}.", release.Title, release.IndexerId, release.Indexer);
}
else
{
_logger.Debug("Push Release {0} not associated with known indexer {1}.", release.Title, release.Indexer);
}
}
else if (release.IndexerId != 0 && release.Indexer.IsNullOrWhiteSpace())
if (indexer == null)
{
try
{
var indexer = _indexerFactory.Get(release.IndexerId);
release.Indexer = indexer.Name;
_logger.Debug("Push Release {0} associated with indexer {1} - {2}.", release.Title, release.IndexerId, release.Indexer);
}
catch (ModelNotFoundException)
{
_logger.Debug("Push Release {0} not associated with known indexer {1}.", release.Title, release.IndexerId);
release.IndexerId = 0;
}
_logger.Debug("Push Release {0} not associated with an indexer.", release.Title);
}
else
{
_logger.Debug("Push Release {0} not associated with an indexer.", release.Title);
_logger.Debug("Push Release {0} associated with indexer '{1} ({2})", release.Title, indexer.Name, indexer.Id);
release.IndexerId = indexer.Id;
release.Indexer = indexer.Name;
}
}
private int? ResolveDownloadClientId(ReleasePushResource release)
{
var downloadClientId = release.DownloadClientId.GetValueOrDefault();
var downloadClient = _downloadClientFactory.ResolveDownloadClient(release.DownloadClientId, release.DownloadClientName);
if (downloadClientId == 0 && release.DownloadClientName.IsNotNullOrWhiteSpace())
if (downloadClient == null)
{
var downloadClient = _downloadClientFactory.All().FirstOrDefault(v => v.Name.EqualsIgnoreCase(release.DownloadClientName));
if (downloadClient != null)
{
_logger.Debug("Push Release {0} associated with download client {1} - {2}.", release.Title, downloadClientId, release.DownloadClientName);
return downloadClient.Id;
}
_logger.Debug("Push Release {0} not associated with known download client {1}.", release.Title, release.DownloadClientName);
_logger.Debug("Push Release {0} not associated with a download client.", release.Title);
}
else
{
_logger.Debug("Push Release {0} associated with download client '{1} ({2})", release.Title, downloadClient.Name, downloadClient.Id);
}
return release.DownloadClientId;
return downloadClient?.Id;
}
}