diff --git a/src/NzbDrone.Core/Download/Clients/Nzbget/Nzbget.cs b/src/NzbDrone.Core/Download/Clients/Nzbget/Nzbget.cs index 2fb35f2a0..11b84cf4e 100644 --- a/src/NzbDrone.Core/Download/Clients/Nzbget/Nzbget.cs +++ b/src/NzbDrone.Core/Download/Clients/Nzbget/Nzbget.cs @@ -1,6 +1,7 @@ using System; -using System.Collections.Generic; +using System.IO; using System.Linq; +using System.Collections.Generic; using NLog; using NzbDrone.Common; using NzbDrone.Common.Http; @@ -213,9 +214,55 @@ namespace NzbDrone.Core.Download.Clients.Nzbget _proxy.RetryDownload(id, Settings); } - public override void Test() + public override DownloadClientStatus GetStatus() { - _proxy.GetVersion(Settings); + var config = _proxy.GetConfig(Settings); + + var category = GetCategories(config).FirstOrDefault(v => v.Name == Settings.TvCategory); + + var status = new DownloadClientStatus + { + IsLocalhost = Settings.Host == "127.0.0.1" || Settings.Host == "localhost" + }; + + if (category != null) + { + status.OutputRootFolders = new List { category.DestDir }; + } + + return status; + } + + protected IEnumerable GetCategories(Dictionary config) + { + for (int i = 1; i < 100; i++) + { + var name = config.GetValueOrDefault("Category" + i + ".Name"); + + if (name == null) yield break; + + var destDir = config.GetValueOrDefault("Category" + i + ".DestDir"); + + if (destDir.IsNullOrWhiteSpace()) + { + var mainDir = config.GetValueOrDefault("MainDir"); + destDir = config.GetValueOrDefault("DestDir", String.Empty).Replace("${MainDir}", mainDir); + + if (config.GetValueOrDefault("AppendCategoryDir", "yes") == "yes") + { + destDir = Path.Combine(destDir, name); + } + } + + yield return new NzbgetCategory + { + Name = name, + DestDir = destDir, + Unpack = config.GetValueOrDefault("Category" + i + ".Unpack") == "yes", + DefScript = config.GetValueOrDefault("Category" + i + ".DefScript"), + Aliases = config.GetValueOrDefault("Category" + i + ".Aliases"), + }; + } } private String GetVersion(string host = null, int port = 0, string username = null, string password = null) @@ -223,6 +270,11 @@ namespace NzbDrone.Core.Download.Clients.Nzbget return _proxy.GetVersion(Settings); } + public override void Test() + { + _proxy.GetVersion(Settings); + } + public void Execute(TestNzbgetCommand message) { var settings = new NzbgetSettings(); diff --git a/src/NzbDrone.Core/Download/Clients/Nzbget/NzbgetCategory.cs b/src/NzbDrone.Core/Download/Clients/Nzbget/NzbgetCategory.cs new file mode 100644 index 000000000..8a0615ad7 --- /dev/null +++ b/src/NzbDrone.Core/Download/Clients/Nzbget/NzbgetCategory.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace NzbDrone.Core.Download.Clients.Nzbget +{ + public class NzbgetCategory + { + public String Name { get; set; } + public String DestDir { get; set; } + public Boolean Unpack { get; set; } + public String DefScript { get; set; } + public String Aliases { get; set; } + } +} diff --git a/src/NzbDrone.Core/Download/Clients/Nzbget/NzbgetConfigItem.cs b/src/NzbDrone.Core/Download/Clients/Nzbget/NzbgetConfigItem.cs new file mode 100644 index 000000000..e04aad77f --- /dev/null +++ b/src/NzbDrone.Core/Download/Clients/Nzbget/NzbgetConfigItem.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace NzbDrone.Core.Download.Clients.Nzbget +{ + public class NzbgetConfigItem + { + public String Name { get; set; } + public String Value { get; set; } + } +} diff --git a/src/NzbDrone.Core/Download/Clients/Nzbget/NzbgetProxy.cs b/src/NzbDrone.Core/Download/Clients/Nzbget/NzbgetProxy.cs index 5d6e34bb6..714f3eb81 100644 --- a/src/NzbDrone.Core/Download/Clients/Nzbget/NzbgetProxy.cs +++ b/src/NzbDrone.Core/Download/Clients/Nzbget/NzbgetProxy.cs @@ -18,6 +18,7 @@ namespace NzbDrone.Core.Download.Clients.Nzbget List GetPostQueue(NzbgetSettings settings); List GetHistory(NzbgetSettings settings); String GetVersion(NzbgetSettings settings); + Dictionary GetConfig(NzbgetSettings settings); void RemoveFromHistory(string id, NzbgetSettings settings); void RetryDownload(string id, NzbgetSettings settings); } @@ -98,6 +99,14 @@ namespace NzbDrone.Core.Download.Clients.Nzbget return Json.Deserialize>(ProcessRequest(request, settings)).Version; } + public Dictionary GetConfig(NzbgetSettings settings) + { + var request = BuildRequest(new JsonRequest("config")); + + return Json.Deserialize>>(ProcessRequest(request, settings)).Result.ToDictionary(v => v.Name, v => v.Value); + } + + public void RemoveFromHistory(string id, NzbgetSettings settings) { var history = GetHistory(settings); diff --git a/src/NzbDrone.Core/Download/Clients/Pneumatic/Pneumatic.cs b/src/NzbDrone.Core/Download/Clients/Pneumatic/Pneumatic.cs index 2753d9056..489acc1d3 100644 --- a/src/NzbDrone.Core/Download/Clients/Pneumatic/Pneumatic.cs +++ b/src/NzbDrone.Core/Download/Clients/Pneumatic/Pneumatic.cs @@ -90,6 +90,16 @@ namespace NzbDrone.Core.Download.Clients.Pneumatic throw new NotSupportedException(); } + public override DownloadClientStatus GetStatus() + { + var status = new DownloadClientStatus + { + IsLocalhost = true + }; + + return status; + } + public override void Test() { PerformTest(Settings.NzbFolder); diff --git a/src/NzbDrone.Core/Download/Clients/Sabnzbd/Sabnzbd.cs b/src/NzbDrone.Core/Download/Clients/Sabnzbd/Sabnzbd.cs index 86c690dd2..31dec7669 100644 --- a/src/NzbDrone.Core/Download/Clients/Sabnzbd/Sabnzbd.cs +++ b/src/NzbDrone.Core/Download/Clients/Sabnzbd/Sabnzbd.cs @@ -208,6 +208,16 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd _proxy.RetryDownload(id, Settings); } + public override DownloadClientStatus GetStatus() + { + var status = new DownloadClientStatus + { + IsLocalhost = Settings.Host == "127.0.0.1" || Settings.Host == "localhost" + }; + + return status; + } + public override void Test() { _proxy.GetCategories(Settings); diff --git a/src/NzbDrone.Core/Download/Clients/UsenetBlackhole/UsenetBlackhole.cs b/src/NzbDrone.Core/Download/Clients/UsenetBlackhole/UsenetBlackhole.cs index 036be4338..0bddc8b2d 100644 --- a/src/NzbDrone.Core/Download/Clients/UsenetBlackhole/UsenetBlackhole.cs +++ b/src/NzbDrone.Core/Download/Clients/UsenetBlackhole/UsenetBlackhole.cs @@ -142,6 +142,15 @@ namespace NzbDrone.Core.Download.Clients.UsenetBlackhole PerformTest(Settings.WatchFolder); } + public override DownloadClientStatus GetStatus() + { + return new DownloadClientStatus + { + IsLocalhost = true, + OutputRootFolders = new List { Settings.WatchFolder } + }; + } + private void PerformTest(string folder) { var testPath = Path.Combine(folder, "drone_test.txt"); diff --git a/src/NzbDrone.Core/Download/DownloadClientBase.cs b/src/NzbDrone.Core/Download/DownloadClientBase.cs index 453dcc479..0b4da9036 100644 --- a/src/NzbDrone.Core/Download/DownloadClientBase.cs +++ b/src/NzbDrone.Core/Download/DownloadClientBase.cs @@ -67,6 +67,7 @@ namespace NzbDrone.Core.Download public abstract void RemoveItem(string id); public abstract void RetryDownload(string id); public abstract void Test(); + public abstract DownloadClientStatus GetStatus(); protected RemoteEpisode GetRemoteEpisode(String title) { diff --git a/src/NzbDrone.Core/Download/DownloadClientStatus.cs b/src/NzbDrone.Core/Download/DownloadClientStatus.cs new file mode 100644 index 000000000..ef4f71b38 --- /dev/null +++ b/src/NzbDrone.Core/Download/DownloadClientStatus.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace NzbDrone.Core.Download +{ + public class DownloadClientStatus + { + public Boolean IsLocalhost { get; set; } + public List OutputRootFolders { get; set; } + } +} diff --git a/src/NzbDrone.Core/Download/IDownloadClient.cs b/src/NzbDrone.Core/Download/IDownloadClient.cs index aab29cde8..7850ca90b 100644 --- a/src/NzbDrone.Core/Download/IDownloadClient.cs +++ b/src/NzbDrone.Core/Download/IDownloadClient.cs @@ -14,5 +14,7 @@ namespace NzbDrone.Core.Download void RemoveItem(string id); void RetryDownload(string id); void Test(); + + DownloadClientStatus GetStatus(); } } diff --git a/src/NzbDrone.Core/HealthCheck/Checks/ImportMechanismCheck.cs b/src/NzbDrone.Core/HealthCheck/Checks/ImportMechanismCheck.cs index d78b97d93..1234b8730 100644 --- a/src/NzbDrone.Core/HealthCheck/Checks/ImportMechanismCheck.cs +++ b/src/NzbDrone.Core/HealthCheck/Checks/ImportMechanismCheck.cs @@ -5,37 +5,74 @@ using NzbDrone.Common; using NzbDrone.Common.Disk; using NzbDrone.Core.Configuration; using NzbDrone.Core.Download; +using NzbDrone.Core.Download.Clients.Sabnzbd; +using NzbDrone.Core.Download.Clients.Nzbget; namespace NzbDrone.Core.HealthCheck.Checks { public class ImportMechanismCheck : HealthCheckBase { private readonly IConfigService _configService; + private readonly IProvideDownloadClient _provideDownloadClient; private readonly IDownloadTrackingService _downloadTrackingService; - public ImportMechanismCheck(IConfigService configService, IDownloadTrackingService downloadTrackingService) + public ImportMechanismCheck(IConfigService configService, IProvideDownloadClient provideDownloadClient, IDownloadTrackingService downloadTrackingService) { _configService = configService; + _provideDownloadClient = provideDownloadClient; _downloadTrackingService = downloadTrackingService; } public override HealthCheck Check() { + var droneFactoryFolder = _configService.DownloadedEpisodesFolder; + var downloadClients = _provideDownloadClient.GetDownloadClients().Select(v => new { downloadClient = v, status = v.GetStatus() }).ToList(); + + var downloadClientIsLocalHost = downloadClients.All(v => v.status.IsLocalhost); + var downloadClientOutputInDroneFactory = !droneFactoryFolder.IsNullOrWhiteSpace() + && downloadClients.Any(v => v.status.OutputRootFolders != null && v.status.OutputRootFolders.Contains(droneFactoryFolder, PathEqualityComparer.Instance)); + if (!_configService.IsDefined("EnableCompletedDownloadHandling")) { - return new HealthCheck(GetType(), HealthCheckResult.Warning, "Completed Download Handling is disabled"); - } + // Migration helper logic + if (!downloadClientIsLocalHost) + { + return new HealthCheck(GetType(), HealthCheckResult.Warning, "Enable Completed Download Handling if possible (Multi-Computer unsupported)", "Migrating-to-Completed-Download-Handling#Unsupported-download-client-on-different-computer"); + } - var droneFactoryFolder = _configService.DownloadedEpisodesFolder; + if (downloadClients.All(v => v.downloadClient is Sabnzbd)) + { + // With Sabnzbd we cannot check the category settings. + + return new HealthCheck(GetType(), HealthCheckResult.Warning, "Enable Completed Download Handling if possible (Sabnzbd)", "Migrating-to-Completed-Download-Handling#sabnzbd-enable-completed-download-handling"); + } + else if (downloadClients.All(v => v.downloadClient is Nzbget)) + { + // With Nzbget we can check if the category should be changed. + if (downloadClientOutputInDroneFactory) + { + return new HealthCheck(GetType(), HealthCheckResult.Warning, "Enable Completed Download Handling if possible (Nzbget - Conflicting Category)", "Migrating-to-Completed-Download-Handling#nzbget-conflicting-download-client-category"); + } + + return new HealthCheck(GetType(), HealthCheckResult.Warning, "Enable Completed Download Handling if possible (Nzbget)", "Migrating-to-Completed-Download-Handling#nzbget-enable-completed-download-handling"); + } + else + { + return new HealthCheck(GetType(), HealthCheckResult.Warning, "Enable Completed Download Handling if possible", "Migrating-to-Completed-Download-Handling"); + } + } if (!_configService.EnableCompletedDownloadHandling && droneFactoryFolder.IsNullOrWhiteSpace()) { return new HealthCheck(GetType(), HealthCheckResult.Warning, "Enable Completed Download Handling or configure Drone factory"); } - if (_configService.EnableCompletedDownloadHandling && !droneFactoryFolder.IsNullOrWhiteSpace() && _downloadTrackingService.GetCompletedDownloads().Any(v => droneFactoryFolder.PathEquals(v.DownloadItem.OutputPath) || droneFactoryFolder.IsParentPath(v.DownloadItem.OutputPath))) + if (_configService.EnableCompletedDownloadHandling && !droneFactoryFolder.IsNullOrWhiteSpace()) { - return new HealthCheck(GetType(), HealthCheckResult.Warning, "Download Client has history items in Drone Factory conflicting with Completed Download Handling"); + if (_downloadTrackingService.GetCompletedDownloads().Any(v => droneFactoryFolder.PathEquals(v.DownloadItem.OutputPath) || droneFactoryFolder.IsParentPath(v.DownloadItem.OutputPath))) + { + return new HealthCheck(GetType(), HealthCheckResult.Warning, "Completed Download Handling conflict with Drone Factory (Conflicting History Item)", "Migrating-to-Completed-Download-Handling#conflicting-download-client-category"); + } } return new HealthCheck(GetType()); diff --git a/src/NzbDrone.Core/NzbDrone.Core.csproj b/src/NzbDrone.Core/NzbDrone.Core.csproj index 7dcfdebe4..63c970219 100644 --- a/src/NzbDrone.Core/NzbDrone.Core.csproj +++ b/src/NzbDrone.Core/NzbDrone.Core.csproj @@ -240,6 +240,8 @@ + + @@ -271,6 +273,7 @@ +