New: Translations support for Health Checks

(cherry picked from commit bfc036178487fe0b692f306a53f2a334cdf7f9d5)
pull/3612/head
Qstick 5 years ago committed by Bogdan
parent 8368a8021d
commit 625787ddb4

@ -1,6 +1,8 @@
using NUnit.Framework; using Moq;
using NUnit.Framework;
using NzbDrone.Common.EnvironmentInfo; using NzbDrone.Common.EnvironmentInfo;
using NzbDrone.Core.HealthCheck.Checks; using NzbDrone.Core.HealthCheck.Checks;
using NzbDrone.Core.Localization;
using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Test.Framework;
using NzbDrone.Test.Common; using NzbDrone.Test.Common;
@ -9,6 +11,14 @@ namespace NzbDrone.Core.Test.HealthCheck.Checks
[TestFixture] [TestFixture]
public class AppDataLocationFixture : CoreTest<AppDataLocationCheck> public class AppDataLocationFixture : CoreTest<AppDataLocationCheck>
{ {
[SetUp]
public void Setup()
{
Mocker.GetMock<ILocalizationService>()
.Setup(s => s.GetLocalizedString(It.IsAny<string>()))
.Returns("Some Warning Message");
}
[Test] [Test]
public void should_return_warning_when_app_data_is_child_of_startup_folder() public void should_return_warning_when_app_data_is_child_of_startup_folder()
{ {

@ -4,6 +4,7 @@ using Moq;
using NUnit.Framework; using NUnit.Framework;
using NzbDrone.Core.Download; using NzbDrone.Core.Download;
using NzbDrone.Core.HealthCheck.Checks; using NzbDrone.Core.HealthCheck.Checks;
using NzbDrone.Core.Localization;
using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test.HealthCheck.Checks namespace NzbDrone.Core.Test.HealthCheck.Checks
@ -11,6 +12,14 @@ namespace NzbDrone.Core.Test.HealthCheck.Checks
[TestFixture] [TestFixture]
public class DownloadClientCheckFixture : CoreTest<DownloadClientCheck> public class DownloadClientCheckFixture : CoreTest<DownloadClientCheck>
{ {
[SetUp]
public void Setup()
{
Mocker.GetMock<ILocalizationService>()
.Setup(s => s.GetLocalizedString(It.IsAny<string>()))
.Returns("Some Warning Message");
}
[Test] [Test]
public void should_return_warning_when_download_client_has_not_been_configured() public void should_return_warning_when_download_client_has_not_been_configured()
{ {

@ -6,6 +6,7 @@ using NzbDrone.Common.Disk;
using NzbDrone.Core.Download; using NzbDrone.Core.Download;
using NzbDrone.Core.Download.Clients; using NzbDrone.Core.Download.Clients;
using NzbDrone.Core.HealthCheck.Checks; using NzbDrone.Core.HealthCheck.Checks;
using NzbDrone.Core.Localization;
using NzbDrone.Core.RootFolders; using NzbDrone.Core.RootFolders;
using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Test.Framework;
using NzbDrone.Test.Common; using NzbDrone.Test.Common;
@ -30,6 +31,10 @@ namespace NzbDrone.Core.Test.HealthCheck.Checks
[SetUp] [SetUp]
public void Setup() public void Setup()
{ {
Mocker.GetMock<ILocalizationService>()
.Setup(s => s.GetLocalizedString(It.IsAny<string>()))
.Returns("Some Warning Message");
_clientStatus = new DownloadClientInfo _clientStatus = new DownloadClientInfo
{ {
IsLocalhost = true, IsLocalhost = true,
@ -68,7 +73,7 @@ namespace NzbDrone.Core.Test.HealthCheck.Checks
{ {
GivenRootFolder(_downloadRootPath); GivenRootFolder(_downloadRootPath);
Subject.Check().ShouldBeWarning(wikiFragment: "downloads_in_root_folder"); Subject.Check().ShouldBeWarning(wikiFragment: "downloads-in-root-folder");
} }
[Test] [Test]

@ -1,4 +1,4 @@
using FluentAssertions; using FluentAssertions;
using NzbDrone.Common.Extensions; using NzbDrone.Common.Extensions;
using NzbDrone.Core.HealthCheck; using NzbDrone.Core.HealthCheck;
@ -32,7 +32,7 @@ namespace NzbDrone.Core.Test.HealthCheck.Checks
if (wikiFragment.IsNotNullOrWhiteSpace()) if (wikiFragment.IsNotNullOrWhiteSpace())
{ {
result.WikiUrl.Fragment.Should().Be(wikiFragment); result.WikiUrl.ToString().Should().Contain(wikiFragment);
} }
} }
@ -47,7 +47,7 @@ namespace NzbDrone.Core.Test.HealthCheck.Checks
if (wikiFragment.IsNotNullOrWhiteSpace()) if (wikiFragment.IsNotNullOrWhiteSpace())
{ {
result.WikiUrl.Fragment.Should().Be(wikiFragment); result.WikiUrl.ToString().Should().Contain(wikiFragment);
} }
} }
} }

@ -4,6 +4,7 @@ using Moq;
using NUnit.Framework; using NUnit.Framework;
using NzbDrone.Core.HealthCheck.Checks; using NzbDrone.Core.HealthCheck.Checks;
using NzbDrone.Core.ImportLists; using NzbDrone.Core.ImportLists;
using NzbDrone.Core.Localization;
using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test.HealthCheck.Checks namespace NzbDrone.Core.Test.HealthCheck.Checks
@ -24,6 +25,10 @@ namespace NzbDrone.Core.Test.HealthCheck.Checks
Mocker.GetMock<IImportListStatusService>() Mocker.GetMock<IImportListStatusService>()
.Setup(v => v.GetBlockedProviders()) .Setup(v => v.GetBlockedProviders())
.Returns(_blockedImportLists); .Returns(_blockedImportLists);
Mocker.GetMock<ILocalizationService>()
.Setup(s => s.GetLocalizedString(It.IsAny<string>()))
.Returns("Some Warning Message");
} }
private Mock<IImportList> GivenImportList(int i, double backoffHours, double failureHours) private Mock<IImportList> GivenImportList(int i, double backoffHours, double failureHours)

@ -1,6 +1,8 @@
using Moq;
using NUnit.Framework; using NUnit.Framework;
using NzbDrone.Core.Configuration; using NzbDrone.Core.Configuration;
using NzbDrone.Core.HealthCheck.Checks; using NzbDrone.Core.HealthCheck.Checks;
using NzbDrone.Core.Localization;
using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test.HealthCheck.Checks namespace NzbDrone.Core.Test.HealthCheck.Checks
@ -8,6 +10,14 @@ namespace NzbDrone.Core.Test.HealthCheck.Checks
[TestFixture] [TestFixture]
public class ImportMechanismCheckFixture : CoreTest<ImportMechanismCheck> public class ImportMechanismCheckFixture : CoreTest<ImportMechanismCheck>
{ {
[SetUp]
public void Setup()
{
Mocker.GetMock<ILocalizationService>()
.Setup(s => s.GetLocalizedString(It.IsAny<string>()))
.Returns("Some Warning Message");
}
private void GivenCompletedDownloadHandling(bool? enabled = null) private void GivenCompletedDownloadHandling(bool? enabled = null)
{ {
if (enabled.HasValue) if (enabled.HasValue)
@ -19,7 +29,7 @@ namespace NzbDrone.Core.Test.HealthCheck.Checks
} }
[Test] [Test]
public void should_return_warning_when_completeddownloadhandling_false() public void should_return_warning_when_completed_download_handling_not_configured()
{ {
GivenCompletedDownloadHandling(false); GivenCompletedDownloadHandling(false);

@ -4,6 +4,7 @@ using NUnit.Framework;
using NzbDrone.Core.HealthCheck.Checks; using NzbDrone.Core.HealthCheck.Checks;
using NzbDrone.Core.Indexers; using NzbDrone.Core.Indexers;
using NzbDrone.Core.Indexers.Torznab; using NzbDrone.Core.Indexers.Torznab;
using NzbDrone.Core.Localization;
using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test.HealthCheck.Checks namespace NzbDrone.Core.Test.HealthCheck.Checks
@ -20,6 +21,10 @@ namespace NzbDrone.Core.Test.HealthCheck.Checks
Mocker.GetMock<IIndexerFactory>() Mocker.GetMock<IIndexerFactory>()
.Setup(v => v.All()) .Setup(v => v.All())
.Returns(_indexers); .Returns(_indexers);
Mocker.GetMock<ILocalizationService>()
.Setup(s => s.GetLocalizedString(It.IsAny<string>()))
.Returns("Some Warning Message");
} }
private void GivenIndexer(string baseUrl, string apiPath) private void GivenIndexer(string baseUrl, string apiPath)

@ -4,6 +4,7 @@ using Moq;
using NUnit.Framework; using NUnit.Framework;
using NzbDrone.Core.HealthCheck.Checks; using NzbDrone.Core.HealthCheck.Checks;
using NzbDrone.Core.Indexers; using NzbDrone.Core.Indexers;
using NzbDrone.Core.Localization;
using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test.HealthCheck.Checks namespace NzbDrone.Core.Test.HealthCheck.Checks
@ -24,6 +25,10 @@ namespace NzbDrone.Core.Test.HealthCheck.Checks
Mocker.GetMock<IIndexerStatusService>() Mocker.GetMock<IIndexerStatusService>()
.Setup(v => v.GetBlockedProviders()) .Setup(v => v.GetBlockedProviders())
.Returns(_blockedIndexers); .Returns(_blockedIndexers);
Mocker.GetMock<ILocalizationService>()
.Setup(v => v.GetLocalizedString(It.IsAny<string>()))
.Returns("Error");
} }
private Mock<IIndexer> GivenIndexer(int id, double backoffHours, double failureHours) private Mock<IIndexer> GivenIndexer(int id, double backoffHours, double failureHours)

@ -1,8 +1,9 @@
using System.Collections.Generic; using System.Collections.Generic;
using Moq; using Moq;
using NUnit.Framework; using NUnit.Framework;
using NzbDrone.Core.HealthCheck.Checks; using NzbDrone.Core.HealthCheck.Checks;
using NzbDrone.Core.Indexers; using NzbDrone.Core.Indexers;
using NzbDrone.Core.Localization;
using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test.HealthCheck.Checks namespace NzbDrone.Core.Test.HealthCheck.Checks
@ -22,6 +23,10 @@ namespace NzbDrone.Core.Test.HealthCheck.Checks
Mocker.GetMock<IIndexerFactory>() Mocker.GetMock<IIndexerFactory>()
.Setup(s => s.RssEnabled(It.IsAny<bool>())) .Setup(s => s.RssEnabled(It.IsAny<bool>()))
.Returns(new List<IIndexer>()); .Returns(new List<IIndexer>());
Mocker.GetMock<ILocalizationService>()
.Setup(s => s.GetLocalizedString(It.IsAny<string>()))
.Returns("Some Warning Message");
} }
private void GivenIndexer(bool supportsRss, bool supportsSearch) private void GivenIndexer(bool supportsRss, bool supportsSearch)
@ -47,6 +52,10 @@ namespace NzbDrone.Core.Test.HealthCheck.Checks
Mocker.GetMock<IIndexerFactory>() Mocker.GetMock<IIndexerFactory>()
.Setup(s => s.RssEnabled(false)) .Setup(s => s.RssEnabled(false))
.Returns(new List<IIndexer> { _indexerMock.Object }); .Returns(new List<IIndexer> { _indexerMock.Object });
Mocker.GetMock<ILocalizationService>()
.Setup(s => s.GetLocalizedString(It.IsAny<string>()))
.Returns("recent indexer errors");
} }
[Test] [Test]

