From fc5ab2fd599baf36db40ba8aaf867be579326506 Mon Sep 17 00:00:00 2001 From: Devin Buhl Date: Thu, 7 Nov 2019 11:08:32 -0500 Subject: [PATCH] New: URL Base support for NZBVortex, Hadouken, qBittorrent and uTorrent --- .../Clients/Hadouken/HadoukenProxy.cs | 3 ++- .../Clients/Hadouken/HadoukenSettings.cs | 13 ++++++++---- .../Clients/NzbVortex/NzbVortexProxy.cs | 4 +++- .../Clients/NzbVortex/NzbVortexSettings.cs | 13 ++++++++---- .../Clients/QBittorrent/QBittorrentProxyV1.cs | 2 +- .../Clients/QBittorrent/QBittorrentProxyV2.cs | 2 +- .../QBittorrent/QBittorrentSettings.cs | 21 ++++++++++++------- .../Clients/uTorrent/UTorrentProxy.cs | 2 +- .../Clients/uTorrent/UTorrentSettings.cs | 19 ++++++++++------- 9 files changed, 51 insertions(+), 28 deletions(-) diff --git a/src/NzbDrone.Core/Download/Clients/Hadouken/HadoukenProxy.cs b/src/NzbDrone.Core/Download/Clients/Hadouken/HadoukenProxy.cs index 7dee6fb32..f91a525d7 100644 --- a/src/NzbDrone.Core/Download/Clients/Hadouken/HadoukenProxy.cs +++ b/src/NzbDrone.Core/Download/Clients/Hadouken/HadoukenProxy.cs @@ -69,7 +69,8 @@ public void RemoveTorrentAndData(HadoukenSettings settings, string downloadId) private T ProcessRequest(HadoukenSettings settings, string method, params object[] parameters) { - var baseUrl = HttpRequestBuilder.BuildBaseUrl(settings.UseSsl, settings.Host, settings.Port, "api"); + var baseUrl = HttpRequestBuilder.BuildBaseUrl(settings.UseSsl, settings.Host, settings.Port, settings.UrlBase); + baseUrl = HttpUri.CombinePath(baseUrl, "api"); var requestBuilder = new JsonRpcRequestBuilder(baseUrl, method, parameters); requestBuilder.LogResponseContent = true; requestBuilder.NetworkCredential = new NetworkCredential(settings.Username, settings.Password); diff --git a/src/NzbDrone.Core/Download/Clients/Hadouken/HadoukenSettings.cs b/src/NzbDrone.Core/Download/Clients/Hadouken/HadoukenSettings.cs index a285f7457..c2768f0c9 100644 --- a/src/NzbDrone.Core/Download/Clients/Hadouken/HadoukenSettings.cs +++ b/src/NzbDrone.Core/Download/Clients/Hadouken/HadoukenSettings.cs @@ -1,4 +1,5 @@ using FluentValidation; +using NzbDrone.Common.Extensions; using NzbDrone.Core.Annotations; using NzbDrone.Core.ThingiProvider; using NzbDrone.Core.Validation; @@ -11,6 +12,7 @@ public HadoukenSettingsValidator() { RuleFor(c => c.Host).ValidHost(); RuleFor(c => c.Port).InclusiveBetween(1, 65535); + RuleFor(c => c.UrlBase).ValidUrlBase().When(c => c.UrlBase.IsNotNullOrWhiteSpace()); RuleFor(c => c.Username).NotEmpty() .WithMessage("Username must not be empty."); @@ -37,16 +39,19 @@ public HadoukenSettings() [FieldDefinition(1, Label = "Port", Type = FieldType.Textbox)] public int Port { get; set; } - [FieldDefinition(2, Label = "Username", Type = FieldType.Textbox)] + [FieldDefinition(2, Label = "Url Base", Type = FieldType.Textbox, Advanced = true, HelpText = "Adds a prefix to the Hadouken url, e.g. http://[host]:[port]/[urlBase]/api")] + public string UrlBase { get; set; } + + [FieldDefinition(3, Label = "Username", Type = FieldType.Textbox)] public string Username { get; set; } - [FieldDefinition(3, Label = "Password", Type = FieldType.Password)] + [FieldDefinition(4, Label = "Password", Type = FieldType.Password)] public string Password { get; set; } - [FieldDefinition(4, Label = "Category", Type = FieldType.Textbox)] + [FieldDefinition(5, Label = "Category", Type = FieldType.Textbox)] public string Category { get; set; } - [FieldDefinition(5, Label = "Use SSL", Type = FieldType.Checkbox, Advanced = true)] + [FieldDefinition(6, Label = "Use SSL", Type = FieldType.Checkbox, Advanced = true)] public bool UseSsl { get; set; } public NzbDroneValidationResult Validate() diff --git a/src/NzbDrone.Core/Download/Clients/NzbVortex/NzbVortexProxy.cs b/src/NzbDrone.Core/Download/Clients/NzbVortex/NzbVortexProxy.cs index 854246bc5..c5e8ef4c7 100644 --- a/src/NzbDrone.Core/Download/Clients/NzbVortex/NzbVortexProxy.cs +++ b/src/NzbDrone.Core/Download/Clients/NzbVortex/NzbVortexProxy.cs @@ -116,7 +116,9 @@ public List GetFiles(int id, NzbVortexSettings settings) private HttpRequestBuilder BuildRequest(NzbVortexSettings settings) { - var requestBuilder = new HttpRequestBuilder(true, settings.Host, settings.Port, "api"); + var baseUrl = HttpRequestBuilder.BuildBaseUrl(true, settings.Host, settings.Port, settings.UrlBase); + baseUrl = HttpUri.CombinePath(baseUrl, "api"); + var requestBuilder = new HttpRequestBuilder(baseUrl); requestBuilder.LogResponseContent = true; return requestBuilder; diff --git a/src/NzbDrone.Core/Download/Clients/NzbVortex/NzbVortexSettings.cs b/src/NzbDrone.Core/Download/Clients/NzbVortex/NzbVortexSettings.cs index 20a2bece4..c75cc8166 100644 --- a/src/NzbDrone.Core/Download/Clients/NzbVortex/NzbVortexSettings.cs +++ b/src/NzbDrone.Core/Download/Clients/NzbVortex/NzbVortexSettings.cs @@ -1,4 +1,5 @@ using FluentValidation; +using NzbDrone.Common.Extensions; using NzbDrone.Core.Annotations; using NzbDrone.Core.ThingiProvider; using NzbDrone.Core.Validation; @@ -11,6 +12,7 @@ public NzbVortexSettingsValidator() { RuleFor(c => c.Host).ValidHost(); RuleFor(c => c.Port).InclusiveBetween(1, 65535); + RuleFor(c => c.UrlBase).ValidUrlBase().When(c => c.UrlBase.IsNotNullOrWhiteSpace()); RuleFor(c => c.ApiKey).NotEmpty() .WithMessage("API Key is required"); @@ -40,16 +42,19 @@ public NzbVortexSettings() [FieldDefinition(1, Label = "Port", Type = FieldType.Textbox)] public int Port { get; set; } - [FieldDefinition(2, Label = "API Key", Type = FieldType.Textbox)] + [FieldDefinition(2, Label = "Url Base", Type = FieldType.Textbox, Advanced = true, HelpText = "Adds a prefix to the NZBVortex url, e.g. http://[host]:[port]/[urlBase]/api")] + public string UrlBase { get; set; } + + [FieldDefinition(3, Label = "API Key", Type = FieldType.Textbox)] public string ApiKey { get; set; } - [FieldDefinition(3, Label = "Group", Type = FieldType.Textbox, HelpText = "Adding a category specific to Radarr avoids conflicts with unrelated downloads, but it's optional")] + [FieldDefinition(4, Label = "Group", Type = FieldType.Textbox, HelpText = "Adding a category specific to Radarr avoids conflicts with unrelated downloads, but it's optional")] public string TvCategory { get; set; } - [FieldDefinition(4, Label = "Recent Priority", Type = FieldType.Select, SelectOptions = typeof(NzbVortexPriority), HelpText = "Priority to use when grabbing movies that released within the last 21 days")] + [FieldDefinition(5, Label = "Recent Priority", Type = FieldType.Select, SelectOptions = typeof(NzbVortexPriority), HelpText = "Priority to use when grabbing movies that released within the last 21 days")] public int RecentMoviePriority { get; set; } - [FieldDefinition(5, Label = "Older Priority", Type = FieldType.Select, SelectOptions = typeof(NzbVortexPriority), HelpText = "Priority to use when grabbing movies that released over 21 days ago")] + [FieldDefinition(6, Label = "Older Priority", Type = FieldType.Select, SelectOptions = typeof(NzbVortexPriority), HelpText = "Priority to use when grabbing movies that released over 21 days ago")] public int OlderMoviePriority { get; set; } public NzbDroneValidationResult Validate() diff --git a/src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrentProxyV1.cs b/src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrentProxyV1.cs index 20f2cbd9f..b317ce675 100644 --- a/src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrentProxyV1.cs +++ b/src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrentProxyV1.cs @@ -257,7 +257,7 @@ public void SetForceStart(string hash, bool enabled, QBittorrentSettings setting private HttpRequestBuilder BuildRequest(QBittorrentSettings settings) { - var requestBuilder = new HttpRequestBuilder(settings.UseSsl, settings.Host, settings.Port) + var requestBuilder = new HttpRequestBuilder(settings.UseSsl, settings.Host, settings.Port, settings.UrlBase) { LogResponseContent = true, NetworkCredential = new NetworkCredential(settings.Username, settings.Password) diff --git a/src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrentProxyV2.cs b/src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrentProxyV2.cs index 8a9134607..7350d5f0c 100644 --- a/src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrentProxyV2.cs +++ b/src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrentProxyV2.cs @@ -268,7 +268,7 @@ public void SetForceStart(string hash, bool enabled, QBittorrentSettings setting private HttpRequestBuilder BuildRequest(QBittorrentSettings settings) { - var requestBuilder = new HttpRequestBuilder(settings.UseSsl, settings.Host, settings.Port) + var requestBuilder = new HttpRequestBuilder(settings.UseSsl, settings.Host, settings.Port, settings.UrlBase) { LogResponseContent = true, NetworkCredential = new NetworkCredential(settings.Username, settings.Password) diff --git a/src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrentSettings.cs b/src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrentSettings.cs index 6fd070894..95fb08b38 100644 --- a/src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrentSettings.cs +++ b/src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrentSettings.cs @@ -1,4 +1,5 @@ using FluentValidation; +using NzbDrone.Common.Extensions; using NzbDrone.Core.Annotations; using NzbDrone.Core.ThingiProvider; using NzbDrone.Core.Validation; @@ -11,6 +12,7 @@ public QBittorrentSettingsValidator() { RuleFor(c => c.Host).ValidHost(); RuleFor(c => c.Port).InclusiveBetween(1, 65535); + RuleFor(c => c.UrlBase).ValidUrlBase().When(c => c.UrlBase.IsNotNullOrWhiteSpace()); RuleFor(c => c.MovieCategory).Matches(@"^([^\\\/](\/?[^\\\/])*)?$").WithMessage(@"Can not contain '\', '//', or start/end with '/'"); RuleFor(c => c.MovieImportedCategory).Matches(@"^([^\\\/](\/?[^\\\/])*)?$").WithMessage(@"Can not contain '\', '//', or start/end with '/'"); @@ -34,28 +36,31 @@ public QBittorrentSettings() [FieldDefinition(1, Label = "Port", Type = FieldType.Textbox)] public int Port { get; set; } - [FieldDefinition(2, Label = "Username", Type = FieldType.Textbox)] + [FieldDefinition(2, Label = "Url Base", Type = FieldType.Textbox, Advanced = true, HelpText = "Adds a prefix to the qBittorrent url, e.g. http://[host]:[port]/[urlBase]/api")] + public string UrlBase { get; set; } + + [FieldDefinition(3, Label = "Username", Type = FieldType.Textbox)] public string Username { get; set; } - [FieldDefinition(3, Label = "Password", Type = FieldType.Password)] + [FieldDefinition(4, Label = "Password", Type = FieldType.Password)] public string Password { get; set; } - [FieldDefinition(4, Label = "Category", Type = FieldType.Textbox, HelpText = "Adding a category specific to Radarr avoids conflicts with unrelated downloads, but it's optional")] + [FieldDefinition(5, Label = "Category", Type = FieldType.Textbox, HelpText = "Adding a category specific to Radarr avoids conflicts with unrelated downloads, but it's optional")] public string MovieCategory { get; set; } - [FieldDefinition(5, Label = "Post-Import Category", Type = FieldType.Textbox, Advanced = true, HelpText = "Category for Radarr to set after it has imported the download. Leave blank to disable this feature.")] + [FieldDefinition(6, Label = "Post-Import Category", Type = FieldType.Textbox, Advanced = true, HelpText = "Category for Radarr to set after it has imported the download. Leave blank to disable this feature.")] public string MovieImportedCategory { get; set; } - [FieldDefinition(6, Label = "Recent Priority", Type = FieldType.Select, SelectOptions = typeof(QBittorrentPriority), HelpText = "Priority to use when grabbing episodes that aired within the last 14 days")] + [FieldDefinition(7, Label = "Recent Priority", Type = FieldType.Select, SelectOptions = typeof(QBittorrentPriority), HelpText = "Priority to use when grabbing episodes that aired within the last 14 days")] public int RecentMoviePriority { get; set; } - [FieldDefinition(7, Label = "Older Priority", Type = FieldType.Select, SelectOptions = typeof(QBittorrentPriority), HelpText = "Priority to use when grabbing episodes that aired over 14 days ago")] + [FieldDefinition(8, Label = "Older Priority", Type = FieldType.Select, SelectOptions = typeof(QBittorrentPriority), HelpText = "Priority to use when grabbing episodes that aired over 14 days ago")] public int OlderMoviePriority { get; set; } - [FieldDefinition(8, Label = "Initial State", Type = FieldType.Select, SelectOptions = typeof(QBittorrentState), HelpText = "Initial state for torrents added to qBittorrent")] + [FieldDefinition(9, Label = "Initial State", Type = FieldType.Select, SelectOptions = typeof(QBittorrentState), HelpText = "Initial state for torrents added to qBittorrent")] public int InitialState { get; set; } - [FieldDefinition(9, Label = "Use SSL", Type = FieldType.Checkbox, HelpText = "Use a secure connection. See Options -> Web UI -> 'Use HTTPS instead of HTTP' in qBittorrent.")] + [FieldDefinition(10, Label = "Use SSL", Type = FieldType.Checkbox, HelpText = "Use a secure connection. See Options -> Web UI -> 'Use HTTPS instead of HTTP' in qBittorrent.")] public bool UseSsl { get; set; } public NzbDroneValidationResult Validate() diff --git a/src/NzbDrone.Core/Download/Clients/uTorrent/UTorrentProxy.cs b/src/NzbDrone.Core/Download/Clients/uTorrent/UTorrentProxy.cs index 684078f1a..f8a76892b 100644 --- a/src/NzbDrone.Core/Download/Clients/uTorrent/UTorrentProxy.cs +++ b/src/NzbDrone.Core/Download/Clients/uTorrent/UTorrentProxy.cs @@ -186,7 +186,7 @@ public void SetState(string hash, UTorrentState state, UTorrentSettings settings private HttpRequestBuilder BuildRequest(UTorrentSettings settings) { - var requestBuilder = new HttpRequestBuilder(false, settings.Host, settings.Port) + var requestBuilder = new HttpRequestBuilder(false, settings.Host, settings.Port, settings.UrlBase) .Resource("/gui/") .KeepAlive() .SetHeader("Cache-Control", "no-cache") diff --git a/src/NzbDrone.Core/Download/Clients/uTorrent/UTorrentSettings.cs b/src/NzbDrone.Core/Download/Clients/uTorrent/UTorrentSettings.cs index b6acb7b48..46ffc1068 100644 --- a/src/NzbDrone.Core/Download/Clients/uTorrent/UTorrentSettings.cs +++ b/src/NzbDrone.Core/Download/Clients/uTorrent/UTorrentSettings.cs @@ -1,4 +1,5 @@ using FluentValidation; +using NzbDrone.Common.Extensions; using NzbDrone.Core.Annotations; using NzbDrone.Core.ThingiProvider; using NzbDrone.Core.Validation; @@ -11,6 +12,7 @@ public UTorrentSettingsValidator() { RuleFor(c => c.Host).ValidHost(); RuleFor(c => c.Port).InclusiveBetween(1, 65535); + RuleFor(c => c.UrlBase).ValidUrlBase().When(c => c.UrlBase.IsNotNullOrWhiteSpace()); RuleFor(c => c.MovieCategory).NotEmpty(); } } @@ -32,25 +34,28 @@ public UTorrentSettings() [FieldDefinition(1, Label = "Port", Type = FieldType.Textbox)] public int Port { get; set; } - [FieldDefinition(2, Label = "Username", Type = FieldType.Textbox)] + [FieldDefinition(2, Label = "Url Base", Type = FieldType.Textbox, Advanced = true, HelpText = "Adds a prefix to the uTorrent url, e.g. http://[host]:[port]/[urlBase]/api")] + public string UrlBase { get; set; } + + [FieldDefinition(3, Label = "Username", Type = FieldType.Textbox)] public string Username { get; set; } - [FieldDefinition(3, Label = "Password", Type = FieldType.Password)] + [FieldDefinition(4, Label = "Password", Type = FieldType.Password)] public string Password { get; set; } - [FieldDefinition(4, Label = "Category", Type = FieldType.Textbox, HelpText = "Adding a category specific to Radarr avoids conflicts with unrelated downloads, but it's optional")] + [FieldDefinition(5, Label = "Category", Type = FieldType.Textbox, HelpText = "Adding a category specific to Radarr avoids conflicts with unrelated downloads, but it's optional")] public string MovieCategory { get; set; } - [FieldDefinition(5, Label = "Post-Import Category", Type = FieldType.Textbox, Advanced = true, HelpText = "Category for Radarr to set after it has imported the download. Leave blank to disable this feature.")] + [FieldDefinition(6, Label = "Post-Import Category", Type = FieldType.Textbox, Advanced = true, HelpText = "Category for Radarr to set after it has imported the download. Leave blank to disable this feature.")] public string MovieImportedCategory { get; set; } - [FieldDefinition(6, Label = "Recent Priority", Type = FieldType.Select, SelectOptions = typeof(UTorrentPriority), HelpText = "Priority to use when grabbing episodes that aired within the last 14 days")] + [FieldDefinition(7, Label = "Recent Priority", Type = FieldType.Select, SelectOptions = typeof(UTorrentPriority), HelpText = "Priority to use when grabbing episodes that aired within the last 14 days")] public int RecentMoviePriority { get; set; } - [FieldDefinition(7, Label = "Older Priority", Type = FieldType.Select, SelectOptions = typeof(UTorrentPriority), HelpText = "Priority to use when grabbing episodes that aired over 14 days ago")] + [FieldDefinition(8, Label = "Older Priority", Type = FieldType.Select, SelectOptions = typeof(UTorrentPriority), HelpText = "Priority to use when grabbing episodes that aired over 14 days ago")] public int OlderMoviePriority { get; set; } - [FieldDefinition(8, Label = "Initial State", Type = FieldType.Select, SelectOptions = typeof(UTorrentState), HelpText = "Initial state for torrents added to uTorrent")] + [FieldDefinition(9, Label = "Initial State", Type = FieldType.Select, SelectOptions = typeof(UTorrentState), HelpText = "Initial state for torrents added to uTorrent")] public int IntialState { get; set; } public NzbDroneValidationResult Validate()