diff --git a/src/NzbDrone.Core/Download/Clients/Nzbget/Nzbget.cs b/src/NzbDrone.Core/Download/Clients/Nzbget/Nzbget.cs index 12a2e0144..49983269d 100644 --- a/src/NzbDrone.Core/Download/Clients/Nzbget/Nzbget.cs +++ b/src/NzbDrone.Core/Download/Clients/Nzbget/Nzbget.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using FluentValidation.Results; using NLog; using NzbDrone.Common; +using NzbDrone.Common.Disk; using NzbDrone.Common.Http; using NzbDrone.Core.Configuration; using NzbDrone.Core.Indexers; @@ -19,11 +20,12 @@ namespace NzbDrone.Core.Download.Clients.Nzbget private readonly IHttpProvider _httpProvider; public Nzbget(INzbgetProxy proxy, + IHttpProvider httpProvider, IConfigService configService, + IDiskProvider diskProvider, IParsingService parsingService, - IHttpProvider httpProvider, Logger logger) - : base(configService, parsingService, logger) + : base(configService, diskProvider, parsingService, logger) { _proxy = proxy; _httpProvider = httpProvider; @@ -259,11 +261,6 @@ namespace NzbDrone.Core.Download.Clients.Nzbget } } - private String GetVersion(string host = null, int port = 0, string username = null, string password = null) - { - return _proxy.GetVersion(Settings); - } - public override ValidationResult Test() { var failures = new List(); @@ -271,6 +268,11 @@ namespace NzbDrone.Core.Download.Clients.Nzbget failures.AddIfNotNull(TestConnection()); failures.AddIfNotNull(TestCategory()); + if (!Settings.TvCategoryLocalPath.IsNullOrWhiteSpace()) + { + failures.AddIfNotNull(TestFolder(Settings.TvCategoryLocalPath, "TvCategoryLocalPath")); + } + return new ValidationResult(failures); } diff --git a/src/NzbDrone.Core/Download/Clients/Pneumatic/Pneumatic.cs b/src/NzbDrone.Core/Download/Clients/Pneumatic/Pneumatic.cs index 8ea07c39b..14424576d 100644 --- a/src/NzbDrone.Core/Download/Clients/Pneumatic/Pneumatic.cs +++ b/src/NzbDrone.Core/Download/Clients/Pneumatic/Pneumatic.cs @@ -17,17 +17,15 @@ namespace NzbDrone.Core.Download.Clients.Pneumatic public class Pneumatic : DownloadClientBase { private readonly IHttpProvider _httpProvider; - private readonly IDiskProvider _diskProvider; public Pneumatic(IHttpProvider httpProvider, - IDiskProvider diskProvider, IConfigService configService, + IDiskProvider diskProvider, IParsingService parsingService, Logger logger) - : base(configService, parsingService, logger) + : base(configService, diskProvider, parsingService, logger) { _httpProvider = httpProvider; - _diskProvider = diskProvider; } public override DownloadProtocol Protocol diff --git a/src/NzbDrone.Core/Download/Clients/Sabnzbd/Responses/SabnzbdConfigResponse.cs b/src/NzbDrone.Core/Download/Clients/Sabnzbd/Responses/SabnzbdConfigResponse.cs new file mode 100644 index 000000000..ae681718a --- /dev/null +++ b/src/NzbDrone.Core/Download/Clients/Sabnzbd/Responses/SabnzbdConfigResponse.cs @@ -0,0 +1,10 @@ +using System; +using System.Collections.Generic; + +namespace NzbDrone.Core.Download.Clients.Sabnzbd.Responses +{ + public class SabnzbdConfigResponse + { + public SabnzbdConfig Config { get; set; } + } +} diff --git a/src/NzbDrone.Core/Download/Clients/Sabnzbd/Sabnzbd.cs b/src/NzbDrone.Core/Download/Clients/Sabnzbd/Sabnzbd.cs index ff4d9d0c2..4b98f55d6 100644 --- a/src/NzbDrone.Core/Download/Clients/Sabnzbd/Sabnzbd.cs +++ b/src/NzbDrone.Core/Download/Clients/Sabnzbd/Sabnzbd.cs @@ -5,6 +5,7 @@ using System.Linq; using FluentValidation.Results; using NLog; using NzbDrone.Common; +using NzbDrone.Common.Disk; using NzbDrone.Common.Http; using NzbDrone.Core.Configuration; using NzbDrone.Core.Indexers; @@ -18,15 +19,16 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd private readonly IHttpProvider _httpProvider; private readonly ISabnzbdProxy _proxy; - public Sabnzbd(IHttpProvider httpProvider, - ISabnzbdProxy proxy, + public Sabnzbd(ISabnzbdProxy proxy, + IHttpProvider httpProvider, IConfigService configService, + IDiskProvider diskProvider, IParsingService parsingService, Logger logger) - : base(configService, parsingService, logger) + : base(configService, diskProvider, parsingService, logger) { - _httpProvider = httpProvider; _proxy = proxy; + _httpProvider = httpProvider; } public override DownloadProtocol Protocol @@ -207,13 +209,67 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd _proxy.RetryDownload(id, Settings); } + protected IEnumerable GetCategories(SabnzbdConfig config) + { + var completeDir = config.Misc.complete_dir.TrimEnd('\\', '/'); + + foreach (var category in config.Categories) + { + var relativeDir = category.Dir.TrimEnd('*'); + + if (relativeDir.IsNullOrWhiteSpace()) + { + category.FullPath = completeDir; + } + else if (completeDir.StartsWith("/")) + { // Process remote Linux paths irrespective of our own OS. + if (relativeDir.StartsWith("/")) + { + category.FullPath = relativeDir; + } + else + { + category.FullPath = completeDir + "/" + relativeDir; + } + } + else + { // Process remote Windows paths irrespective of our own OS. + if (relativeDir.StartsWith("\\") || relativeDir.Contains(':')) + { + category.FullPath = relativeDir; + } + else + { + category.FullPath = completeDir + "\\" + relativeDir; + } + } + + yield return category; + } + } + public override DownloadClientStatus GetStatus() { + var config = _proxy.GetConfig(Settings); + var categories = GetCategories(config).ToArray(); + + var category = categories.FirstOrDefault(v => v.Name == Settings.TvCategory); + + if (category == null) + { + category = categories.FirstOrDefault(v => v.Name == "*"); + } + var status = new DownloadClientStatus { IsLocalhost = Settings.Host == "127.0.0.1" || Settings.Host == "localhost" }; + if (category != null) + { + status.OutputRootFolders = new List { category.FullPath }; + } + return status; } @@ -224,6 +280,11 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd failures.AddIfNotNull(TestConnection()); failures.AddIfNotNull(TestCategory()); + if (!Settings.TvCategoryLocalPath.IsNullOrWhiteSpace()) + { + failures.AddIfNotNull(TestFolder(Settings.TvCategoryLocalPath, "TvCategoryLocalPath")); + } + return new ValidationResult(failures); } @@ -231,7 +292,7 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd { try { - _proxy.GetCategories(Settings); + _proxy.GetVersion(Settings); } catch (Exception ex) { @@ -244,11 +305,32 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd private ValidationFailure TestCategory() { - var categories = _proxy.GetCategories(Settings); + var config = this._proxy.GetConfig(Settings); + var category = GetCategories(config).FirstOrDefault((SabnzbdCategory v) => v.Name == Settings.TvCategory); - if (!Settings.TvCategory.IsNullOrWhiteSpace() && !categories.Any(v => v == Settings.TvCategory)) + if (category != null) { - return new ValidationFailure("TvCategory", "Category does not exist"); + if (category.Dir.EndsWith("*")) + { + return new ValidationFailure(String.Empty, String.Format("Remove * from Sabnzbd '{2}' category Folder/Path so job folders will be created", Settings.Host, Settings.Port, Settings.TvCategory)); + } + } + else + { + if (!Settings.TvCategory.IsNullOrWhiteSpace()) + { + return new ValidationFailure("TvCategory", String.Format("Category does not exist", Settings.Host, Settings.Port)); + } + } + + if (config.Misc.enable_tv_sorting) + { + if (!config.Misc.tv_categories.Any() || + config.Misc.tv_categories.Contains(Settings.TvCategory) || + (Settings.TvCategory.IsNullOrWhiteSpace() && config.Misc.tv_categories.Contains("Default"))) + { + return new ValidationFailure(String.Empty, String.Format("Disable TV Sorting for the '{2}' category", Settings.Host, Settings.Port, Settings.TvCategory)); + } } return null; diff --git a/src/NzbDrone.Core/Download/Clients/Sabnzbd/SabnzbdCategory.cs b/src/NzbDrone.Core/Download/Clients/Sabnzbd/SabnzbdCategory.cs new file mode 100644 index 000000000..15913c620 --- /dev/null +++ b/src/NzbDrone.Core/Download/Clients/Sabnzbd/SabnzbdCategory.cs @@ -0,0 +1,34 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace NzbDrone.Core.Download.Clients.Sabnzbd +{ + public class SabnzbdConfig + { + public SabnzbdConfigMisc Misc { get; set; } + + public List Categories { get; set; } + + public List Servers { get; set; } + } + + public class SabnzbdConfigMisc + { + public String complete_dir { get; set; } + public String[] tv_categories { get; set; } + public Boolean enable_tv_sorting { get; set; } + } + + public class SabnzbdCategory + { + public Int32 Priority { get; set; } + public String PP { get; set; } + public String Name { get; set; } + public String Script { get; set; } + public String Dir { get; set; } + + public String FullPath { get; set; } + } +} diff --git a/src/NzbDrone.Core/Download/Clients/Sabnzbd/SabnzbdProxy.cs b/src/NzbDrone.Core/Download/Clients/Sabnzbd/SabnzbdProxy.cs index 00e943721..90acd4cd6 100644 --- a/src/NzbDrone.Core/Download/Clients/Sabnzbd/SabnzbdProxy.cs +++ b/src/NzbDrone.Core/Download/Clients/Sabnzbd/SabnzbdProxy.cs @@ -18,7 +18,7 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd void RemoveFrom(string source, string id, SabnzbdSettings settings); string ProcessRequest(IRestRequest restRequest, string action, SabnzbdSettings settings); SabnzbdVersionResponse GetVersion(SabnzbdSettings settings); - List GetCategories(SabnzbdSettings settings); + SabnzbdConfig GetConfig(SabnzbdSettings settings); SabnzbdQueue GetQueue(int start, int limit, SabnzbdSettings settings); SabnzbdHistory GetHistory(int start, int limit, SabnzbdSettings settings); void RetryDownload(string id, SabnzbdSettings settings); @@ -85,14 +85,14 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd return response; } - public List GetCategories(SabnzbdSettings settings) + public SabnzbdConfig GetConfig(SabnzbdSettings settings) { var request = new RestRequest(); - var action = "mode=get_cats"; + var action = "mode=get_config"; - var response = Json.Deserialize(ProcessRequest(request, action, settings)).Categories; + var response = Json.Deserialize(ProcessRequest(request, action, settings)); - return response; + return response.Config; } public SabnzbdQueue GetQueue(int start, int limit, SabnzbdSettings settings) diff --git a/src/NzbDrone.Core/Download/Clients/UsenetBlackhole/UsenetBlackhole.cs b/src/NzbDrone.Core/Download/Clients/UsenetBlackhole/UsenetBlackhole.cs index d896ba467..442be58e9 100644 --- a/src/NzbDrone.Core/Download/Clients/UsenetBlackhole/UsenetBlackhole.cs +++ b/src/NzbDrone.Core/Download/Clients/UsenetBlackhole/UsenetBlackhole.cs @@ -18,19 +18,17 @@ namespace NzbDrone.Core.Download.Clients.UsenetBlackhole { public class UsenetBlackhole : DownloadClientBase { - private readonly IDiskProvider _diskProvider; private readonly IDiskScanService _diskScanService; private readonly IHttpProvider _httpProvider; - public UsenetBlackhole(IDiskProvider diskProvider, - IDiskScanService diskScanService, + public UsenetBlackhole(IDiskScanService diskScanService, IHttpProvider httpProvider, IConfigService configService, + IDiskProvider diskProvider, IParsingService parsingService, Logger logger) - : base(configService, parsingService, logger) + : base(configService, diskProvider, parsingService, logger) { - _diskProvider = diskProvider; _diskScanService = diskScanService; _httpProvider = httpProvider; } @@ -149,32 +147,10 @@ namespace NzbDrone.Core.Download.Clients.UsenetBlackhole { var failures = new List(); - failures.AddIfNotNull(TestWrite(Settings.NzbFolder, "NzbFolder")); - failures.AddIfNotNull(TestWrite(Settings.WatchFolder, "WatchFolder")); + failures.AddIfNotNull(TestFolder(Settings.NzbFolder, "NzbFolder")); + failures.AddIfNotNull(TestFolder(Settings.WatchFolder, "WatchFolder")); return new ValidationResult(failures); } - - private ValidationFailure TestWrite(String folder, String propertyName) - { - if (!_diskProvider.FolderExists(folder)) - { - return new ValidationFailure(propertyName, "Folder does not exist"); - } - - try - { - var testPath = Path.Combine(folder, "drone_test.txt"); - _diskProvider.WriteAllText(testPath, DateTime.Now.ToString()); - _diskProvider.DeleteFile(testPath); - } - catch (Exception ex) - { - _logger.ErrorException(ex.Message, ex); - return new ValidationFailure(propertyName, "Unable to write to folder"); - } - - return null; - } } } diff --git a/src/NzbDrone.Core/Download/DownloadClientBase.cs b/src/NzbDrone.Core/Download/DownloadClientBase.cs index e0272781b..cf609060a 100644 --- a/src/NzbDrone.Core/Download/DownloadClientBase.cs +++ b/src/NzbDrone.Core/Download/DownloadClientBase.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using FluentValidation.Results; +using NzbDrone.Common.Disk; using NzbDrone.Core.Indexers; using NzbDrone.Core.Parser; using NzbDrone.Core.Parser.Model; @@ -14,7 +15,8 @@ namespace NzbDrone.Core.Download where TSettings : IProviderConfig, new() { protected readonly IConfigService _configService; - private readonly IParsingService _parsingService; + protected readonly IDiskProvider _diskProvider; + protected readonly IParsingService _parsingService; protected readonly Logger _logger; public Type ConfigContract @@ -44,9 +46,10 @@ namespace NzbDrone.Core.Download } } - protected DownloadClientBase(IConfigService configService, IParsingService parsingService, Logger logger) + protected DownloadClientBase(IConfigService configService, IDiskProvider diskProvider, IParsingService parsingService, Logger logger) { _configService = configService; + _diskProvider = diskProvider; _parsingService = parsingService; _logger = logger; } @@ -77,5 +80,30 @@ namespace NzbDrone.Core.Download return remoteEpisode; } + + protected ValidationFailure TestFolder(String folder, String propertyName, Boolean mustBeWritable = true) + { + if (!_diskProvider.FolderExists(folder)) + { + return new ValidationFailure(propertyName, "Folder does not exist"); + } + + if (mustBeWritable) + { + try + { + var testPath = Path.Combine(folder, "drone_test.txt"); + _diskProvider.WriteAllText(testPath, DateTime.Now.ToString()); + _diskProvider.DeleteFile(testPath); + } + catch (Exception ex) + { + _logger.ErrorException(ex.Message, ex); + return new ValidationFailure(propertyName, "Unable to write to folder"); + } + } + + return null; + } } } diff --git a/src/NzbDrone.Core/HealthCheck/Checks/ImportMechanismCheck.cs b/src/NzbDrone.Core/HealthCheck/Checks/ImportMechanismCheck.cs index 1234b8730..6a0ca2f7a 100644 --- a/src/NzbDrone.Core/HealthCheck/Checks/ImportMechanismCheck.cs +++ b/src/NzbDrone.Core/HealthCheck/Checks/ImportMechanismCheck.cs @@ -42,7 +42,11 @@ namespace NzbDrone.Core.HealthCheck.Checks if (downloadClients.All(v => v.downloadClient is Sabnzbd)) { - // With Sabnzbd we cannot check the category settings. + // With Sabnzbd we can check if the category should be changed. + if (downloadClientOutputInDroneFactory) + { + return new HealthCheck(GetType(), HealthCheckResult.Warning, "Enable Completed Download Handling if possible (Sabnzbd - Conflicting Category)", "Migrating-to-Completed-Download-Handling#sabnzbd-conflicting-download-client-category"); + } return new HealthCheck(GetType(), HealthCheckResult.Warning, "Enable Completed Download Handling if possible (Sabnzbd)", "Migrating-to-Completed-Download-Handling#sabnzbd-enable-completed-download-handling"); } diff --git a/src/NzbDrone.Core/NzbDrone.Core.csproj b/src/NzbDrone.Core/NzbDrone.Core.csproj index fbba0c940..34d3e69c3 100644 --- a/src/NzbDrone.Core/NzbDrone.Core.csproj +++ b/src/NzbDrone.Core/NzbDrone.Core.csproj @@ -251,7 +251,6 @@ - @@ -262,8 +261,11 @@ + + +