diff --git a/src/NzbDrone.Core.Test/Download/DownloadClientTests/SabnzbdTests/SabnzbdFixture.cs b/src/NzbDrone.Core.Test/Download/DownloadClientTests/SabnzbdTests/SabnzbdFixture.cs index cf0f28b4f..f1fbc197c 100644 --- a/src/NzbDrone.Core.Test/Download/DownloadClientTests/SabnzbdTests/SabnzbdFixture.cs +++ b/src/NzbDrone.Core.Test/Download/DownloadClientTests/SabnzbdTests/SabnzbdFixture.cs @@ -579,5 +579,115 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.SabnzbdTests result.IsValid.Should().BeFalse(); } + + [Test] + public void should_remove_output_path_folder_when_deleting_a_completed_item_and_delete_data_is_true() + { + var path = @"C:\Test\Series.Title.S01E01".AsOsAgnostic(); + + Mocker.GetMock() + .Setup(s => s.FolderExists(path)) + .Returns(true); + + _completed.Items.First().Storage = path; + + GivenQueue(null); + GivenHistory(_completed); + + Subject.RemoveItem(_completed.Items.First().Id, true); + + Mocker.GetMock() + .Verify(v => v.DeleteFolder(path, true), Times.Once); + + Mocker.GetMock() + .Verify(v => v.DeleteFile(path), Times.Never); + } + + [Test] + public void should_remove_output_path_file_when_deleting_a_completed_item_and_delete_data_is_true() + { + var path = @"C:\Test\Series.Title.S01E01.mkv".AsOsAgnostic(); + + Mocker.GetMock() + .Setup(s => s.FolderExists(path)) + .Returns(false); + + Mocker.GetMock() + .Setup(s => s.FileExists(path)) + .Returns(true); + + _completed.Items.First().Storage = path; + + GivenQueue(null); + GivenHistory(_completed); + + Subject.RemoveItem(_completed.Items.First().Id, true); + + Mocker.GetMock() + .Verify(v => v.DeleteFolder(path, true), Times.Never); + + Mocker.GetMock() + .Verify(v => v.DeleteFile(path), Times.Once); + } + + [Test] + public void should_not_remove_output_path_file_when_deleting_a_completed_item_and_delete_data_is_true_if_it_does_not_exist() + { + var path = @"C:\Test\Series.Title.S01E01.mkv".AsOsAgnostic(); + + Mocker.GetMock() + .Setup(s => s.FolderExists(path)) + .Returns(false); + + Mocker.GetMock() + .Setup(s => s.FileExists(path)) + .Returns(false); + + _completed.Items.First().Storage = path; + + GivenQueue(null); + GivenHistory(_completed); + + Subject.RemoveItem(_completed.Items.First().Id, true); + + Mocker.GetMock() + .Verify(v => v.DeleteFolder(path, true), Times.Never); + + Mocker.GetMock() + .Verify(v => v.DeleteFile(path), Times.Never); + } + + [Test] + public void should_not_remove_output_path_file_when_deleting_a_completed_item_and_delete_data_is_false() + { + var path = @"C:\Test\Series.Title.S01E01.mkv".AsOsAgnostic(); + + Mocker.GetMock() + .Setup(s => s.FolderExists(path)) + .Returns(false); + + Mocker.GetMock() + .Setup(s => s.FileExists(path)) + .Returns(false); + + _completed.Items.First().Storage = path; + + GivenQueue(null); + GivenHistory(_completed); + + Subject.RemoveItem(_completed.Items.First().Id, false); + + Mocker.GetMock() + .Verify(v => v.FolderExists(path), Times.Never); + + Mocker.GetMock() + .Verify(v => v.FileExists(path), Times.Never); + + Mocker.GetMock() + .Verify(v => v.DeleteFolder(path, true), Times.Never); + + Mocker.GetMock() + .Verify(v => v.DeleteFile(path), Times.Never); + } } } diff --git a/src/NzbDrone.Core/Download/Clients/Sabnzbd/Sabnzbd.cs b/src/NzbDrone.Core/Download/Clients/Sabnzbd/Sabnzbd.cs index 46692a454..2be1c7887 100644 --- a/src/NzbDrone.Core/Download/Clients/Sabnzbd/Sabnzbd.cs +++ b/src/NzbDrone.Core/Download/Clients/Sabnzbd/Sabnzbd.cs @@ -5,6 +5,7 @@ using System.Text.RegularExpressions; using FluentValidation.Results; using NLog; using NzbDrone.Common.Disk; +using NzbDrone.Common.EnvironmentInfo; using NzbDrone.Common.Extensions; using NzbDrone.Common.Http; using NzbDrone.Core.Configuration; @@ -193,13 +194,45 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd public override void RemoveItem(string downloadId, bool deleteData) { - if (GetQueue().Any(v => v.DownloadId == downloadId)) + var historyItem = GetHistory().SingleOrDefault(v => v.DownloadId == downloadId); + + if (historyItem == null) { _proxy.RemoveFrom("queue", downloadId, deleteData, Settings); } else { _proxy.RemoveFrom("history", downloadId, deleteData, Settings); + + // Completed items in SAB's history do not remove the files from the file system when deleted, even if instructed to. + // If the output path is valid delete the file(s), otherwise warn that they cannot be deleted. + if (deleteData && historyItem.Status == DownloadItemStatus.Completed) + { + if (ValidatePath(historyItem)) + { + var outputPath = historyItem.OutputPath; + + try + { + if (_diskProvider.FolderExists(outputPath.FullPath)) + { + _diskProvider.DeleteFolder(outputPath.FullPath.ToString(), true); + } + else if (_diskProvider.FileExists(outputPath.FullPath)) + { + _diskProvider.DeleteFile(outputPath.FullPath.ToString()); + } + } + catch (Exception) + { + _logger.Error("Unable to delete output path: '{0}'. Delete file(s) manually", outputPath.FullPath); + } + } + else + { + _logger.Warn("Invalid path '{0}'. Delete file(s) manually"); + } + } } } @@ -494,5 +527,23 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd return categories.Contains(category); } + + private bool ValidatePath(DownloadClientItem downloadClientItem) + { + var downloadItemOutputPath = downloadClientItem.OutputPath; + + if (downloadItemOutputPath.IsEmpty) + { + return false; + } + + if ((OsInfo.IsWindows && !downloadItemOutputPath.IsWindowsPath) || + (OsInfo.IsNotWindows && !downloadItemOutputPath.IsUnixPath)) + { + return false; + } + + return true; + } } }