@ -3,6 +3,7 @@ using Moq;
using NUnit.Framework; using NUnit.Framework;
using NzbDrone.Core.HealthCheck.Checks; using NzbDrone.Core.HealthCheck.Checks;
using NzbDrone.Core.Indexers; using NzbDrone.Core.Indexers;
using NzbDrone.Core.Localization;
using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test.HealthCheck.Checks namespace NzbDrone.Core.Test.HealthCheck.Checks
@ -26,6 +27,10 @@ namespace NzbDrone.Core.Test.HealthCheck.Checks
Mocker.GetMock<IIndexerFactory>() Mocker.GetMock<IIndexerFactory>()
.Setup(s => s.InteractiveSearchEnabled(It.IsAny<bool>())) .Setup(s => s.InteractiveSearchEnabled(It.IsAny<bool>()))
.Returns(new List<IIndexer>()); .Returns(new List<IIndexer>());
Mocker.GetMock<ILocalizationService>()
.Setup(s => s.GetLocalizedString(It.IsAny<string>()))
.Returns("Some Warning Message");
} }
private void GivenIndexer(bool supportsRss, bool supportsSearch) private void GivenIndexer(bool supportsRss, bool supportsSearch)
@ -62,6 +67,10 @@ namespace NzbDrone.Core.Test.HealthCheck.Checks
Mocker.GetMock<IIndexerFactory>() Mocker.GetMock<IIndexerFactory>()
.Setup(s => s.InteractiveSearchEnabled(false)) .Setup(s => s.InteractiveSearchEnabled(false))
.Returns(new List<IIndexer> { _indexerMock.Object }); .Returns(new List<IIndexer> { _indexerMock.Object });
Mocker.GetMock<ILocalizationService>()
.Setup(s => s.GetLocalizedString(It.IsAny<string>()))
.Returns("recent indexer errors");
} }
[Test] [Test]

@ -4,6 +4,7 @@ using Moq;
using NUnit.Framework; using NUnit.Framework;
using NzbDrone.Core.HealthCheck.Checks; using NzbDrone.Core.HealthCheck.Checks;
using NzbDrone.Core.Indexers; using NzbDrone.Core.Indexers;
using NzbDrone.Core.Localization;
using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test.HealthCheck.Checks namespace NzbDrone.Core.Test.HealthCheck.Checks
@ -24,6 +25,10 @@ namespace NzbDrone.Core.Test.HealthCheck.Checks
Mocker.GetMock<IIndexerStatusService>() Mocker.GetMock<IIndexerStatusService>()
.Setup(v => v.GetBlockedProviders()) .Setup(v => v.GetBlockedProviders())
.Returns(_blockedIndexers); .Returns(_blockedIndexers);
Mocker.GetMock<ILocalizationService>()
.Setup(s => s.GetLocalizedString(It.IsAny<string>()))
.Returns("Some Warning Message");
} }
private Mock<IIndexer> GivenIndexer(int i, double backoffHours, double failureHours) private Mock<IIndexer> GivenIndexer(int i, double backoffHours, double failureHours)

@ -10,7 +10,7 @@ using NzbDrone.Core.Configuration;
using NzbDrone.Core.Download; using NzbDrone.Core.Download;
using NzbDrone.Core.Download.Clients; using NzbDrone.Core.Download.Clients;
using NzbDrone.Core.HealthCheck.Checks; using NzbDrone.Core.HealthCheck.Checks;
using NzbDrone.Core.Indexers; using NzbDrone.Core.Localization;
using NzbDrone.Core.MediaFiles; using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.MediaFiles.Events; using NzbDrone.Core.MediaFiles.Events;
using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Parser.Model;
@ -41,12 +41,7 @@ namespace NzbDrone.Core.Test.HealthCheck.Checks
{ {
_downloadItem = new DownloadClientItem _downloadItem = new DownloadClientItem
{ {
DownloadClientInfo = new DownloadClientItemClientInfo DownloadClientInfo = new DownloadClientItemClientInfo { Name = "Test" },
{
Protocol = DownloadProtocol.Usenet,
Id = 1,
Name = "Test"
},
DownloadId = "TestId", DownloadId = "TestId",
OutputPath = new OsPath(_downloadItemPath) OutputPath = new OsPath(_downloadItemPath)
}; };
@ -71,10 +66,6 @@ namespace NzbDrone.Core.Test.HealthCheck.Checks
.Setup(s => s.GetDownloadClients(It.IsAny<bool>())) .Setup(s => s.GetDownloadClients(It.IsAny<bool>()))
.Returns(new IDownloadClient[] { _downloadClient.Object }); .Returns(new IDownloadClient[] { _downloadClient.Object });
Mocker.GetMock<IProvideDownloadClient>()
.Setup(s => s.Get(It.IsAny<int>()))
.Returns(_downloadClient.Object);
Mocker.GetMock<IConfigService>() Mocker.GetMock<IConfigService>()
.Setup(s => s.EnableCompletedDownloadHandling) .Setup(s => s.EnableCompletedDownloadHandling)
.Returns(true); .Returns(true);
@ -94,6 +85,10 @@ namespace NzbDrone.Core.Test.HealthCheck.Checks
Ensure.That(path, () => path).IsValidPath(PathValidationType.CurrentOs); Ensure.That(path, () => path).IsValidPath(PathValidationType.CurrentOs);
return false; return false;
}); });
Mocker.GetMock<ILocalizationService>()
.Setup(s => s.GetLocalizedString(It.IsAny<string>()))
.Returns("Some Warning Message");
} }
private void GivenFolderExists(string folder) private void GivenFolderExists(string folder)
@ -181,7 +176,7 @@ namespace NzbDrone.Core.Test.HealthCheck.Checks
public void should_return_ok_on_track_imported_event() public void should_return_ok_on_track_imported_event()
{ {
GivenFolderExists(_downloadRootPath); GivenFolderExists(_downloadRootPath);
var importEvent = new TrackImportedEvent(new LocalTrack(), new TrackFile(), new List<TrackFile>(), true, new DownloadClientItem { DownloadClientInfo = new DownloadClientItemClientInfo() }); var importEvent = new TrackImportedEvent(new LocalTrack(), new TrackFile(), new List<TrackFile>(), true, new DownloadClientItem());
Subject.Check(importEvent).ShouldBeOk(); Subject.Check(importEvent).ShouldBeOk();
} }
@ -195,7 +190,7 @@ namespace NzbDrone.Core.Test.HealthCheck.Checks
}; };
GivenFileExists(localTrack.Path); GivenFileExists(localTrack.Path);
var importEvent = new TrackImportFailedEvent(new Exception(), localTrack, true, new DownloadClientItem { DownloadClientInfo = new DownloadClientItemClientInfo() }); var importEvent = new TrackImportFailedEvent(new Exception(), localTrack, true, new DownloadClientItem());
Subject.Check(importEvent).ShouldBeError(wikiFragment: "permissions-error"); Subject.Check(importEvent).ShouldBeError(wikiFragment: "permissions-error");
} }

