diff --git a/src/NzbDrone.Core/HealthCheck/Checks/RecyclingBinCheck.cs b/src/NzbDrone.Core/HealthCheck/Checks/RecyclingBinCheck.cs new file mode 100644 index 000000000..02f48855a --- /dev/null +++ b/src/NzbDrone.Core/HealthCheck/Checks/RecyclingBinCheck.cs @@ -0,0 +1,41 @@ +using NzbDrone.Common.Disk; +using NzbDrone.Common.Extensions; +using NzbDrone.Core.Configuration; +using NzbDrone.Core.Localization; +using NzbDrone.Core.MediaFiles.Events; + +namespace NzbDrone.Core.HealthCheck.Checks +{ + [CheckOn(typeof(TrackImportedEvent), CheckOnCondition.FailedOnly)] + [CheckOn(typeof(TrackImportFailedEvent), CheckOnCondition.SuccessfulOnly)] + public class RecyclingBinCheck : HealthCheckBase + { + private readonly IConfigService _configService; + private readonly IDiskProvider _diskProvider; + private readonly ILocalizationService _localizationService; + + public RecyclingBinCheck(IConfigService configService, IDiskProvider diskProvider, ILocalizationService localizationService) + { + _configService = configService; + _diskProvider = diskProvider; + _localizationService = localizationService; + } + + public override HealthCheck Check() + { + var recycleBin = _configService.RecycleBin; + + if (recycleBin.IsNullOrWhiteSpace()) + { + return new HealthCheck(GetType()); + } + + if (!_diskProvider.FolderWritable(recycleBin)) + { + return new HealthCheck(GetType(), HealthCheckResult.Error, string.Format(_localizationService.GetLocalizedString("RecycleBinUnableToWriteHealthCheck"), recycleBin), "#cannot-write-recycle-bin"); + } + + return new HealthCheck(GetType()); + } + } +} diff --git a/src/NzbDrone.Core/Localization/Core/en.json b/src/NzbDrone.Core/Localization/Core/en.json index 9cc512733..484113ed4 100644 --- a/src/NzbDrone.Core/Localization/Core/en.json +++ b/src/NzbDrone.Core/Localization/Core/en.json @@ -623,6 +623,7 @@ "RecycleBinCleanupDaysHelpText": "Set to 0 to disable automatic cleanup", "RecycleBinCleanupDaysHelpTextWarning": "Files in the recycle bin older than the selected number of days will be cleaned up automatically", "RecycleBinHelpText": "Track files will go here when deleted instead of being permanently deleted", + "RecycleBinUnableToWriteHealthCheck": "Unable to write to configured recycling bin folder: {0}. Ensure this path exists and is writable by the user running Lidarr", "RecyclingBin": "Recycling Bin", "RecyclingBinCleanup": "Recycling Bin Cleanup", "Redownload": "Redownload", @@ -926,4 +927,4 @@ "WriteMetadataToAudioFiles": "Write Metadata to Audio Files", "Year": "Year", "YesCancel": "Yes, Cancel" -} \ No newline at end of file +} diff --git a/src/NzbDrone.Core/MediaFiles/RecycleBinProvider.cs b/src/NzbDrone.Core/MediaFiles/RecycleBinProvider.cs index 2c3a29cf6..9e8b4320d 100644 --- a/src/NzbDrone.Core/MediaFiles/RecycleBinProvider.cs +++ b/src/NzbDrone.Core/MediaFiles/RecycleBinProvider.cs @@ -6,6 +6,7 @@ using NzbDrone.Common.EnvironmentInfo; using NzbDrone.Common.Extensions; using NzbDrone.Core.Configuration; using NzbDrone.Core.MediaFiles.Commands; +using NzbDrone.Core.MediaFiles.TrackImport; using NzbDrone.Core.Messaging.Commands; namespace NzbDrone.Core.MediaFiles @@ -96,7 +97,7 @@ namespace NzbDrone.Core.MediaFiles catch (IOException e) { _logger.Error(e, "Unable to create the folder '{0}' in the recycling bin for the file '{1}'", destinationFolder, fileInfo.Name); - throw; + throw new RecycleBinException($"Unable to create the folder '{destinationFolder}' in the recycling bin for the file '{fileInfo.Name}'", e); } var index = 1; @@ -121,7 +122,7 @@ namespace NzbDrone.Core.MediaFiles catch (IOException e) { _logger.Error(e, "Unable to move '{0}' to the recycling bin: '{1}'", path, destination); - throw; + throw new RecycleBinException($"Unable to move '{path}' to the recycling bin: '{destination}'", e); } SetLastWriteTime(destination, DateTime.UtcNow); diff --git a/src/NzbDrone.Core/MediaFiles/TrackImport/ImportApprovedTracks.cs b/src/NzbDrone.Core/MediaFiles/TrackImport/ImportApprovedTracks.cs index db9627ef3..7988edda8 100644 --- a/src/NzbDrone.Core/MediaFiles/TrackImport/ImportApprovedTracks.cs +++ b/src/NzbDrone.Core/MediaFiles/TrackImport/ImportApprovedTracks.cs @@ -266,6 +266,13 @@ namespace NzbDrone.Core.MediaFiles.TrackImport importResults.Add(new ImportResult(importDecision, "Failed to import track, Permissions error")); } + catch (RecycleBinException e) + { + _logger.Warn(e, "Couldn't import track " + localTrack); + _eventAggregator.PublishEvent(new TrackImportFailedEvent(e, localTrack, !localTrack.ExistingFile, downloadClientItem)); + + importResults.Add(new ImportResult(importDecision, "Failed to import track, unable to move existing file to the Recycle Bin.")); + } catch (Exception e) { _logger.Warn(e, "Couldn't import track " + localTrack); diff --git a/src/NzbDrone.Core/MediaFiles/TrackImport/RecycleBinException.cs b/src/NzbDrone.Core/MediaFiles/TrackImport/RecycleBinException.cs new file mode 100644 index 000000000..765ebeb25 --- /dev/null +++ b/src/NzbDrone.Core/MediaFiles/TrackImport/RecycleBinException.cs @@ -0,0 +1,28 @@ +using System; +using System.IO; +using System.Runtime.Serialization; + +namespace NzbDrone.Core.MediaFiles.TrackImport +{ + public class RecycleBinException : DirectoryNotFoundException + { + public RecycleBinException() + { + } + + public RecycleBinException(string message) + : base(message) + { + } + + public RecycleBinException(string message, Exception innerException) + : base(message, innerException) + { + } + + protected RecycleBinException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + } +}