diff --git a/src/NzbDrone.Core/Download/Clients/Hadouken/HadoukenProxy.cs b/src/NzbDrone.Core/Download/Clients/Hadouken/HadoukenProxy.cs index 4fa1c99e4..faeddd5ee 100644 --- a/src/NzbDrone.Core/Download/Clients/Hadouken/HadoukenProxy.cs +++ b/src/NzbDrone.Core/Download/Clients/Hadouken/HadoukenProxy.cs @@ -69,7 +69,9 @@ namespace NzbDrone.Core.Download.Clients.Hadouken 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 f66dbb365..336f0d8f2 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 @@ namespace NzbDrone.Core.Download.Clients.Hadouken { 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 @@ namespace NzbDrone.Core.Download.Clients.Hadouken [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 @@ namespace NzbDrone.Core.Download.Clients.NzbVortex 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 e35f48f01..f0ea1247e 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 @@ namespace NzbDrone.Core.Download.Clients.NzbVortex { 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 @@ namespace NzbDrone.Core.Download.Clients.NzbVortex [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 Sonarr avoids conflicts with unrelated downloads, but it's optional")] + [FieldDefinition(4, Label = "Group", Type = FieldType.Textbox, HelpText = "Adding a category specific to Sonarr 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 episodes that aired within the last 14 days")] + [FieldDefinition(5, Label = "Recent Priority", Type = FieldType.Select, SelectOptions = typeof(NzbVortexPriority), HelpText = "Priority to use when grabbing episodes that aired within the last 14 days")] public int RecentTvPriority { get; set; } - [FieldDefinition(5, Label = "Older Priority", Type = FieldType.Select, SelectOptions = typeof(NzbVortexPriority), HelpText = "Priority to use when grabbing episodes that aired over 14 days ago")] + [FieldDefinition(6, Label = "Older Priority", Type = FieldType.Select, SelectOptions = typeof(NzbVortexPriority), HelpText = "Priority to use when grabbing episodes that aired over 14 days ago")] public int OlderTvPriority { 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 069781d74..14a632687 100644 --- a/src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrentProxyV1.cs +++ b/src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrentProxyV1.cs @@ -257,7 +257,7 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent 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 8140b9055..b6cb3bc49 100644 --- a/src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrentProxyV2.cs +++ b/src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrentProxyV2.cs @@ -269,7 +269,7 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent 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 69fa8fd2f..460698856 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 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent { 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.TvCategory).Matches(@"^([^\\\/](\/?[^\\\/])*)?$").WithMessage(@"Can not contain '\', '//', or start/end with '/'"); RuleFor(c => c.TvImportedCategory).Matches(@"^([^\\\/](\/?[^\\\/])*)?$").WithMessage(@"Can not contain '\', '//', or start/end with '/'"); @@ -34,28 +36,31 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent [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 Sonarr avoids conflicts with unrelated downloads, but it's optional")] + [FieldDefinition(5, Label = "Category", Type = FieldType.Textbox, HelpText = "Adding a category specific to Sonarr avoids conflicts with unrelated downloads, but it's optional")] public string TvCategory { get; set; } - [FieldDefinition(5, Label = "Post-Import Category", Type = FieldType.Textbox, Advanced = true, HelpText = "Category for Sonarr 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 Sonarr to set after it has imported the download. Leave blank to disable this feature.")] public string TvImportedCategory { 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 RecentTvPriority { 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 OlderTvPriority { 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 @@ namespace NzbDrone.Core.Download.Clients.UTorrent 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 b18bbce23..000b8270b 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 @@ namespace NzbDrone.Core.Download.Clients.UTorrent { 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.TvCategory).NotEmpty(); } } @@ -32,25 +34,28 @@ namespace NzbDrone.Core.Download.Clients.UTorrent [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 Sonarr avoids conflicts with unrelated downloads, but it's optional")] + [FieldDefinition(5, Label = "Category", Type = FieldType.Textbox, HelpText = "Adding a category specific to Sonarr avoids conflicts with unrelated downloads, but it's optional")] public string TvCategory { get; set; } - [FieldDefinition(5, Label = "Post-Import Category", Type = FieldType.Textbox, Advanced = true, HelpText = "Category for Sonarr 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 Sonarr to set after it has imported the download. Leave blank to disable this feature.")] public string TvImportedCategory { 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 RecentTvPriority { 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 OlderTvPriority { 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()