@ -6,6 +6,7 @@ using NUnit.Framework;
using NzbDrone.Common.Disk; using NzbDrone.Common.Disk;
using NzbDrone.Core.HealthCheck.Checks; using NzbDrone.Core.HealthCheck.Checks;
using NzbDrone.Core.ImportLists; using NzbDrone.Core.ImportLists;
using NzbDrone.Core.Localization;
using NzbDrone.Core.Music; using NzbDrone.Core.Music;
using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Test.Framework;
@ -14,6 +15,14 @@ namespace NzbDrone.Core.Test.HealthCheck.Checks
[TestFixture] [TestFixture]
public class RootFolderCheckFixture : CoreTest<RootFolderCheck> public class RootFolderCheckFixture : CoreTest<RootFolderCheck>
{ {
[SetUp]
public void Setup()
{
Mocker.GetMock<ILocalizationService>()
.Setup(s => s.GetLocalizedString(It.IsAny<string>()))
.Returns("Some Warning Message");
}
private void GivenMissingRootFolder() private void GivenMissingRootFolder()
{ {
var artist = Builder<Artist>.CreateListOfSize(1) var artist = Builder<Artist>.CreateListOfSize(1)

@ -6,6 +6,7 @@ using NzbDrone.Common.Cloud;
using NzbDrone.Common.Http; using NzbDrone.Common.Http;
using NzbDrone.Common.Serializer; using NzbDrone.Common.Serializer;
using NzbDrone.Core.HealthCheck.Checks; using NzbDrone.Core.HealthCheck.Checks;
using NzbDrone.Core.Localization;
using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Test.Framework;
using NzbDrone.Test.Common; using NzbDrone.Test.Common;
@ -24,6 +25,10 @@ namespace NzbDrone.Core.Test.HealthCheck.Checks
{ {
var json = new ServiceTimeResponse { DateTimeUtc = dateTime }.ToJson(); var json = new ServiceTimeResponse { DateTimeUtc = dateTime }.ToJson();
Mocker.GetMock<ILocalizationService>()
.Setup(s => s.GetLocalizedString(It.IsAny<string>()))
.Returns("System time is off by more than 1 day. Scheduled tasks may not run correctly until the time is corrected");
Mocker.GetMock<IHttpClient>() Mocker.GetMock<IHttpClient>()
.Setup(s => s.Execute(It.IsAny<HttpRequest>())) .Setup(s => s.Execute(It.IsAny<HttpRequest>()))
.Returns<HttpRequest>(r => new HttpResponse(r, new HttpHeader(), Encoding.ASCII.GetBytes(json))); .Returns<HttpRequest>(r => new HttpResponse(r, new HttpHeader(), Encoding.ASCII.GetBytes(json)));

@ -1,9 +1,10 @@
using Moq; using Moq;
using NUnit.Framework; using NUnit.Framework;
using NzbDrone.Common.Disk; using NzbDrone.Common.Disk;
using NzbDrone.Common.EnvironmentInfo; using NzbDrone.Common.EnvironmentInfo;
using NzbDrone.Core.Configuration; using NzbDrone.Core.Configuration;
using NzbDrone.Core.HealthCheck.Checks; using NzbDrone.Core.HealthCheck.Checks;
using NzbDrone.Core.Localization;
using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Test.Framework;
using NzbDrone.Core.Update; using NzbDrone.Core.Update;
@ -12,6 +13,14 @@ namespace NzbDrone.Core.Test.HealthCheck.Checks
[TestFixture] [TestFixture]
public class UpdateCheckFixture : CoreTest<UpdateCheck> public class UpdateCheckFixture : CoreTest<UpdateCheck>
{ {
[SetUp]
public void Setup()
{
Mocker.GetMock<ILocalizationService>()
.Setup(s => s.GetLocalizedString(It.IsAny<string>()))
.Returns("Some Warning Message");
}
[Test] [Test]
public void should_return_error_when_app_folder_is_write_protected() public void should_return_error_when_app_folder_is_write_protected()
{ {

@ -1,5 +1,6 @@
using NzbDrone.Common.EnvironmentInfo; using NzbDrone.Common.EnvironmentInfo;
using NzbDrone.Common.Extensions; using NzbDrone.Common.Extensions;
using NzbDrone.Core.Localization;
namespace NzbDrone.Core.HealthCheck.Checks namespace NzbDrone.Core.HealthCheck.Checks
{ {
@ -7,7 +8,8 @@ namespace NzbDrone.Core.HealthCheck.Checks
{ {
private readonly IAppFolderInfo _appFolderInfo; private readonly IAppFolderInfo _appFolderInfo;
public AppDataLocationCheck(IAppFolderInfo appFolderInfo) public AppDataLocationCheck(IAppFolderInfo appFolderInfo, ILocalizationService localizationService)
: base(localizationService)
{ {
_appFolderInfo = appFolderInfo; _appFolderInfo = appFolderInfo;
} }
@ -17,7 +19,7 @@ namespace NzbDrone.Core.HealthCheck.Checks
if (_appFolderInfo.StartUpFolder.IsParentPath(_appFolderInfo.AppDataFolder) || if (_appFolderInfo.StartUpFolder.IsParentPath(_appFolderInfo.AppDataFolder) ||
_appFolderInfo.StartUpFolder.PathEquals(_appFolderInfo.AppDataFolder)) _appFolderInfo.StartUpFolder.PathEquals(_appFolderInfo.AppDataFolder))
{ {
return new HealthCheck(GetType(), HealthCheckResult.Warning, "Updating will not be possible to prevent deleting AppData on Update"); return new HealthCheck(GetType(), HealthCheckResult.Warning, _localizationService.GetLocalizedString("AppDataLocationHealthCheckMessage"), "#updating-will-not-be-possible-to-prevent-deleting-appdata-on-update");
} }
return new HealthCheck(GetType()); return new HealthCheck(GetType());

@ -2,6 +2,7 @@ using System;
using System.Linq; using System.Linq;
using NLog; using NLog;
using NzbDrone.Core.Download; using NzbDrone.Core.Download;
using NzbDrone.Core.Localization;
using NzbDrone.Core.ThingiProvider.Events; using NzbDrone.Core.ThingiProvider.Events;
namespace NzbDrone.Core.HealthCheck.Checks namespace NzbDrone.Core.HealthCheck.Checks
@ -15,7 +16,8 @@ namespace NzbDrone.Core.HealthCheck.Checks
private readonly IProvideDownloadClient _downloadClientProvider; private readonly IProvideDownloadClient _downloadClientProvider;
private readonly Logger _logger; private readonly Logger _logger;
public DownloadClientCheck(IProvideDownloadClient downloadClientProvider, Logger logger) public DownloadClientCheck(IProvideDownloadClient downloadClientProvider, ILocalizationService localizationService, Logger logger)
: base(localizationService)
{ {
_downloadClientProvider = downloadClientProvider; _downloadClientProvider = downloadClientProvider;
_logger = logger; _logger = logger;
@ -27,7 +29,7 @@ namespace NzbDrone.Core.HealthCheck.Checks
if (!downloadClients.Any()) if (!downloadClients.Any())
{ {
return new HealthCheck(GetType(), HealthCheckResult.Warning, "No download client is available"); return new HealthCheck(GetType(), HealthCheckResult.Warning, _localizationService.GetLocalizedString("DownloadClientCheckNoneAvailableMessage"), "#no-download-client-is-available");
} }
foreach (var downloadClient in downloadClients) foreach (var downloadClient in downloadClients)
@ -40,7 +42,7 @@ namespace NzbDrone.Core.HealthCheck.Checks
{ {
_logger.Debug(ex, "Unable to communicate with {0}", downloadClient.Definition.Name); _logger.Debug(ex, "Unable to communicate with {0}", downloadClient.Definition.Name);
var message = $"Unable to communicate with {downloadClient.Definition.Name}."; var message = string.Format(_localizationService.GetLocalizedString("DownloadClientCheckUnableToCommunicateMessage"), downloadClient.Definition.Name);
return new HealthCheck(GetType(), HealthCheckResult.Error, $"{message} {ex.Message}", "#unable-to-communicate-with-download-client"); return new HealthCheck(GetType(), HealthCheckResult.Error, $"{message} {ex.Message}", "#unable-to-communicate-with-download-client");
} }
} }

@ -6,6 +6,7 @@ using NzbDrone.Common.Extensions;
using NzbDrone.Core.Datastore.Events; using NzbDrone.Core.Datastore.Events;
using NzbDrone.Core.Download; using NzbDrone.Core.Download;
using NzbDrone.Core.Download.Clients; using NzbDrone.Core.Download.Clients;
using NzbDrone.Core.Localization;
using NzbDrone.Core.RemotePathMappings; using NzbDrone.Core.RemotePathMappings;
using NzbDrone.Core.RootFolders; using NzbDrone.Core.RootFolders;
using NzbDrone.Core.ThingiProvider.Events; using NzbDrone.Core.ThingiProvider.Events;
@ -26,7 +27,9 @@ namespace NzbDrone.Core.HealthCheck.Checks
public DownloadClientRootFolderCheck(IProvideDownloadClient downloadClientProvider, public DownloadClientRootFolderCheck(IProvideDownloadClient downloadClientProvider,
IRootFolderService rootFolderService, IRootFolderService rootFolderService,
Logger logger) Logger logger,
ILocalizationService localizationService)
: base(localizationService)
{ {
_downloadClientProvider = downloadClientProvider; _downloadClientProvider = downloadClientProvider;
_rootFolderService = rootFolderService; _rootFolderService = rootFolderService;
@ -50,7 +53,7 @@ namespace NzbDrone.Core.HealthCheck.Checks
{ {
if (rootFolders.Any(r => r.Path.PathEquals(folder.FullPath))) if (rootFolders.Any(r => r.Path.PathEquals(folder.FullPath)))
{ {
return new HealthCheck(GetType(), HealthCheckResult.Warning, string.Format("Download client {0} places downloads in the root folder {1}. You should not download to a root folder.", client.Definition.Name, folder.FullPath), "#downloads_in_root_folder"); return new HealthCheck(GetType(), HealthCheckResult.Warning, string.Format(_localizationService.GetLocalizedString("DownloadClientCheckDownloadingToRoot"), client.Definition.Name, folder.FullPath), "#downloads-in-root-folder");
} }
} }
} }

@ -1,6 +1,7 @@
using System.Linq; using System.Linq;
using NzbDrone.Common.Extensions; using NzbDrone.Common.Extensions;
using NzbDrone.Core.Download; using NzbDrone.Core.Download;
using NzbDrone.Core.Localization;
using NzbDrone.Core.ThingiProvider.Events; using NzbDrone.Core.ThingiProvider.Events;
namespace NzbDrone.Core.HealthCheck.Checks namespace NzbDrone.Core.HealthCheck.Checks
@ -13,7 +14,8 @@ namespace NzbDrone.Core.HealthCheck.Checks
private readonly IDownloadClientFactory _providerFactory; private readonly IDownloadClientFactory _providerFactory;
private readonly IDownloadClientStatusService _providerStatusService; private readonly IDownloadClientStatusService _providerStatusService;
public DownloadClientStatusCheck(IDownloadClientFactory providerFactory, IDownloadClientStatusService providerStatusService) public DownloadClientStatusCheck(IDownloadClientFactory providerFactory, IDownloadClientStatusService providerStatusService, ILocalizationService localizationService)
: base(localizationService)
{ {
_providerFactory = providerFactory; _providerFactory = providerFactory;
_providerStatusService = providerStatusService; _providerStatusService = providerStatusService;
@ -35,10 +37,10 @@ namespace NzbDrone.Core.HealthCheck.Checks
if (backOffProviders.Count == enabledProviders.Count) if (backOffProviders.Count == enabledProviders.Count)
{ {
return new HealthCheck(GetType(), HealthCheckResult.Error, "All download clients are unavailable due to failures", "#download-clients-are-unavailable-due-to-failures"); return new HealthCheck(GetType(), HealthCheckResult.Error, _localizationService.GetLocalizedString("DownloadClientStatusCheckAllClientMessage"), "#download-clients-are-unavailable-due-to-failures");
} }
return new HealthCheck(GetType(), HealthCheckResult.Warning, string.Format("Download clients unavailable due to failures: {0}", string.Join(", ", backOffProviders.Select(v => v.Provider.Definition.Name))), "#download-clients-are-unavailable-due-to-failures"); return new HealthCheck(GetType(), HealthCheckResult.Warning, string.Format(_localizationService.GetLocalizedString("DownloadClientStatusCheckSingleClientMessage"), string.Join(", ", backOffProviders.Select(v => v.Provider.Definition.Name))), "#download-clients-are-unavailable-due-to-failures");
} }
} }
} }

@ -1,6 +1,7 @@
using System; using System;
using NzbDrone.Core.Configuration; using NzbDrone.Core.Configuration;
using NzbDrone.Core.Configuration.Events; using NzbDrone.Core.Configuration.Events;
using NzbDrone.Core.Localization;
using NzbDrone.Core.Parser; using NzbDrone.Core.Parser;
namespace NzbDrone.Core.HealthCheck.Checks namespace NzbDrone.Core.HealthCheck.Checks
@ -12,7 +13,9 @@ namespace NzbDrone.Core.HealthCheck.Checks
private readonly IConfigService _configService; private readonly IConfigService _configService;
public FpcalcCheck(IFingerprintingService fingerprintingService, public FpcalcCheck(IFingerprintingService fingerprintingService,
IConfigService configService) IConfigService configService,
ILocalizationService localizationService)
: base(localizationService)
{ {
_fingerprintingService = fingerprintingService; _fingerprintingService = fingerprintingService;
_configService = configService; _configService = configService;
@ -28,13 +31,13 @@ namespace NzbDrone.Core.HealthCheck.Checks
if (!_fingerprintingService.IsSetup()) if (!_fingerprintingService.IsSetup())
{ {
return new HealthCheck(GetType(), HealthCheckResult.Warning, $"fpcalc could not be found. Audio fingerprinting disabled.", "#fpcalc-missing"); return new HealthCheck(GetType(), HealthCheckResult.Warning, $"fpcalc could not be found. Audio fingerprinting disabled.", "#fpcalc-missing");
} }
var fpcalcVersion = _fingerprintingService.FpcalcVersion(); var fpcalcVersion = _fingerprintingService.FpcalcVersion();
if (fpcalcVersion == null || fpcalcVersion < new Version("1.4.3")) if (fpcalcVersion == null || fpcalcVersion < new Version("1.4.3"))
{ {
return new HealthCheck(GetType(), HealthCheckResult.Warning, $"You have an old version of fpcalc. Please upgrade to 1.4.3.", "#fpcalc-upgrade"); return new HealthCheck(GetType(), HealthCheckResult.Warning, $"You have an old version of fpcalc. Please upgrade to 1.4.3.", "#fpcalc-upgrade");
} }
return new HealthCheck(GetType()); return new HealthCheck(GetType());

@ -1,6 +1,7 @@
using System.Linq; using System.Linq;
using NzbDrone.Common.Extensions; using NzbDrone.Common.Extensions;
using NzbDrone.Core.ImportLists; using NzbDrone.Core.ImportLists;
using NzbDrone.Core.Localization;
using NzbDrone.Core.ThingiProvider.Events; using NzbDrone.Core.ThingiProvider.Events;
namespace NzbDrone.Core.HealthCheck.Checks namespace NzbDrone.Core.HealthCheck.Checks
@ -13,7 +14,8 @@ namespace NzbDrone.Core.HealthCheck.Checks
private readonly IImportListFactory _providerFactory; private readonly IImportListFactory _providerFactory;
private readonly IImportListStatusService _providerStatusService; private readonly IImportListStatusService _providerStatusService;
public ImportListStatusCheck(IImportListFactory providerFactory, IImportListStatusService providerStatusService) public ImportListStatusCheck(IImportListFactory providerFactory, IImportListStatusService providerStatusService, ILocalizationService localizationService)
: base(localizationService)
{ {
_providerFactory = providerFactory; _providerFactory = providerFactory;
_providerStatusService = providerStatusService; _providerStatusService = providerStatusService;
@ -35,10 +37,10 @@ namespace NzbDrone.Core.HealthCheck.Checks
if (backOffProviders.Count == enabledProviders.Count) if (backOffProviders.Count == enabledProviders.Count)
{ {
return new HealthCheck(GetType(), HealthCheckResult.Error, "All import lists are unavailable due to failures", "#import-lists-are-unavailable-due-to-failures"); return new HealthCheck(GetType(), HealthCheckResult.Error, _localizationService.GetLocalizedString("ImportListStatusCheckAllClientMessage"), "#lists-are-unavailable-due-to-failures");
} }
return new HealthCheck(GetType(), HealthCheckResult.Warning, string.Format("Import lists unavailable due to failures: {0}", string.Join(", ", backOffProviders.Select(v => v.ImportList.Definition.Name))), "#import-lsits-are-unavailable-due-to-failures"); return new HealthCheck(GetType(), HealthCheckResult.Warning, string.Format(_localizationService.GetLocalizedString("ImportListStatusCheckSingleClientMessage"), string.Join(", ", backOffProviders.Select(v => v.ImportList.Definition.Name))), "#lists-are-unavailable-due-to-failures");
} }
} }
} }

@ -1,6 +1,7 @@
using NzbDrone.Core.Configuration; using NzbDrone.Core.Configuration;
using NzbDrone.Core.Configuration.Events; using NzbDrone.Core.Configuration.Events;
using NzbDrone.Core.Download; using NzbDrone.Core.Download;
using NzbDrone.Core.Localization;
using NzbDrone.Core.ThingiProvider.Events; using NzbDrone.Core.ThingiProvider.Events;
namespace NzbDrone.Core.HealthCheck.Checks namespace NzbDrone.Core.HealthCheck.Checks
@ -12,7 +13,8 @@ namespace NzbDrone.Core.HealthCheck.Checks
{ {
private readonly IConfigService _configService; private readonly IConfigService _configService;
public ImportMechanismCheck(IConfigService configService) public ImportMechanismCheck(IConfigService configService, ILocalizationService localizationService)
: base(localizationService)
{ {
_configService = configService; _configService = configService;
} }
@ -21,7 +23,7 @@ namespace NzbDrone.Core.HealthCheck.Checks
{ {
if (!_configService.EnableCompletedDownloadHandling) if (!_configService.EnableCompletedDownloadHandling)
{ {
return new HealthCheck(GetType(), HealthCheckResult.Warning, "Enable Completed Download Handling"); return new HealthCheck(GetType(), HealthCheckResult.Warning, _localizationService.GetLocalizedString("ImportMechanismHealthCheckMessage"), "#completed-download-handling-is-disabled");
} }
return new HealthCheck(GetType()); return new HealthCheck(GetType());

@ -3,6 +3,7 @@ using System.Linq;
using NzbDrone.Common.Extensions; using NzbDrone.Common.Extensions;
using NzbDrone.Core.Indexers; using NzbDrone.Core.Indexers;
using NzbDrone.Core.Indexers.Torznab; using NzbDrone.Core.Indexers.Torznab;
using NzbDrone.Core.Localization;
using NzbDrone.Core.ThingiProvider.Events; using NzbDrone.Core.ThingiProvider.Events;
namespace NzbDrone.Core.HealthCheck.Checks namespace NzbDrone.Core.HealthCheck.Checks
@ -14,7 +15,8 @@ namespace NzbDrone.Core.HealthCheck.Checks
{ {
private readonly IIndexerFactory _providerFactory; private readonly IIndexerFactory _providerFactory;
public IndexerJackettAllCheck(IIndexerFactory providerFactory) public IndexerJackettAllCheck(IIndexerFactory providerFactory, ILocalizationService localizationService)
: base(localizationService)
{ {
_providerFactory = providerFactory; _providerFactory = providerFactory;
} }
@ -23,10 +25,10 @@ namespace NzbDrone.Core.HealthCheck.Checks
{ {
var jackettAllProviders = _providerFactory.All().Where( var jackettAllProviders = _providerFactory.All().Where(
i => i.ConfigContract.Equals("TorznabSettings") && i => i.ConfigContract.Equals("TorznabSettings") &&
((i.Settings as TorznabSettings).BaseUrl.Contains("/torznab/all/api") || ((i.Settings as TorznabSettings).BaseUrl.Contains("/torznab/all/api", StringComparison.InvariantCultureIgnoreCase) ||
(i.Settings as TorznabSettings).BaseUrl.Contains("/api/v2.0/indexers/all/results/torznab") || (i.Settings as TorznabSettings).BaseUrl.Contains("/api/v2.0/indexers/all/results/torznab", StringComparison.InvariantCultureIgnoreCase) ||
(i.Settings as TorznabSettings).ApiPath.Contains("/torznab/all/api") || (i.Settings as TorznabSettings).ApiPath.Contains("/torznab/all/api", StringComparison.InvariantCultureIgnoreCase) ||
(i.Settings as TorznabSettings).ApiPath.Contains("/api/v2.0/indexers/all/results/torznab"))); (i.Settings as TorznabSettings).ApiPath.Contains("/api/v2.0/indexers/all/results/torznab", StringComparison.InvariantCultureIgnoreCase)));
if (jackettAllProviders.Empty()) if (jackettAllProviders.Empty())
{ {
@ -35,7 +37,7 @@ namespace NzbDrone.Core.HealthCheck.Checks
return new HealthCheck(GetType(), return new HealthCheck(GetType(),
HealthCheckResult.Warning, HealthCheckResult.Warning,
string.Format("Indexers using the unsupported Jackett 'all' endpoint: {0}", string.Format(_localizationService.GetLocalizedString("IndexerJackettAll"),
string.Join(", ", jackettAllProviders.Select(i => i.Name))), string.Join(", ", jackettAllProviders.Select(i => i.Name))),
"#jackett-all-endpoint-used"); "#jackett-all-endpoint-used");
} }

@ -2,6 +2,7 @@ using System;
using System.Linq; using System.Linq;
using NzbDrone.Common.Extensions; using NzbDrone.Common.Extensions;
using NzbDrone.Core.Indexers; using NzbDrone.Core.Indexers;
using NzbDrone.Core.Localization;
using NzbDrone.Core.ThingiProvider.Events; using NzbDrone.Core.ThingiProvider.Events;
namespace NzbDrone.Core.HealthCheck.Checks namespace NzbDrone.Core.HealthCheck.Checks
@ -14,7 +15,10 @@ namespace NzbDrone.Core.HealthCheck.Checks
private readonly IIndexerFactory _providerFactory; private readonly IIndexerFactory _providerFactory;
private readonly IIndexerStatusService _providerStatusService; private readonly IIndexerStatusService _providerStatusService;
public IndexerLongTermStatusCheck(IIndexerFactory providerFactory, IIndexerStatusService providerStatusService) public IndexerLongTermStatusCheck(IIndexerFactory providerFactory,
IIndexerStatusService providerStatusService,
ILocalizationService localizationService)
: base(localizationService)
{ {
_providerFactory = providerFactory; _providerFactory = providerFactory;
_providerStatusService = providerStatusService; _providerStatusService = providerStatusService;
@ -41,13 +45,13 @@ namespace NzbDrone.Core.HealthCheck.Checks
{ {
return new HealthCheck(GetType(), return new HealthCheck(GetType(),
HealthCheckResult.Error, HealthCheckResult.Error,
"All indexers are unavailable due to failures for more than 6 hours", _localizationService.GetLocalizedString("IndexerLongTermStatusCheckAllClientMessage"),
"#indexers-are-unavailable-due-to-failures"); "#indexers-are-unavailable-due-to-failures");
} }
return new HealthCheck(GetType(), return new HealthCheck(GetType(),
HealthCheckResult.Warning, HealthCheckResult.Warning,
string.Format("Indexers unavailable due to failures for more than 6 hours: {0}", string.Format(_localizationService.GetLocalizedString("IndexerLongTermStatusCheckSingleClientMessage"),
string.Join(", ", backOffProviders.Select(v => v.Provider.Definition.Name))), string.Join(", ", backOffProviders.Select(v => v.Provider.Definition.Name))),
"#indexers-are-unavailable-due-to-failures"); "#indexers-are-unavailable-due-to-failures");
} }

@ -1,5 +1,6 @@
using NzbDrone.Common.Extensions; using NzbDrone.Common.Extensions;
using NzbDrone.Core.Indexers; using NzbDrone.Core.Indexers;
using NzbDrone.Core.Localization;
using NzbDrone.Core.ThingiProvider.Events; using NzbDrone.Core.ThingiProvider.Events;
namespace NzbDrone.Core.HealthCheck.Checks namespace NzbDrone.Core.HealthCheck.Checks
@ -12,7 +13,8 @@ namespace NzbDrone.Core.HealthCheck.Checks
{ {
private readonly IIndexerFactory _indexerFactory; private readonly IIndexerFactory _indexerFactory;
public IndexerRssCheck(IIndexerFactory indexerFactory) public IndexerRssCheck(IIndexerFactory indexerFactory, ILocalizationService localizationService)
: base(localizationService)
{ {
_indexerFactory = indexerFactory; _indexerFactory = indexerFactory;
} }
@ -23,14 +25,14 @@ namespace NzbDrone.Core.HealthCheck.Checks
if (enabled.Empty()) if (enabled.Empty())
{ {
return new HealthCheck(GetType(), HealthCheckResult.Error, "No indexers available with RSS sync enabled, Lidarr will not grab new releases automatically"); return new HealthCheck(GetType(), HealthCheckResult.Error, _localizationService.GetLocalizedString("IndexerRssHealthCheckNoIndexers"), "#no-indexers-available-with-rss-sync-enabled-lidarr-will-not-grab-new-releases-automatically");
} }
var active = _indexerFactory.RssEnabled(true); var active = _indexerFactory.RssEnabled(true);
if (active.Empty()) if (active.Empty())
{ {
return new HealthCheck(GetType(), HealthCheckResult.Warning, "All rss-capable indexers are temporarily unavailable due to recent indexer errors"); return new HealthCheck(GetType(), HealthCheckResult.Warning, _localizationService.GetLocalizedString("IndexerRssHealthCheckNoAvailableIndexers"), "#indexers-are-unavailable-due-to-failures");
} }
return new HealthCheck(GetType()); return new HealthCheck(GetType());

@ -1,5 +1,6 @@
using NzbDrone.Common.Extensions; using NzbDrone.Common.Extensions;
using NzbDrone.Core.Indexers; using NzbDrone.Core.Indexers;
using NzbDrone.Core.Localization;
using NzbDrone.Core.ThingiProvider.Events; using NzbDrone.Core.ThingiProvider.Events;
namespace NzbDrone.Core.HealthCheck.Checks namespace NzbDrone.Core.HealthCheck.Checks
@ -12,7 +13,8 @@ namespace NzbDrone.Core.HealthCheck.Checks
{ {
private readonly IIndexerFactory _indexerFactory; private readonly IIndexerFactory _indexerFactory;
public IndexerSearchCheck(IIndexerFactory indexerFactory) public IndexerSearchCheck(IIndexerFactory indexerFactory, ILocalizationService localizationService)
: base(localizationService)
{ {
_indexerFactory = indexerFactory; _indexerFactory = indexerFactory;
} }
@ -23,21 +25,21 @@ namespace NzbDrone.Core.HealthCheck.Checks
if (automaticSearchEnabled.Empty()) if (automaticSearchEnabled.Empty())
{ {
return new HealthCheck(GetType(), HealthCheckResult.Warning, "No indexers available with Automatic Search enabled, Lidarr will not provide any automatic search results"); return new HealthCheck(GetType(), HealthCheckResult.Warning, _localizationService.GetLocalizedString("IndexerSearchCheckNoAutomaticMessage"), "#no-indexers-available-with-automatic-search-enabled-lidarr-will-not-provide-any-automatic-search-results");
} }
var interactiveSearchEnabled = _indexerFactory.InteractiveSearchEnabled(false); var interactiveSearchEnabled = _indexerFactory.InteractiveSearchEnabled(false);
if (interactiveSearchEnabled.Empty()) if (interactiveSearchEnabled.Empty())
{ {
return new HealthCheck(GetType(), HealthCheckResult.Warning, "No indexers available with Interactive Search enabled, Lidarr will not provide any interactive search results"); return new HealthCheck(GetType(), HealthCheckResult.Warning, _localizationService.GetLocalizedString("IndexerSearchCheckNoInteractiveMessage"), "#no-indexers-available-with-interactive-search-enabled");
} }
var active = _indexerFactory.AutomaticSearchEnabled(true); var active = _indexerFactory.AutomaticSearchEnabled(true);
if (active.Empty()) if (active.Empty())
{ {
return new HealthCheck(GetType(), HealthCheckResult.Warning, "All search-capable indexers are temporarily unavailable due to recent indexer errors"); return new HealthCheck(GetType(), HealthCheckResult.Warning, _localizationService.GetLocalizedString("IndexerSearchCheckNoAvailableIndexersMessage"), "#indexers-are-unavailable-due-to-failures");
} }
return new HealthCheck(GetType()); return new HealthCheck(GetType());

@ -2,6 +2,7 @@ using System;
using System.Linq; using System.Linq;
using NzbDrone.Common.Extensions; using NzbDrone.Common.Extensions;
using NzbDrone.Core.Indexers; using NzbDrone.Core.Indexers;
using NzbDrone.Core.Localization;
using NzbDrone.Core.ThingiProvider.Events; using NzbDrone.Core.ThingiProvider.Events;
namespace NzbDrone.Core.HealthCheck.Checks namespace NzbDrone.Core.HealthCheck.Checks
@ -14,7 +15,8 @@ namespace NzbDrone.Core.HealthCheck.Checks
private readonly IIndexerFactory _providerFactory; private readonly IIndexerFactory _providerFactory;
private readonly IIndexerStatusService _providerStatusService; private readonly IIndexerStatusService _providerStatusService;
public IndexerStatusCheck(IIndexerFactory providerFactory, IIndexerStatusService providerStatusService) public IndexerStatusCheck(IIndexerFactory providerFactory, IIndexerStatusService providerStatusService, ILocalizationService localizationService)
: base(localizationService)
{ {
_providerFactory = providerFactory; _providerFactory = providerFactory;
_providerStatusService = providerStatusService; _providerStatusService = providerStatusService;
@ -39,10 +41,17 @@ namespace NzbDrone.Core.HealthCheck.Checks
if (backOffProviders.Count == enabledProviders.Count) if (backOffProviders.Count == enabledProviders.Count)
{ {
return new HealthCheck(GetType(), HealthCheckResult.Error, "All indexers are unavailable due to failures", "#indexers-are-unavailable-due-to-failures"); return new HealthCheck(GetType(),
HealthCheckResult.Error,
_localizationService.GetLocalizedString("IndexerStatusCheckAllClientMessage"),
"#indexers-are-unavailable-due-to-failures");
} }
return new HealthCheck(GetType(), HealthCheckResult.Warning, string.Format("Indexers unavailable due to failures: {0}", string.Join(", ", backOffProviders.Select(v => v.Provider.Definition.Name))), "#indexers-are-unavailable-due-to-failures"); return new HealthCheck(GetType(),
HealthCheckResult.Warning,
string.Format(_localizationService.GetLocalizedString("IndexerStatusCheckSingleClientMessage"),
string.Join(", ", backOffProviders.Select(v => v.Provider.Definition.Name))),
"#indexers-are-unavailable-due-to-failures");
} }
} }
} }

@ -1,6 +1,6 @@
using System.Linq; using System.Linq;
using NzbDrone.Common.Disk; using NzbDrone.Common.Disk;
using NzbDrone.Common.Extensions; using NzbDrone.Core.Localization;
using NzbDrone.Core.Music; using NzbDrone.Core.Music;
namespace NzbDrone.Core.HealthCheck.Checks namespace NzbDrone.Core.HealthCheck.Checks
@ -10,7 +10,8 @@ namespace NzbDrone.Core.HealthCheck.Checks
private readonly IDiskProvider _diskProvider; private readonly IDiskProvider _diskProvider;
private readonly IArtistService _artistService; private readonly IArtistService _artistService;
public MountCheck(IDiskProvider diskProvider, IArtistService artistService) public MountCheck(IDiskProvider diskProvider, IArtistService artistService, ILocalizationService localizationService)
: base(localizationService)
{ {
_diskProvider = diskProvider; _diskProvider = diskProvider;
_artistService = artistService; _artistService = artistService;
@ -27,7 +28,7 @@ namespace NzbDrone.Core.HealthCheck.Checks
if (mounts.Any()) if (mounts.Any())
{ {
return new HealthCheck(GetType(), HealthCheckResult.Error, "Mount containing a artist path is mounted read-only: " + string.Join(",", mounts.Select(m => m.Name)), "#artist-mount-ro"); return new HealthCheck(GetType(), HealthCheckResult.Error, _localizationService.GetLocalizedString("MountCheckMessage") + string.Join(", ", mounts.Select(m => m.Name)), "#movie-mount-ro");
} }
return new HealthCheck(GetType()); return new HealthCheck(GetType());

@ -1,9 +1,6 @@
using System;
using System.Linq;
using System.Text.RegularExpressions;
using NLog;
using NzbDrone.Common.Extensions; using NzbDrone.Common.Extensions;
using NzbDrone.Core.Configuration; using NzbDrone.Core.Configuration;
using NzbDrone.Core.Localization;
namespace NzbDrone.Core.HealthCheck.Checks namespace NzbDrone.Core.HealthCheck.Checks
{ {
@ -11,7 +8,8 @@ namespace NzbDrone.Core.HealthCheck.Checks
{ {
private readonly IDeploymentInfoProvider _deploymentInfoProvider; private readonly IDeploymentInfoProvider _deploymentInfoProvider;
public PackageGlobalMessageCheck(IDeploymentInfoProvider deploymentInfoProvider) public PackageGlobalMessageCheck(IDeploymentInfoProvider deploymentInfoProvider, ILocalizationService localizationService)
: base(localizationService)
{ {
_deploymentInfoProvider = deploymentInfoProvider; _deploymentInfoProvider = deploymentInfoProvider;
} }
@ -37,7 +35,7 @@ namespace NzbDrone.Core.HealthCheck.Checks
result = HealthCheckResult.Warning; result = HealthCheckResult.Warning;
} }
return new HealthCheck(GetType(), result, message, "#package_maintainer_message"); return new HealthCheck(GetType(), result, message, "#package-maintainer-message");
} }
} }
} }

@ -6,6 +6,7 @@ using NzbDrone.Common.Cloud;
using NzbDrone.Common.Http; using NzbDrone.Common.Http;
using NzbDrone.Core.Configuration; using NzbDrone.Core.Configuration;
using NzbDrone.Core.Configuration.Events; using NzbDrone.Core.Configuration.Events;
using NzbDrone.Core.Localization;
namespace NzbDrone.Core.HealthCheck.Checks namespace NzbDrone.Core.HealthCheck.Checks
{ {
@ -18,7 +19,8 @@ namespace NzbDrone.Core.HealthCheck.Checks
private readonly IHttpRequestBuilderFactory _cloudRequestBuilder; private readonly IHttpRequestBuilderFactory _cloudRequestBuilder;
public ProxyCheck(ILidarrCloudRequestBuilder cloudRequestBuilder, IConfigService configService, IHttpClient client, Logger logger) public ProxyCheck(ILidarrCloudRequestBuilder cloudRequestBuilder, IConfigService configService, IHttpClient client, ILocalizationService localizationService, Logger logger)
: base(localizationService)
{ {
_configService = configService; _configService = configService;
_client = client; _client = client;
@ -34,7 +36,7 @@ namespace NzbDrone.Core.HealthCheck.Checks
var addresses = Dns.GetHostAddresses(_configService.ProxyHostname); var addresses = Dns.GetHostAddresses(_configService.ProxyHostname);
if (!addresses.Any()) if (!addresses.Any())
{ {
return new HealthCheck(GetType(), HealthCheckResult.Error, string.Format("Failed to resolve the IP Address for the Configured Proxy Host {0}", _configService.ProxyHostname)); return new HealthCheck(GetType(), HealthCheckResult.Error, string.Format(_localizationService.GetLocalizedString("ProxyCheckResolveIpMessage"), _configService.ProxyHostname), "#proxy-failed-resolve-ip");
} }
var request = _cloudRequestBuilder.Create() var request = _cloudRequestBuilder.Create()
@ -49,13 +51,13 @@ namespace NzbDrone.Core.HealthCheck.Checks
if (response.StatusCode == HttpStatusCode.BadRequest) if (response.StatusCode == HttpStatusCode.BadRequest)
{ {
_logger.Error("Proxy Health Check failed: {0}", response.StatusCode); _logger.Error("Proxy Health Check failed: {0}", response.StatusCode);
return new HealthCheck(GetType(), HealthCheckResult.Error, $"Failed to test proxy. StatusCode: {response.StatusCode}"); return new HealthCheck(GetType(), HealthCheckResult.Error, string.Format(_localizationService.GetLocalizedString("ProxyCheckBadRequestMessage"), response.StatusCode), "#proxy-failed-test");
} }
} }
catch (Exception ex) catch (Exception ex)
{ {
_logger.Error(ex, "Proxy Health Check failed"); _logger.Error(ex, "Proxy Health Check failed");
return new HealthCheck(GetType(), HealthCheckResult.Error, $"Failed to test proxy: {request.Url}"); return new HealthCheck(GetType(), HealthCheckResult.Error, string.Format(_localizationService.GetLocalizedString("ProxyCheckFailedToTestMessage"), request.Url), "#proxy-failed-test");
} }
} }

@ -12,13 +12,12 @@ namespace NzbDrone.Core.HealthCheck.Checks
{ {
private readonly IConfigService _configService; private readonly IConfigService _configService;
private readonly IDiskProvider _diskProvider; private readonly IDiskProvider _diskProvider;
private readonly ILocalizationService _localizationService;
public RecyclingBinCheck(IConfigService configService, IDiskProvider diskProvider, ILocalizationService localizationService) public RecyclingBinCheck(IConfigService configService, IDiskProvider diskProvider, ILocalizationService localizationService)
: base(localizationService)
{ {
_configService = configService; _configService = configService;
_diskProvider = diskProvider; _diskProvider = diskProvider;
_localizationService = localizationService;
} }
public override HealthCheck Check() public override HealthCheck Check()

@ -1,5 +1,6 @@
using NzbDrone.Core.Indexers; using NzbDrone.Core.Indexers;
using NzbDrone.Core.Indexers.Gazelle; using NzbDrone.Core.Indexers.Gazelle;
using NzbDrone.Core.Localization;
using NzbDrone.Core.ThingiProvider.Events; using NzbDrone.Core.ThingiProvider.Events;
namespace NzbDrone.Core.HealthCheck.Checks namespace NzbDrone.Core.HealthCheck.Checks
@ -11,7 +12,8 @@ namespace NzbDrone.Core.HealthCheck.Checks
{ {
private readonly IIndexerFactory _indexerFactory; private readonly IIndexerFactory _indexerFactory;
public RedactedGazelleCheck(IIndexerFactory indexerFactory) public RedactedGazelleCheck(IIndexerFactory indexerFactory, ILocalizationService localizationService)
: base(localizationService)
{ {
_indexerFactory = indexerFactory; _indexerFactory = indexerFactory;
} }
@ -24,8 +26,7 @@ namespace NzbDrone.Core.HealthCheck.Checks
{ {
var definition = (IndexerDefinition)indexer.Definition; var definition = (IndexerDefinition)indexer.Definition;
if (definition.Settings is GazelleSettings s && if (definition.Settings is GazelleSettings { BaseUrl: "https://redacted.ch" })
s.BaseUrl == "https://redacted.ch")
{ {
return new HealthCheck(GetType(), HealthCheckResult.Warning, "You have set up Redacted as a Gazelle indexer, please reconfigure using the Redacted indexer setting"); return new HealthCheck(GetType(), HealthCheckResult.Warning, "You have set up Redacted as a Gazelle indexer, please reconfigure using the Redacted indexer setting");
} }

@ -1,5 +1,6 @@
using System; using System;
using System.Linq; using System.Linq;
using System.Net.Http;
using NLog; using NLog;
using NzbDrone.Common.Disk; using NzbDrone.Common.Disk;
using NzbDrone.Common.EnvironmentInfo; using NzbDrone.Common.EnvironmentInfo;
@ -9,6 +10,7 @@ using NzbDrone.Core.Configuration;
using NzbDrone.Core.Datastore.Events; using NzbDrone.Core.Datastore.Events;
using NzbDrone.Core.Download; using NzbDrone.Core.Download;
using NzbDrone.Core.Download.Clients; using NzbDrone.Core.Download.Clients;
using NzbDrone.Core.Localization;
using NzbDrone.Core.MediaFiles.Events; using NzbDrone.Core.MediaFiles.Events;
using NzbDrone.Core.RemotePathMappings; using NzbDrone.Core.RemotePathMappings;
using NzbDrone.Core.ThingiProvider.Events; using NzbDrone.Core.ThingiProvider.Events;
@ -19,9 +21,8 @@ namespace NzbDrone.Core.HealthCheck.Checks
[CheckOn(typeof(ProviderUpdatedEvent<IDownloadClient>))] [CheckOn(typeof(ProviderUpdatedEvent<IDownloadClient>))]
[CheckOn(typeof(ProviderDeletedEvent<IDownloadClient>))] [CheckOn(typeof(ProviderDeletedEvent<IDownloadClient>))]
[CheckOn(typeof(ModelEvent<RemotePathMapping>))] [CheckOn(typeof(ModelEvent<RemotePathMapping>))]
[CheckOn(typeof(TrackImportedEvent), CheckOnCondition.FailedOnly)] [CheckOn(typeof(TrackImportedEvent), CheckOnCondition.SuccessfulOnly)]
[CheckOn(typeof(TrackImportFailedEvent), CheckOnCondition.SuccessfulOnly)] public class RemotePathMappingCheck : HealthCheckBase, IProvideHealthCheck
public class RemotePathMappingCheck : HealthCheckBase, IProvideHealthCheckWithMessage
{ {
private readonly IDiskProvider _diskProvider; private readonly IDiskProvider _diskProvider;
private readonly IProvideDownloadClient _downloadClientProvider; private readonly IProvideDownloadClient _downloadClientProvider;
@ -33,7 +34,9 @@ namespace NzbDrone.Core.HealthCheck.Checks
IProvideDownloadClient downloadClientProvider, IProvideDownloadClient downloadClientProvider,
IConfigService configService, IConfigService configService,
IOsInfo osInfo, IOsInfo osInfo,
Logger logger) Logger logger,
ILocalizationService localizationService)
: base(localizationService)
{ {
_diskProvider = diskProvider; _diskProvider = diskProvider;
_downloadClientProvider = downloadClientProvider; _downloadClientProvider = downloadClientProvider;
@ -65,15 +68,15 @@ namespace NzbDrone.Core.HealthCheck.Checks
{ {
if (!status.IsLocalhost) if (!status.IsLocalhost)
{ {
return new HealthCheck(GetType(), HealthCheckResult.Error, $"Remote download client {client.Definition.Name} places downloads in {folder.FullPath} but this is not a valid {_osInfo.Name} path. Review your remote path mappings and download client settings.", "#bad-remote-path-mapping"); return new HealthCheck(GetType(), HealthCheckResult.Error, string.Format(_localizationService.GetLocalizedString("RemotePathMappingCheckWrongOSPath"), client.Definition.Name, folder.FullPath, _osInfo.Name), "#bad-remote-path-mapping");
} }
else if (_osInfo.IsDocker) else if (_osInfo.IsDocker)
{ {
return new HealthCheck(GetType(), HealthCheckResult.Error, $"You are using docker; download client {client.Definition.Name} places downloads in {folder.FullPath} but this is not a valid {_osInfo.Name} path. Review your remote path mappings and download client settings.", "#docker-bad-remote-path-mapping"); return new HealthCheck(GetType(), HealthCheckResult.Error, string.Format(_localizationService.GetLocalizedString("RemotePathMappingCheckBadDockerPath"), client.Definition.Name, folder.FullPath, _osInfo.Name), "#docker-bad-remote-path-mapping");
} }
else else
{ {
return new HealthCheck(GetType(), HealthCheckResult.Error, $"Local download client {client.Definition.Name} places downloads in {folder.FullPath} but this is not a valid {_osInfo.Name} path. Review your download client settings.", "#bad-download-client-settings"); return new HealthCheck(GetType(), HealthCheckResult.Error, string.Format(_localizationService.GetLocalizedString("RemotePathMappingCheckLocalWrongOSPath"), client.Definition.Name, folder.FullPath, _osInfo.Name), "#bad-download-client-settings");
} }
} }
@ -81,15 +84,15 @@ namespace NzbDrone.Core.HealthCheck.Checks
{ {
if (_osInfo.IsDocker) if (_osInfo.IsDocker)
{ {
return new HealthCheck(GetType(), HealthCheckResult.Error, $"You are using docker; download client {client.Definition.Name} places downloads in {folder.FullPath} but this directory does not appear to exist inside the container. Review your remote path mappings and container volume settings.", "#docker-bad-remote-path-mapping"); return new HealthCheck(GetType(), HealthCheckResult.Error, string.Format(_localizationService.GetLocalizedString("RemotePathMappingCheckDockerFolderMissing"), client.Definition.Name, folder.FullPath), "#docker-bad-remote-path-mapping");
} }
else if (!status.IsLocalhost) else if (!status.IsLocalhost)
{ {
return new HealthCheck(GetType(), HealthCheckResult.Error, $"Remote download client {client.Definition.Name} places downloads in {folder.FullPath} but this directory does not appear to exist. Likely missing or incorrect remote path mapping.", "#bad-remote-path-mapping"); return new HealthCheck(GetType(), HealthCheckResult.Error, string.Format(_localizationService.GetLocalizedString("RemotePathMappingCheckLocalFolderMissing"), client.Definition.Name, folder.FullPath), "#bad-remote-path-mapping");
} }
else else
{ {
return new HealthCheck(GetType(), HealthCheckResult.Error, $"Download client {client.Definition.Name} places downloads in {folder.FullPath} but Lidarr cannot see this directory. You may need to adjust the folder's permissions.", "#permissions-error"); return new HealthCheck(GetType(), HealthCheckResult.Error, string.Format(_localizationService.GetLocalizedString("RemotePathMappingCheckGenericPermissions"), client.Definition.Name, folder.FullPath), "#permissions-error");
} }
} }
} }
@ -98,9 +101,13 @@ namespace NzbDrone.Core.HealthCheck.Checks
{ {
_logger.Debug(ex, "Unable to communicate with {0}", client.Definition.Name); _logger.Debug(ex, "Unable to communicate with {0}", client.Definition.Name);
} }
catch (HttpRequestException ex)
{
_logger.Debug(ex, "Unable to communicate with {0}", client.Definition.Name);
}
catch (Exception ex) catch (Exception ex)
{ {
_logger.Error(ex, "Unknown error occurred in RemotePathMapping HealthCheck"); _logger.Error(ex, "Unknown error occured in RemotePathMapping HealthCheck");
} }
} }
@ -125,19 +132,26 @@ namespace NzbDrone.Core.HealthCheck.Checks
var trackPath = failureMessage.TrackInfo.Path; var trackPath = failureMessage.TrackInfo.Path;
if (_diskProvider.FileExists(trackPath)) if (_diskProvider.FileExists(trackPath))
{ {
return new HealthCheck(GetType(), HealthCheckResult.Error, $"Lidarr can see but not access downloaded track {trackPath}. Likely permissions error.", "#permissions-error"); return new HealthCheck(GetType(), HealthCheckResult.Error, string.Format(_localizationService.GetLocalizedString("RemotePathMappingCheckDownloadPermissions"), trackPath), "#permissions-error");
} }
else else
{ {
// If the file doesn't exist but TrackInfo is not null then the message is coming from // If the file doesn't exist but TrackInfo is not null then the message is coming from
// ImportApprovedTracks and the file must have been removed part way through processing // ImportApprovedTracks and the file must have been removed part way through processing
return new HealthCheck(GetType(), HealthCheckResult.Error, $"File {trackPath} was removed part way through processing."); return new HealthCheck(GetType(), HealthCheckResult.Error, string.Format(_localizationService.GetLocalizedString("RemotePathMappingCheckFileRemoved"), trackPath), "#remote-path-file-removed");
} }
} }
// If the previous case did not match then the failure occurred in DownloadedTracksImportService, // If the previous case did not match then the failure occured in DownloadedTracksImportService,
// while trying to locate the files reported by the download client // while trying to locate the files reported by the download client
var client = _downloadClientProvider.Get(failureMessage.DownloadClientInfo.Id); // Only check clients not in failure status, those get another message
var client = _downloadClientProvider.GetDownloadClients(true).FirstOrDefault(x => x.Definition.Name == failureMessage.DownloadClientInfo.Name);
if (client == null)
{
return new HealthCheck(GetType());
}
try try
{ {
var status = client.GetStatus(); var status = client.GetStatus();
@ -147,52 +161,56 @@ namespace NzbDrone.Core.HealthCheck.Checks
// that the user realises something is wrong. // that the user realises something is wrong.
if (dlpath.IsNullOrWhiteSpace()) if (dlpath.IsNullOrWhiteSpace())
{ {
return new HealthCheck(GetType(), HealthCheckResult.Error, $"Lidarr failed to import a track. Check your logs for details."); return new HealthCheck(GetType(), HealthCheckResult.Error, _localizationService.GetLocalizedString("RemotePathMappingCheckImportFailed"), "#remote-path-import-failed");
} }
if (!dlpath.IsPathValid(PathValidationType.CurrentOs)) if (!dlpath.IsPathValid(PathValidationType.CurrentOs))
{ {
if (!status.IsLocalhost) if (!status.IsLocalhost)
{ {
return new HealthCheck(GetType(), HealthCheckResult.Error, $"Remote download client {client.Definition.Name} reported files in {dlpath} but this is not a valid {_osInfo.Name} path. Review your remote path mappings and download client settings.", "#bad-remote-path-mapping"); return new HealthCheck(GetType(), HealthCheckResult.Error, string.Format(_localizationService.GetLocalizedString("RemotePathMappingCheckFilesWrongOSPath"), client.Definition.Name, dlpath, _osInfo.Name), "#bad-remote-path-mapping");
} }
else if (_osInfo.IsDocker) else if (_osInfo.IsDocker)
{ {
return new HealthCheck(GetType(), HealthCheckResult.Error, $"You are using docker; download client {client.Definition.Name} reported files in {dlpath} but this is not a valid {_osInfo.Name} path. Review your remote path mappings and download client settings.", "#docker-bad-remote-path-mapping"); return new HealthCheck(GetType(), HealthCheckResult.Error, string.Format(_localizationService.GetLocalizedString("RemotePathMappingCheckFilesBadDockerPath"), client.Definition.Name, dlpath, _osInfo.Name), "#docker-bad-remote-path-mapping");
} }
else else
{ {
return new HealthCheck(GetType(), HealthCheckResult.Error, $"Local download client {client.Definition.Name} reported files in {dlpath} but this is not a valid {_osInfo.Name} path. Review your download client settings.", "#bad-download-client-settings"); return new HealthCheck(GetType(), HealthCheckResult.Error, string.Format(_localizationService.GetLocalizedString("RemotePathMappingCheckFilesLocalWrongOSPath"), client.Definition.Name, dlpath, _osInfo.Name), "#bad-download-client-settings");
} }
} }
if (_diskProvider.FolderExists(dlpath)) if (_diskProvider.FolderExists(dlpath))
{ {
return new HealthCheck(GetType(), HealthCheckResult.Error, $"Lidarr can see but not access download directory {dlpath}. Likely permissions error.", "#permissions-error"); return new HealthCheck(GetType(), HealthCheckResult.Error, string.Format(_localizationService.GetLocalizedString("RemotePathMappingCheckFolderPermissions"), dlpath), "#permissions-error");
} }
// if it's a remote client/docker, likely missing path mappings // if it's a remote client/docker, likely missing path mappings
if (_osInfo.IsDocker) if (_osInfo.IsDocker)
{ {
return new HealthCheck(GetType(), HealthCheckResult.Error, $"You are using docker; download client {client.Definition.Name} reported files in {dlpath} but this directory does not appear to exist inside the container. Review your remote path mappings and container volume settings.", "#docker-bad-remote-path-mapping"); return new HealthCheck(GetType(), HealthCheckResult.Error, string.Format(_localizationService.GetLocalizedString("RemotePathMappingCheckFolderPermissions"), client.Definition.Name, dlpath), "#docker-bad-remote-path-mapping");
} }
else if (!status.IsLocalhost) else if (!status.IsLocalhost)
{ {
return new HealthCheck(GetType(), HealthCheckResult.Error, $"Remote download client {client.Definition.Name} reported files in {dlpath} but this directory does not appear to exist. Likely missing remote path mapping.", "#bad-remote-path-mapping"); return new HealthCheck(GetType(), HealthCheckResult.Error, string.Format(_localizationService.GetLocalizedString("RemotePathMappingCheckRemoteDownloadClient"), client.Definition.Name, dlpath), "#bad-remote-path-mapping");
} }
else else
{ {
// path mappings shouldn't be needed locally so probably a permissions issue // path mappings shouldn't be needed locally so probably a permissions issue
return new HealthCheck(GetType(), HealthCheckResult.Error, $"Download client {client.Definition.Name} reported files in {dlpath} but Lidarr cannot see this directory. You may need to adjust the folder's permissions.", "#permissions-error"); return new HealthCheck(GetType(), HealthCheckResult.Error, string.Format(_localizationService.GetLocalizedString("RemotePathMappingCheckFilesGenericPermissions"), client.Definition.Name, dlpath), "#permissions-error");
} }
} }
catch (DownloadClientException ex) catch (DownloadClientException ex)
{ {
_logger.Debug(ex, "Unable to communicate with {0}", client.Definition.Name); _logger.Debug(ex, "Unable to communicate with {0}", client.Definition.Name);
} }
catch (HttpRequestException ex)
{
_logger.Debug(ex, "Unable to communicate with {0}", client.Definition.Name);
}
catch (Exception ex) catch (Exception ex)
{ {
_logger.Error(ex, "Unknown error occurred in RemotePathMapping HealthCheck"); _logger.Error(ex, "Unknown error occured in RemotePathMapping HealthCheck");
} }
return new HealthCheck(GetType()); return new HealthCheck(GetType());

@ -1,6 +1,7 @@
using System.Linq; using System.Linq;
using NzbDrone.Common.Disk; using NzbDrone.Common.Disk;
using NzbDrone.Core.ImportLists; using NzbDrone.Core.ImportLists;
using NzbDrone.Core.Localization;
using NzbDrone.Core.MediaFiles.Events; using NzbDrone.Core.MediaFiles.Events;
using NzbDrone.Core.Music; using NzbDrone.Core.Music;
using NzbDrone.Core.Music.Events; using NzbDrone.Core.Music.Events;
@ -19,7 +20,8 @@ namespace NzbDrone.Core.HealthCheck.Checks
private readonly IDiskProvider _diskProvider; private readonly IDiskProvider _diskProvider;
private readonly IRootFolderService _rootFolderService; private readonly IRootFolderService _rootFolderService;
public RootFolderCheck(IArtistService artistService, IImportListFactory importListFactory, IDiskProvider diskProvider, IRootFolderService rootFolderService) public RootFolderCheck(IArtistService artistService, IImportListFactory importListFactory, IDiskProvider diskProvider, IRootFolderService rootFolderService, ILocalizationService localizationService)
: base(localizationService)
{ {
_artistService = artistService; _artistService = artistService;
_importListFactory = importListFactory; _importListFactory = importListFactory;
@ -48,10 +50,10 @@ namespace NzbDrone.Core.HealthCheck.Checks
{ {
if (missingRootFolders.Count == 1) if (missingRootFolders.Count == 1)
{ {
return new HealthCheck(GetType(), HealthCheckResult.Error, "Missing root folder: " + missingRootFolders.First(), "#missing-root-folder"); return new HealthCheck(GetType(), HealthCheckResult.Error, string.Format(_localizationService.GetLocalizedString("RootFolderCheckSingleMessage"), missingRootFolders.First()), "#missing-root-folder");
} }
var message = string.Format("Multiple root folders are missing: {0}", string.Join(" | ", missingRootFolders)); var message = string.Format(_localizationService.GetLocalizedString("RootFolderCheckMultipleMessage"), string.Join(" | ", missingRootFolders));
return new HealthCheck(GetType(), HealthCheckResult.Error, message, "#missing-root-folder"); return new HealthCheck(GetType(), HealthCheckResult.Error, message, "#missing-root-folder");
} }

@ -3,6 +3,7 @@ using NLog;
using NzbDrone.Common.Cloud; using NzbDrone.Common.Cloud;
using NzbDrone.Common.Http; using NzbDrone.Common.Http;
using NzbDrone.Common.Serializer; using NzbDrone.Common.Serializer;
using NzbDrone.Core.Localization;
namespace NzbDrone.Core.HealthCheck.Checks namespace NzbDrone.Core.HealthCheck.Checks
{ {
@ -12,7 +13,8 @@ namespace NzbDrone.Core.HealthCheck.Checks
private readonly IHttpRequestBuilderFactory _cloudRequestBuilder; private readonly IHttpRequestBuilderFactory _cloudRequestBuilder;
private readonly Logger _logger; private readonly Logger _logger;
public SystemTimeCheck(IHttpClient client, ILidarrCloudRequestBuilder cloudRequestBuilder, Logger logger) public SystemTimeCheck(IHttpClient client, ILidarrCloudRequestBuilder cloudRequestBuilder, ILocalizationService localizationService, Logger logger)
: base(localizationService)
{ {
_client = client; _client = client;
_cloudRequestBuilder = cloudRequestBuilder.Services; _cloudRequestBuilder = cloudRequestBuilder.Services;
@ -33,7 +35,7 @@ namespace NzbDrone.Core.HealthCheck.Checks
if (Math.Abs(result.DateTimeUtc.Subtract(systemTime).TotalDays) >= 1) if (Math.Abs(result.DateTimeUtc.Subtract(systemTime).TotalDays) >= 1)
{ {
_logger.Error("System time mismatch. SystemTime: {0} Expected Time: {1}. Update system time", systemTime, result.DateTimeUtc); _logger.Error("System time mismatch. SystemTime: {0} Expected Time: {1}. Update system time", systemTime, result.DateTimeUtc);
return new HealthCheck(GetType(), HealthCheckResult.Error, $"System time is off by more than 1 day. Scheduled tasks may not run correctly until the time is corrected"); return new HealthCheck(GetType(), HealthCheckResult.Error, _localizationService.GetLocalizedString("SystemTimeCheckMessage"), "#system-time-off");
} }
return new HealthCheck(GetType()); return new HealthCheck(GetType());

@ -5,6 +5,7 @@ using NzbDrone.Common.EnvironmentInfo;
using NzbDrone.Common.Extensions; using NzbDrone.Common.Extensions;
using NzbDrone.Core.Configuration; using NzbDrone.Core.Configuration;
using NzbDrone.Core.Configuration.Events; using NzbDrone.Core.Configuration.Events;
using NzbDrone.Core.Localization;
using NzbDrone.Core.Update; using NzbDrone.Core.Update;
namespace NzbDrone.Core.HealthCheck.Checks namespace NzbDrone.Core.HealthCheck.Checks
@ -22,7 +23,9 @@ namespace NzbDrone.Core.HealthCheck.Checks
IAppFolderInfo appFolderInfo, IAppFolderInfo appFolderInfo,
ICheckUpdateService checkUpdateService, ICheckUpdateService checkUpdateService,
IConfigFileProvider configFileProvider, IConfigFileProvider configFileProvider,
IOsInfo osInfo) IOsInfo osInfo,
ILocalizationService localizationService)
: base(localizationService)
{ {
_diskProvider = diskProvider; _diskProvider = diskProvider;
_appFolderInfo = appFolderInfo; _appFolderInfo = appFolderInfo;
@ -44,33 +47,30 @@ namespace NzbDrone.Core.HealthCheck.Checks
{ {
return new HealthCheck(GetType(), return new HealthCheck(GetType(),
HealthCheckResult.Error, HealthCheckResult.Error,
string.Format("Cannot install update because startup folder '{0}' is in an App Translocation folder.", startupFolder), string.Format(_localizationService.GetLocalizedString("UpdateCheckStartupTranslocationMessage"), startupFolder),
"Cannot install update because startup folder is in an App Translocation folder."); "#cannot-install-update-because-startup-folder-is-in-an-app-translocation-folder.");
} }
if (!_diskProvider.FolderWritable(startupFolder)) if (!_diskProvider.FolderWritable(startupFolder))
{ {
return new HealthCheck(GetType(), return new HealthCheck(GetType(),
HealthCheckResult.Error, HealthCheckResult.Error,
string.Format("Cannot install update because startup folder '{0}' is not writable by the user '{1}'.", startupFolder, Environment.UserName), string.Format(_localizationService.GetLocalizedString("UpdateCheckStartupNotWritableMessage"), startupFolder, Environment.UserName),
"Cannot install update because startup folder is not writable by the user"); "#cannot-install-update-because-startup-folder-is-not-writable-by-the-user");
} }
if (!_diskProvider.FolderWritable(uiFolder)) if (!_diskProvider.FolderWritable(uiFolder))
{ {
return new HealthCheck(GetType(), return new HealthCheck(GetType(),
HealthCheckResult.Error, HealthCheckResult.Error,
string.Format("Cannot install update because UI folder '{0}' is not writable by the user '{1}'.", uiFolder, Environment.UserName), string.Format(_localizationService.GetLocalizedString("UpdateCheckUINotWritableMessage"), uiFolder, Environment.UserName),
"Cannot install update because UI folder is not writable by the user"); "#cannot-install-update-because-ui-folder-is-not-writable-by-the-user");
} }
} }
if (BuildInfo.BuildDateTime < DateTime.UtcNow.AddDays(-14)) if (BuildInfo.BuildDateTime < DateTime.UtcNow.AddDays(-14) && _checkUpdateService.AvailableUpdate() != null)
{ {
if (_checkUpdateService.AvailableUpdate() != null) return new HealthCheck(GetType(), HealthCheckResult.Warning, _localizationService.GetLocalizedString("UpdateAvailable"), "#new-update-is-available");
{
return new HealthCheck(GetType(), HealthCheckResult.Warning, "New update is available");
}
} }
return new HealthCheck(GetType()); return new HealthCheck(GetType());

@ -1,7 +1,15 @@
using NzbDrone.Core.Localization;
namespace NzbDrone.Core.HealthCheck namespace NzbDrone.Core.HealthCheck
{ {
public abstract class HealthCheckBase : IProvideHealthCheck public abstract class HealthCheckBase : IProvideHealthCheck
{ {
public readonly ILocalizationService _localizationService;
public HealthCheckBase(ILocalizationService localizationService)
{
_localizationService = localizationService;
}
public abstract HealthCheck Check(); public abstract HealthCheck Check();
public virtual bool CheckOnStartup => true; public virtual bool CheckOnStartup => true;

@ -69,6 +69,7 @@
"AnyReleaseOkHelpText": "Lidarr will automatically switch to the release best matching downloaded tracks", "AnyReleaseOkHelpText": "Lidarr will automatically switch to the release best matching downloaded tracks",
"ApiKeyHelpTextWarning": "Requires restart to take effect", "ApiKeyHelpTextWarning": "Requires restart to take effect",
"AppDataDirectory": "AppData directory", "AppDataDirectory": "AppData directory",
"AppDataLocationHealthCheckMessage": "Updating will not be possible to prevent deleting AppData on Update",
"ApplicationURL": "Application URL", "ApplicationURL": "Application URL",
"ApplicationUrlHelpText": "This application's external URL including http(s)://, port and URL base", "ApplicationUrlHelpText": "This application's external URL including http(s)://, port and URL base",
"Apply": "Apply", "Apply": "Apply",
@ -239,7 +240,12 @@
"Donations": "Donations", "Donations": "Donations",
"DoneEditingGroups": "Done Editing Groups", "DoneEditingGroups": "Done Editing Groups",
"DownloadClient": "Download Client", "DownloadClient": "Download Client",
"DownloadClientCheckDownloadingToRoot": "Download client {0} places downloads in the root folder {1}. You should not download to a root folder.",
"DownloadClientCheckNoneAvailableMessage": "No download client is available",
"DownloadClientCheckUnableToCommunicateMessage": "Unable to communicate with {0}.",
"DownloadClientSettings": "Download Client Settings", "DownloadClientSettings": "Download Client Settings",
"DownloadClientStatusCheckAllClientMessage": "All download clients are unavailable due to failures",
"DownloadClientStatusCheckSingleClientMessage": "Download clients unavailable due to failures: {0}",
"DownloadClients": "Download Clients", "DownloadClients": "Download Clients",
"DownloadFailed": "Download Failed", "DownloadFailed": "Download Failed",
"DownloadFailedCheckDownloadClientForMoreDetails": "Download failed: check download client for more details", "DownloadFailedCheckDownloadClientForMoreDetails": "Download failed: check download client for more details",
@ -379,7 +385,10 @@
"ImportListExclusions": "Import List Exclusions", "ImportListExclusions": "Import List Exclusions",
"ImportListSettings": "General Import List Settings", "ImportListSettings": "General Import List Settings",
"ImportListSpecificSettings": "Import List Specific Settings", "ImportListSpecificSettings": "Import List Specific Settings",
"ImportListStatusCheckAllClientMessage": "All lists are unavailable due to failures",
"ImportListStatusCheckSingleClientMessage": "Lists unavailable due to failures: {0}",
"ImportLists": "Import Lists", "ImportLists": "Import Lists",
"ImportMechanismHealthCheckMessage": "Enable Completed Download Handling",
"ImportedTo": "Imported To", "ImportedTo": "Imported To",
"Importing": "Importing", "Importing": "Importing",
"Inactive": "Inactive", "Inactive": "Inactive",
@ -391,8 +400,18 @@
"IndexerDownloadClientHelpText": "Specify which download client is used for grabs from this indexer", "IndexerDownloadClientHelpText": "Specify which download client is used for grabs from this indexer",
"IndexerIdHelpText": "Specify what indexer the profile applies to", "IndexerIdHelpText": "Specify what indexer the profile applies to",
"IndexerIdHelpTextWarning": "Using a specific indexer with preferred words can lead to duplicate releases being grabbed", "IndexerIdHelpTextWarning": "Using a specific indexer with preferred words can lead to duplicate releases being grabbed",
"IndexerJackettAll": "Indexers using the unsupported Jackett 'all' endpoint: {0}",
"IndexerLongTermStatusCheckAllClientMessage": "All indexers are unavailable due to failures for more than 6 hours",
"IndexerLongTermStatusCheckSingleClientMessage": "Indexers unavailable due to failures for more than 6 hours: {0}",
"IndexerPriority": "Indexer Priority", "IndexerPriority": "Indexer Priority",
"IndexerRssHealthCheckNoAvailableIndexers": "All rss-capable indexers are temporarily unavailable due to recent indexer errors",
"IndexerRssHealthCheckNoIndexers": "No indexers available with RSS sync enabled, Lidarr will not grab new releases automatically",
"IndexerSearchCheckNoAutomaticMessage": "No indexers available with Automatic Search enabled, Lidarr will not provide any automatic search results",
"IndexerSearchCheckNoAvailableIndexersMessage": "All search-capable indexers are temporarily unavailable due to recent indexer errors",
"IndexerSearchCheckNoInteractiveMessage": "No indexers available with Interactive Search enabled, Lidarr will not provide any interactive search results",
"IndexerSettings": "Indexer Settings", "IndexerSettings": "Indexer Settings",
"IndexerStatusCheckAllClientMessage": "All indexers are unavailable due to failures",
"IndexerStatusCheckSingleClientMessage": "Indexers unavailable due to failures: {0}",
"IndexerTagHelpText": "Only use this indexer for artist with at least one matching tag. Leave blank to use with all artists.", "IndexerTagHelpText": "Only use this indexer for artist with at least one matching tag. Leave blank to use with all artists.",
"Indexers": "Indexers", "Indexers": "Indexers",
"Info": "Info", "Info": "Info",
@ -500,6 +519,7 @@
"MonitoringOptions": "Monitoring Options", "MonitoringOptions": "Monitoring Options",
"MonitoringOptionsHelpText": "Which albums should be monitored after the artist is added (one-time adjustment)", "MonitoringOptionsHelpText": "Which albums should be monitored after the artist is added (one-time adjustment)",
"MoreInfo": "More Info", "MoreInfo": "More Info",
"MountCheckMessage": "Mount containing a movie path is mounted read-only: ",
"MoveAutomatically": "Move Automatically", "MoveAutomatically": "Move Automatically",
"MoveFiles": "Move Files", "MoveFiles": "Move Files",
"MultiDiscTrackFormat": "Multi Disc Track Format", "MultiDiscTrackFormat": "Multi Disc Track Format",
@ -600,6 +620,9 @@
"ProtocolHelpText": "Choose which protocol(s) to use and which one is preferred when choosing between otherwise equal releases", "ProtocolHelpText": "Choose which protocol(s) to use and which one is preferred when choosing between otherwise equal releases",
"Proxy": "Proxy", "Proxy": "Proxy",
"ProxyBypassFilterHelpText": "Use ',' as a separator, and '*.' as a wildcard for subdomains", "ProxyBypassFilterHelpText": "Use ',' as a separator, and '*.' as a wildcard for subdomains",
"ProxyCheckBadRequestMessage": "Failed to test proxy. StatusCode: {0}",
"ProxyCheckFailedToTestMessage": "Failed to test proxy: {0}",
"ProxyCheckResolveIpMessage": "Failed to resolve the IP Address for the Configured Proxy Host {0}",
"ProxyPasswordHelpText": "You only need to enter a username and password if one is required. Leave them blank otherwise.", "ProxyPasswordHelpText": "You only need to enter a username and password if one is required. Leave them blank otherwise.",
"ProxyType": "Proxy Type", "ProxyType": "Proxy Type",
"ProxyUsernameHelpText": "You only need to enter a username and password if one is required. Leave them blank otherwise.", "ProxyUsernameHelpText": "You only need to enter a username and password if one is required. Leave them blank otherwise.",
@ -644,6 +667,21 @@
"Reload": "Reload", "Reload": "Reload",
"RemotePath": "Remote Path", "RemotePath": "Remote Path",
"RemotePathHelpText": "Root path to the directory that the Download Client accesses", "RemotePathHelpText": "Root path to the directory that the Download Client accesses",
"RemotePathMappingCheckBadDockerPath": "You are using docker; download client {0} places downloads in {1} but this is not a valid {2} path. Review your remote path mappings and download client settings.",
"RemotePathMappingCheckDockerFolderMissing": "You are using docker; download client {0} places downloads in {1} but this directory does not appear to exist inside the container. Review your remote path mappings and container volume settings.",
"RemotePathMappingCheckDownloadPermissions": "Lidarr can see but not access downloaded movie {0}. Likely permissions error.",
"RemotePathMappingCheckFileRemoved": "File {0} was removed part way through processing.",
"RemotePathMappingCheckFilesBadDockerPath": "You are using docker; download client {0} reported files in {1} but this is not a valid {2} path. Review your remote path mappings and download client settings.",
"RemotePathMappingCheckFilesGenericPermissions": "Download client {0} reported files in {1} but Lidarr cannot see this directory. You may need to adjust the folder's permissions.",
"RemotePathMappingCheckFilesLocalWrongOSPath": "Local download client {0} reported files in {1} but this is not a valid {2} path. Review your download client settings.",
"RemotePathMappingCheckFilesWrongOSPath": "Remote download client {0} reported files in {1} but this is not a valid {2} path. Review your remote path mappings and download client settings.",
"RemotePathMappingCheckFolderPermissions": "Lidarr can see but not access download directory {0}. Likely permissions error.",
"RemotePathMappingCheckGenericPermissions": "Download client {0} places downloads in {1} but Lidarr cannot see this directory. You may need to adjust the folder's permissions.",
"RemotePathMappingCheckImportFailed": "Lidarr failed to import a movie. Check your logs for details.",
"RemotePathMappingCheckLocalFolderMissing": "Remote download client {0} places downloads in {1} but this directory does not appear to exist. Likely missing or incorrect remote path mapping.",
"RemotePathMappingCheckLocalWrongOSPath": "Local download client {0} places downloads in {1} but this is not a valid {2} path. Review your download client settings.",
"RemotePathMappingCheckRemoteDownloadClient": "Remote download client {0} reported files in {1} but this directory does not appear to exist. Likely missing remote path mapping.",
"RemotePathMappingCheckWrongOSPath": "Remote download client {0} places downloads in {1} but this is not a valid {2} path. Review your remote path mappings and download client settings.",
"RemotePathMappings": "Remote Path Mappings", "RemotePathMappings": "Remote Path Mappings",
"Remove": "Remove", "Remove": "Remove",
"RemoveCompleted": "Remove Completed", "RemoveCompleted": "Remove Completed",
@ -695,6 +733,8 @@
"RetentionHelpText": "Usenet only: Set to zero to set for unlimited retention", "RetentionHelpText": "Usenet only: Set to zero to set for unlimited retention",
"RetryingDownloadInterp": "Retrying download {0} at {1}", "RetryingDownloadInterp": "Retrying download {0} at {1}",
"RootFolder": "Root Folder", "RootFolder": "Root Folder",
"RootFolderCheckMultipleMessage": "Multiple root folders are missing: {0}",
"RootFolderCheckSingleMessage": "Missing root folder: {0}",
"RootFolderPath": "Root Folder Path", "RootFolderPath": "Root Folder Path",
"RootFolderPathHelpText": "Root Folder list items will be added to", "RootFolderPathHelpText": "Root Folder list items will be added to",
"RootFolders": "Root Folders", "RootFolders": "Root Folders",
@ -799,6 +839,7 @@
"SupportsSearchvalueWillBeUsedWhenAutomaticSearchesArePerformedViaTheUIOrByLidarr": "Will be used when automatic searches are performed via the UI or by Lidarr", "SupportsSearchvalueWillBeUsedWhenAutomaticSearchesArePerformedViaTheUIOrByLidarr": "Will be used when automatic searches are performed via the UI or by Lidarr",
"SupportsSearchvalueWillBeUsedWhenInteractiveSearchIsUsed": "Will be used when interactive search is used", "SupportsSearchvalueWillBeUsedWhenInteractiveSearchIsUsed": "Will be used when interactive search is used",
"System": "System", "System": "System",
"SystemTimeCheckMessage": "System time is off by more than 1 day. Scheduled tasks may not run correctly until the time is corrected",
"TBA": "TBA", "TBA": "TBA",
"TagAudioFilesWithMetadata": "Tag Audio Files with Metadata", "TagAudioFilesWithMetadata": "Tag Audio Files with Metadata",
"TagIsNotUsedAndCanBeDeleted": "Tag is not used and can be deleted", "TagIsNotUsedAndCanBeDeleted": "Tag is not used and can be deleted",
@ -897,6 +938,10 @@
"UnmonitoredOnly": "Unmonitored Only", "UnmonitoredOnly": "Unmonitored Only",
"UpdateAll": "Update all", "UpdateAll": "Update all",
"UpdateAutomaticallyHelpText": "Automatically download and install updates. You will still be able to install from System: Updates", "UpdateAutomaticallyHelpText": "Automatically download and install updates. You will still be able to install from System: Updates",
"UpdateAvailable": "New update is available",
"UpdateCheckStartupNotWritableMessage": "Cannot install update because startup folder '{0}' is not writable by the user '{1}'.",
"UpdateCheckStartupTranslocationMessage": "Cannot install update because startup folder '{0}' is in an App Translocation folder.",
"UpdateCheckUINotWritableMessage": "Cannot install update because UI folder '{0}' is not writable by the user '{1}'.",
"UpdateMechanismHelpText": "Use Lidarr's built-in updater or a script", "UpdateMechanismHelpText": "Use Lidarr's built-in updater or a script",
"UpdateScriptPathHelpText": "Path to a custom script that takes an extracted update package and handle the remainder of the update process", "UpdateScriptPathHelpText": "Path to a custom script that takes an extracted update package and handle the remainder of the update process",
"Updates": "Updates", "Updates": "Updates",

Loading…
Cancel
Save