From f3435f904643e1d2751dd10ea7ca373f3b5ceaba Mon Sep 17 00:00:00 2001 From: Taloth Saldono Date: Thu, 30 May 2019 01:42:34 +0200 Subject: [PATCH] Added Client Priority with matching ui. --- frontend/src/Components/Form/TextInput.js | 6 +++ .../DownloadClients/DownloadClient.js | 14 +++++- .../EditDownloadClientModalContent.js | 18 +++++++ .../DownloadClient/DownloadClientModule.cs | 2 + .../DownloadClient/DownloadClientResource.cs | 1 + .../Download/DownloadClientProviderFixture.cs | 48 ++++++++++++++++++- .../132_add_download_client_priority.cs | 45 +++++++++++++++++ .../Download/DownloadClientDefinition.cs | 1 + .../Download/DownloadClientProvider.cs | 5 ++ src/NzbDrone.Core/NzbDrone.Core.csproj | 1 + .../DownloadClient/DownloadClientResource.cs | 3 ++ 11 files changed, 141 insertions(+), 3 deletions(-) create mode 100644 src/NzbDrone.Core/Datastore/Migration/132_add_download_client_priority.cs diff --git a/frontend/src/Components/Form/TextInput.js b/frontend/src/Components/Form/TextInput.js index 9feefa616fd..cc0cbca024a 100644 --- a/frontend/src/Components/Form/TextInput.js +++ b/frontend/src/Components/Form/TextInput.js @@ -128,6 +128,8 @@ class TextInput extends Component { hasWarning, hasButton, step, + min, + max, onBlur } = this.props; @@ -148,6 +150,8 @@ class TextInput extends Component { name={name} value={value} step={step} + min={min} + max={max} onChange={this.onChange} onFocus={this.onFocus} onBlur={onBlur} @@ -171,6 +175,8 @@ TextInput.propTypes = { hasWarning: PropTypes.bool, hasButton: PropTypes.bool, step: PropTypes.number, + min: PropTypes.number, + max: PropTypes.number, onChange: PropTypes.func.isRequired, onFocus: PropTypes.func, onBlur: PropTypes.func, diff --git a/frontend/src/Settings/DownloadClients/DownloadClients/DownloadClient.js b/frontend/src/Settings/DownloadClients/DownloadClients/DownloadClient.js index 6a86fef16fe..de4b2ac92fe 100644 --- a/frontend/src/Settings/DownloadClients/DownloadClients/DownloadClient.js +++ b/frontend/src/Settings/DownloadClients/DownloadClients/DownloadClient.js @@ -54,7 +54,8 @@ class DownloadClient extends Component { const { id, name, - enable + enable, + priority } = this.props; return ( @@ -80,6 +81,16 @@ class DownloadClient extends Component { Disabled } + + { + priority > 1 && + + } + Client Priority + + + + } diff --git a/src/NzbDrone.Api/DownloadClient/DownloadClientModule.cs b/src/NzbDrone.Api/DownloadClient/DownloadClientModule.cs index d7568189ff3..8b5ad25d4a6 100644 --- a/src/NzbDrone.Api/DownloadClient/DownloadClientModule.cs +++ b/src/NzbDrone.Api/DownloadClient/DownloadClientModule.cs @@ -15,6 +15,7 @@ protected override void MapToResource(DownloadClientResource resource, DownloadC resource.Enable = definition.Enable; resource.Protocol = definition.Protocol; + resource.Priority = definition.Priority; } protected override void MapToModel(DownloadClientDefinition definition, DownloadClientResource resource) @@ -23,6 +24,7 @@ protected override void MapToModel(DownloadClientDefinition definition, Download definition.Enable = resource.Enable; definition.Protocol = resource.Protocol; + definition.Priority = resource.Priority; } protected override void Validate(DownloadClientDefinition definition, bool includeWarnings) diff --git a/src/NzbDrone.Api/DownloadClient/DownloadClientResource.cs b/src/NzbDrone.Api/DownloadClient/DownloadClientResource.cs index a7156e08d63..5e268578bf4 100644 --- a/src/NzbDrone.Api/DownloadClient/DownloadClientResource.cs +++ b/src/NzbDrone.Api/DownloadClient/DownloadClientResource.cs @@ -6,5 +6,6 @@ public class DownloadClientResource : ProviderResource { public bool Enable { get; set; } public DownloadProtocol Protocol { get; set; } + public int Priority { get; set; } } } \ No newline at end of file diff --git a/src/NzbDrone.Core.Test/Download/DownloadClientProviderFixture.cs b/src/NzbDrone.Core.Test/Download/DownloadClientProviderFixture.cs index a8e60165eba..27fe13b8106 100644 --- a/src/NzbDrone.Core.Test/Download/DownloadClientProviderFixture.cs +++ b/src/NzbDrone.Core.Test/Download/DownloadClientProviderFixture.cs @@ -35,13 +35,14 @@ public void SetUp() .Returns(_blockedProviders); } - private Mock WithUsenetClient() + private Mock WithUsenetClient(int priority = 0) { var mock = new Mock(MockBehavior.Default); mock.SetupGet(s => s.Definition) .Returns(Builder .CreateNew() .With(v => v.Id = _nextId++) + .With(v => v.Priority = priority) .Build()); _downloadClients.Add(mock.Object); @@ -51,13 +52,14 @@ private Mock WithUsenetClient() return mock; } - private Mock WithTorrentClient() + private Mock WithTorrentClient(int priority = 0) { var mock = new Mock(MockBehavior.Default); mock.SetupGet(s => s.Definition) .Returns(Builder .CreateNew() .With(v => v.Id = _nextId++) + .With(v => v.Priority = priority) .Build()); _downloadClients.Add(mock.Object); @@ -181,5 +183,47 @@ public void should_not_skip_blocked_torrent_client_if_all_blocked() 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); + } } } diff --git a/src/NzbDrone.Core/Datastore/Migration/132_add_download_client_priority.cs b/src/NzbDrone.Core/Datastore/Migration/132_add_download_client_priority.cs new file mode 100644 index 00000000000..7b8c1d153ac --- /dev/null +++ b/src/NzbDrone.Core/Datastore/Migration/132_add_download_client_priority.cs @@ -0,0 +1,45 @@ +using System.Data; +using FluentMigrator; +using NzbDrone.Core.Datastore.Migration.Framework; + +namespace NzbDrone.Core.Datastore.Migration +{ + [Migration(132)] + public class add_download_client_priority : NzbDroneMigrationBase + { + protected override void MainDbUpgrade() + { + Alter.Table("DownloadClients").AddColumn("Priority").AsInt32().WithDefaultValue(1); + Execute.WithConnection(InitPriorityForBackwardCompatibility); + + } + + private void InitPriorityForBackwardCompatibility(IDbConnection conn, IDbTransaction tran) + { + using (var cmd = conn.CreateCommand()) + { + cmd.Transaction = tran; + cmd.CommandText = "SELECT Id FROM DownloadClients WHERE Enable = 1"; + + using (var reader = cmd.ExecuteReader()) + { + int i = 1; + while (reader.Read()) + { + var id = reader.GetInt32(0); + + using (var updateCmd = conn.CreateCommand()) + { + updateCmd.Transaction = tran; + updateCmd.CommandText = "UPDATE DownloadClients SET Priority = ? WHERE Id = ?"; + updateCmd.AddParameter(i++); + updateCmd.AddParameter(id); + + updateCmd.ExecuteNonQuery(); + } + } + } + } + } + } +} diff --git a/src/NzbDrone.Core/Download/DownloadClientDefinition.cs b/src/NzbDrone.Core/Download/DownloadClientDefinition.cs index b81536a53b3..1c0dfa9272f 100644 --- a/src/NzbDrone.Core/Download/DownloadClientDefinition.cs +++ b/src/NzbDrone.Core/Download/DownloadClientDefinition.cs @@ -6,5 +6,6 @@ namespace NzbDrone.Core.Download public class DownloadClientDefinition : ProviderDefinition { public DownloadProtocol Protocol { get; set; } + public int Priority { get; set; } = 1; } } diff --git a/src/NzbDrone.Core/Download/DownloadClientProvider.cs b/src/NzbDrone.Core/Download/DownloadClientProvider.cs index e606ad97294..978f9ea60f5 100644 --- a/src/NzbDrone.Core/Download/DownloadClientProvider.cs +++ b/src/NzbDrone.Core/Download/DownloadClientProvider.cs @@ -50,6 +50,11 @@ public IDownloadClient GetDownloadClient(DownloadProtocol downloadProtocol) } } + // Use the first priority clients first + availableProviders = availableProviders.GroupBy(v => (v.Definition as DownloadClientDefinition).Priority) + .OrderBy(v => v.Key) + .First().OrderBy(v => v.Definition.Id).ToList(); + var lastId = _lastUsedDownloadClient.Find(downloadProtocol.ToString()); var provider = availableProviders.FirstOrDefault(v => v.Definition.Id > lastId) ?? availableProviders.First(); diff --git a/src/NzbDrone.Core/NzbDrone.Core.csproj b/src/NzbDrone.Core/NzbDrone.Core.csproj index dce02434cab..5065c9a093b 100644 --- a/src/NzbDrone.Core/NzbDrone.Core.csproj +++ b/src/NzbDrone.Core/NzbDrone.Core.csproj @@ -136,6 +136,7 @@ + diff --git a/src/Sonarr.Api.V3/DownloadClient/DownloadClientResource.cs b/src/Sonarr.Api.V3/DownloadClient/DownloadClientResource.cs index 71fe480d3cf..43efc111aae 100644 --- a/src/Sonarr.Api.V3/DownloadClient/DownloadClientResource.cs +++ b/src/Sonarr.Api.V3/DownloadClient/DownloadClientResource.cs @@ -7,6 +7,7 @@ public class DownloadClientResource : ProviderResource { public bool Enable { get; set; } public DownloadProtocol Protocol { get; set; } + public int Priority { get; set; } } public class DownloadClientResourceMapper : ProviderResourceMapper @@ -19,6 +20,7 @@ public override DownloadClientResource ToResource(DownloadClientDefinition defin resource.Enable = definition.Enable; resource.Protocol = definition.Protocol; + resource.Priority = definition.Priority; return resource; } @@ -31,6 +33,7 @@ public override DownloadClientDefinition ToModel(DownloadClientResource resource definition.Enable = resource.Enable; definition.Protocol = resource.Protocol; + definition.Priority = resource.Priority; return definition; }