From 83370ddbbb43d27c14899dcbcfe8a2a2cce00fa1 Mon Sep 17 00:00:00 2001 From: Mark McDowall Date: Wed, 29 Mar 2017 06:44:50 -0700 Subject: [PATCH] New: Episode files sent to Recycling Bin are put into subfolders Closes #401 --- src/NzbDrone.Api/EpisodeFiles/EpisodeFileModule.cs | 8 +++++++- .../MediaFiles/UpgradeMediaFileServiceFixture.cs | 8 ++++---- .../RecycleBinProviderTests/DeleteFileFixture.cs | 12 ++++++++++++ src/NzbDrone.Core/Extras/Files/ExtraFileService.cs | 4 +++- src/NzbDrone.Core/MediaFiles/RecycleBinProvider.cs | 13 ++++++++----- .../MediaFiles/UpgradeMediaFileService.cs | 4 +++- 6 files changed, 37 insertions(+), 12 deletions(-) diff --git a/src/NzbDrone.Api/EpisodeFiles/EpisodeFileModule.cs b/src/NzbDrone.Api/EpisodeFiles/EpisodeFileModule.cs index 0271ae218..c2044bda3 100644 --- a/src/NzbDrone.Api/EpisodeFiles/EpisodeFileModule.cs +++ b/src/NzbDrone.Api/EpisodeFiles/EpisodeFileModule.cs @@ -2,6 +2,8 @@ using System.IO; using NLog; using NzbDrone.Api.REST; +using NzbDrone.Common.Disk; +using NzbDrone.Common.Extensions; using NzbDrone.Core.Datastore.Events; using NzbDrone.Core.MediaFiles; using NzbDrone.Core.MediaFiles.Events; @@ -16,6 +18,7 @@ public class EpisodeFileModule : NzbDroneRestModuleWithSignalR { private readonly IMediaFileService _mediaFileService; + private readonly IDiskProvider _diskProvider; private readonly IRecycleBinProvider _recycleBinProvider; private readonly ISeriesService _seriesService; private readonly IQualityUpgradableSpecification _qualityUpgradableSpecification; @@ -23,6 +26,7 @@ public class EpisodeFileModule : NzbDroneRestModuleWithSignalR().Verify(v => v.DeleteFile(It.IsAny()), Times.Once()); + Mocker.GetMock().Verify(v => v.DeleteFile(It.IsAny(), It.IsAny()), Times.Once()); } [Test] @@ -105,7 +105,7 @@ public void should_delete_the_same_episode_file_only_once() Subject.UpgradeEpisodeFile(_episodeFile, _localEpisode); - Mocker.GetMock().Verify(v => v.DeleteFile(It.IsAny()), Times.Once()); + Mocker.GetMock().Verify(v => v.DeleteFile(It.IsAny(), It.IsAny()), Times.Once()); } [Test] @@ -115,7 +115,7 @@ public void should_delete_multiple_different_episode_files() Subject.UpgradeEpisodeFile(_episodeFile, _localEpisode); - Mocker.GetMock().Verify(v => v.DeleteFile(It.IsAny()), Times.Exactly(2)); + Mocker.GetMock().Verify(v => v.DeleteFile(It.IsAny(), It.IsAny()), Times.Exactly(2)); } [Test] @@ -153,7 +153,7 @@ public void should_not_try_to_recyclebin_existing_file_if_file_doesnt_exist() Subject.UpgradeEpisodeFile(_episodeFile, _localEpisode); - Mocker.GetMock().Verify(v => v.DeleteFile(It.IsAny()), Times.Never()); + Mocker.GetMock().Verify(v => v.DeleteFile(It.IsAny(), It.IsAny()), Times.Never()); } [Test] diff --git a/src/NzbDrone.Core.Test/ProviderTests/RecycleBinProviderTests/DeleteFileFixture.cs b/src/NzbDrone.Core.Test/ProviderTests/RecycleBinProviderTests/DeleteFileFixture.cs index 3adb28208..4f5433a88 100644 --- a/src/NzbDrone.Core.Test/ProviderTests/RecycleBinProviderTests/DeleteFileFixture.cs +++ b/src/NzbDrone.Core.Test/ProviderTests/RecycleBinProviderTests/DeleteFileFixture.cs @@ -75,5 +75,17 @@ public void should_call_fileSetLastWriteTime_for_each_file() Mocker.GetMock().Verify(v => v.FileSetLastWriteTime(@"C:\Test\Recycle Bin\S01E01.avi".AsOsAgnostic(), It.IsAny()), Times.Once()); } + + [Test] + public void should_use_subfolder_when_passed_in() + { + WithRecycleBin(); + + var path = @"C:\Test\TV\30 Rock\S01E01.avi".AsOsAgnostic(); + + Mocker.Resolve().DeleteFile(path, "30 Rock"); + + Mocker.GetMock().Verify(v => v.TransferFile(path, @"C:\Test\Recycle Bin\30 Rock\S01E01.avi".AsOsAgnostic(), TransferMode.Move, false, true), Times.Once()); + } } } diff --git a/src/NzbDrone.Core/Extras/Files/ExtraFileService.cs b/src/NzbDrone.Core/Extras/Files/ExtraFileService.cs index 54d86e908..ac30f6536 100644 --- a/src/NzbDrone.Core/Extras/Files/ExtraFileService.cs +++ b/src/NzbDrone.Core/Extras/Files/ExtraFileService.cs @@ -4,6 +4,7 @@ using System.Linq; using NLog; using NzbDrone.Common.Disk; +using NzbDrone.Common.Extensions; using NzbDrone.Core.MediaFiles; using NzbDrone.Core.MediaFiles.Events; using NzbDrone.Core.Messaging.Events; @@ -129,7 +130,8 @@ public void HandleAsync(EpisodeFileDeletedEvent message) else { // Send extra files to the recycling bin so they can be recovered if necessary - _recycleBinProvider.DeleteFile(path); + var subfolder = _diskProvider.GetParentFolder(series.Path).GetRelativePath(_diskProvider.GetParentFolder(path)); + _recycleBinProvider.DeleteFile(path, subfolder); } } } diff --git a/src/NzbDrone.Core/MediaFiles/RecycleBinProvider.cs b/src/NzbDrone.Core/MediaFiles/RecycleBinProvider.cs index 520fbf676..540164a7c 100644 --- a/src/NzbDrone.Core/MediaFiles/RecycleBinProvider.cs +++ b/src/NzbDrone.Core/MediaFiles/RecycleBinProvider.cs @@ -15,7 +15,7 @@ namespace NzbDrone.Core.MediaFiles public interface IRecycleBinProvider { void DeleteFolder(string path); - void DeleteFile(string path); + void DeleteFile(string path, string subfolder = ""); void Empty(); void Cleanup(); } @@ -73,7 +73,7 @@ public void DeleteFolder(string path) } } - public void DeleteFile(string path) + public void DeleteFile(string path, string subfolder = "") { _logger.Debug("Attempting to send '{0}' to recycling bin", path); var recyclingBin = _configService.RecycleBin; @@ -94,7 +94,10 @@ public void DeleteFile(string path) else { var fileInfo = new FileInfo(path); - var destination = Path.Combine(recyclingBin, fileInfo.Name); + var destinationFolder = Path.Combine(recyclingBin, subfolder); + var destination = Path.Combine(destinationFolder, fileInfo.Name); + + _diskProvider.CreateFolder(destinationFolder); var index = 1; while (_diskProvider.FileExists(destination)) @@ -102,11 +105,11 @@ public void DeleteFile(string path) index++; if (fileInfo.Extension.IsNullOrWhiteSpace()) { - destination = Path.Combine(recyclingBin, fileInfo.Name + "_" + index); + destination = Path.Combine(destinationFolder, fileInfo.Name + "_" + index); } else { - destination = Path.Combine(recyclingBin, Path.GetFileNameWithoutExtension(fileInfo.Name) + "_" + index + fileInfo.Extension); + destination = Path.Combine(destinationFolder, Path.GetFileNameWithoutExtension(fileInfo.Name) + "_" + index + fileInfo.Extension); } } diff --git a/src/NzbDrone.Core/MediaFiles/UpgradeMediaFileService.cs b/src/NzbDrone.Core/MediaFiles/UpgradeMediaFileService.cs index 95f245e3e..d6c270d2c 100644 --- a/src/NzbDrone.Core/MediaFiles/UpgradeMediaFileService.cs +++ b/src/NzbDrone.Core/MediaFiles/UpgradeMediaFileService.cs @@ -2,6 +2,7 @@ using System.Linq; using NLog; using NzbDrone.Common.Disk; +using NzbDrone.Common.Extensions; using NzbDrone.Core.Parser.Model; namespace NzbDrone.Core.MediaFiles @@ -44,11 +45,12 @@ public EpisodeFileMoveResult UpgradeEpisodeFile(EpisodeFile episodeFile, LocalEp { var file = existingFile.First(); var episodeFilePath = Path.Combine(localEpisode.Series.Path, file.RelativePath); + var subfolder = _diskProvider.GetParentFolder(localEpisode.Series.Path).GetRelativePath(_diskProvider.GetParentFolder(episodeFilePath)); if (_diskProvider.FileExists(episodeFilePath)) { _logger.Debug("Removing existing episode file: {0}", file); - _recycleBinProvider.DeleteFile(episodeFilePath); + _recycleBinProvider.DeleteFile(episodeFilePath, subfolder); } moveFileResult.OldFiles.Add(file);