From c0d1d2c5020b119b06619940bfea3cb85d54f181 Mon Sep 17 00:00:00 2001 From: Mark McDowall Date: Tue, 17 Apr 2012 14:44:20 -0700 Subject: [PATCH] Fix: Successful downloads that are not moved properly should be retried. --- .../ProviderTests/DiskScanProviderTest.cs | 99 +++++++++++++++++++ .../ProviderTests/MediaFileProviderTest.cs | 35 +++++++ .../ProcessDownloadProviderFixture.cs | 1 + NzbDrone.Core/Providers/DiskScanProvider.cs | 27 +++++ NzbDrone.Core/Providers/MediaFileProvider.cs | 7 +- .../Providers/PostDownloadProvider.cs | 2 + 6 files changed, 170 insertions(+), 1 deletion(-) diff --git a/NzbDrone.Core.Test/ProviderTests/DiskScanProviderTest.cs b/NzbDrone.Core.Test/ProviderTests/DiskScanProviderTest.cs index 596425f60..3890079dc 100644 --- a/NzbDrone.Core.Test/ProviderTests/DiskScanProviderTest.cs +++ b/NzbDrone.Core.Test/ProviderTests/DiskScanProviderTest.cs @@ -212,5 +212,104 @@ namespace NzbDrone.Core.Test.ProviderTests //Assert result.Should().BeFalse(); } + + [Test] + public void CleanUpDropFolder_should_do_nothing_if_no_files_are_found() + { + //Setup + var folder = @"C:\Test\DropDir\The Office"; + + Mocker.GetMock().Setup(s => s.GetFiles(folder, SearchOption.AllDirectories)) + .Returns(new string[0]); + + //Act + Mocker.Resolve().CleanUpDropFolder(folder); + + //Assert + Mocker.GetMock().Verify(v => v.GetFileByPath(It.IsAny()), Times.Never()); + } + + [Test] + public void CleanUpDropFolder_should_do_nothing_if_no_conflicting_files_are_found() + { + //Setup + var folder = @"C:\Test\DropDir\The Office"; + var filename = Path.Combine(folder, "NotAProblem.avi"); + + var episodeFile = Builder.CreateNew() + .With(f => f.Path = filename.NormalizePath()) + .With(f => f.SeriesId = 12345) + .Build(); + + Mocker.GetMock().Setup(s => s.GetFiles(folder, SearchOption.AllDirectories)) + .Returns(new string[] { filename }); + + Mocker.GetMock().Setup(s => s.GetFileByPath(filename)) + .Returns(() => null); + + //Act + Mocker.Resolve().CleanUpDropFolder(folder); + + //Assert + Mocker.GetMock().Verify(v => v.GetFileByPath(filename), Times.Once()); + Mocker.GetMock().Verify(v => v.GetSeries(It.IsAny()), Times.Never()); + } + + [Test] + public void CleanUpDropFolder_should_move_file_if_a_conflict_is_found() + { + //Setup + var folder = @"C:\Test\DropDir\The Office"; + var filename = Path.Combine(folder, "Problem.avi"); + var seriesId = 12345; + var newFilename = "S01E01 - Title"; + var newFilePath = @"C:\Test\TV\The Office\Season 01\S01E01 - Title.avi"; + + var episodeFile = Builder.CreateNew() + .With(f => f.Path = filename.NormalizePath()) + .With(f => f.SeriesId = seriesId) + .Build(); + + var series = Builder.CreateNew() + .With(s => s.SeriesId = seriesId) + .With(s => s.Title = "The Office") + .Build(); + + var episode = Builder.CreateListOfSize(1) + .All() + .With(e => e.SeriesId = seriesId) + .With(e => e.EpisodeFileId = episodeFile.EpisodeFileId) + .Build(); + + Mocker.GetMock().Setup(v => v.GetFileByPath(filename)) + .Returns(() => null); + + Mocker.GetMock().Setup(s => s.GetFiles(folder, SearchOption.AllDirectories)) + .Returns(new string[] { filename }); + + Mocker.GetMock().Setup(s => s.GetFileByPath(filename)) + .Returns(episodeFile); + + Mocker.GetMock().Setup(s => s.GetSeries(It.IsAny())) + .Returns(series); + + Mocker.GetMock().Setup(s => s.GetEpisodesByFileId(episodeFile.EpisodeFileId)) + .Returns(episode); + + Mocker.GetMock().Setup(s => s.GetNewFilename(It.IsAny>(), series.Title, QualityTypes.Unknown, false)) + .Returns(newFilename); + + Mocker.GetMock().Setup(s => s.CalculateFilePath(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .Returns(new FileInfo(newFilePath)); + + Mocker.GetMock().Setup(s => s.MoveFile(episodeFile.Path, newFilePath)); + + //Act + Mocker.Resolve().CleanUpDropFolder(folder); + + //Assert + Mocker.GetMock().Verify(v => v.GetFileByPath(filename), Times.Once()); + Mocker.GetMock().Verify(v => v.MoveFile(filename.NormalizePath(), newFilePath), Times.Once()); + } } } diff --git a/NzbDrone.Core.Test/ProviderTests/MediaFileProviderTest.cs b/NzbDrone.Core.Test/ProviderTests/MediaFileProviderTest.cs index 623badbe8..1a746bd27 100644 --- a/NzbDrone.Core.Test/ProviderTests/MediaFileProviderTest.cs +++ b/NzbDrone.Core.Test/ProviderTests/MediaFileProviderTest.cs @@ -158,5 +158,40 @@ namespace NzbDrone.Core.Test.ProviderTests result.Should().HaveCount(9); result.Should().NotContain(e => e.EpisodeFileId == 1); } + + [Test] + public void GetFileByPath_should_return_null_if_file_does_not_exist_in_database() + { + //Setup + WithRealDb(); + + //Act + var result = Mocker.Resolve().GetFileByPath(@"C:\Test\EpisodeFile.avi"); + + //Resolve + result.Should().BeNull(); + } + + [Test] + public void GetFileByPath_should_return_EpisodeFile_if_file_exists_in_database() + { + var path = @"C:\Test\EpisodeFile.avi"; + + //Setup + WithRealDb(); + var episodeFile = Builder.CreateNew() + .With(f => f.Path = path.NormalizePath()) + .Build(); + + var episodeFileId = Convert.ToInt32(Db.Insert(episodeFile)); + + //Act + var result = Mocker.Resolve().GetFileByPath(path); + + //Resolve + result.Should().NotBeNull(); + result.Path.Should().Be(path.NormalizePath()); + result.EpisodeFileId.Should().Be(episodeFileId); + } } } \ No newline at end of file diff --git a/NzbDrone.Core.Test/ProviderTests/PostDownloadProviderTests/ProcessDownloadProviderFixture.cs b/NzbDrone.Core.Test/ProviderTests/PostDownloadProviderTests/ProcessDownloadProviderFixture.cs index eb4e979d4..9ce2a76dd 100644 --- a/NzbDrone.Core.Test/ProviderTests/PostDownloadProviderTests/ProcessDownloadProviderFixture.cs +++ b/NzbDrone.Core.Test/ProviderTests/PostDownloadProviderTests/ProcessDownloadProviderFixture.cs @@ -279,6 +279,7 @@ namespace NzbDrone.Core.Test.ProviderTests.PostDownloadProviderTests .Build().ToList(); Mocker.GetMock().Setup(s => s.FindSeries("office")).Returns(fakeSeries); + Mocker.GetMock().Setup(s => s.CleanUpDropFolder(droppedFolder.FullName)); Mocker.GetMock().Setup(s => s.Scan(fakeSeries, droppedFolder.FullName)).Returns(fakeEpisodeFiles); Mocker.GetMock().Setup(s => s.MoveEpisodeFile(It.IsAny(), true)).Returns(true); Mocker.GetMock().Setup(s => s.GetDirectorySize(droppedFolder.FullName)).Returns(Constants.IgnoreFileSize - 1.Megabytes()); diff --git a/NzbDrone.Core/Providers/DiskScanProvider.cs b/NzbDrone.Core/Providers/DiskScanProvider.cs index 91e77a874..54e661223 100644 --- a/NzbDrone.Core/Providers/DiskScanProvider.cs +++ b/NzbDrone.Core/Providers/DiskScanProvider.cs @@ -253,6 +253,33 @@ namespace NzbDrone.Core.Providers } } + public virtual void CleanUpDropFolder(string path) + { + //Todo: We should rename files before importing them to prevent this issue from ever happening + + var filesOnDisk = GetVideoFiles(path); + + foreach(var file in filesOnDisk) + { + try + { + var episodeFile = _mediaFileProvider.GetFileByPath(file); + + if (episodeFile != null) + { + Logger.Trace("[{0}] was imported but not moved, moving it now", file); + + MoveEpisodeFile(episodeFile); + } + + } + catch (Exception ex) + { + Logger.WarnException("Failed to move epiosde file from drop folder: " + file, ex); + throw; + } + } + } private List GetVideoFiles(string path) { diff --git a/NzbDrone.Core/Providers/MediaFileProvider.cs b/NzbDrone.Core/Providers/MediaFileProvider.cs index a438d0893..a45b7f9fe 100644 --- a/NzbDrone.Core/Providers/MediaFileProvider.cs +++ b/NzbDrone.Core/Providers/MediaFileProvider.cs @@ -52,6 +52,11 @@ namespace NzbDrone.Core.Providers return _database.Exists("WHERE Path =@0", path.NormalizePath()); } + public virtual EpisodeFile GetFileByPath(string path) + { + return _database.SingleOrDefault("WHERE Path =@0", path.NormalizePath()); + } + public virtual EpisodeFile GetEpisodeFile(int episodeFileId) { return _database.Single(episodeFileId); @@ -133,7 +138,7 @@ namespace NzbDrone.Core.Providers if (updated > 0) { - Logger.Debug("Removed {0} orphan file(s) from database.S", updated); + Logger.Debug("Removed {0} orphan file(s) from database.", updated); } } diff --git a/NzbDrone.Core/Providers/PostDownloadProvider.cs b/NzbDrone.Core/Providers/PostDownloadProvider.cs index fba0e89c7..9bd196b7b 100644 --- a/NzbDrone.Core/Providers/PostDownloadProvider.cs +++ b/NzbDrone.Core/Providers/PostDownloadProvider.cs @@ -66,6 +66,8 @@ namespace NzbDrone.Core.Providers return; } + _diskScanProvider.CleanUpDropFolder(subfolderInfo.FullName); + var importedFiles = _diskScanProvider.Scan(series, subfolderInfo.FullName); importedFiles.ForEach(file => _diskScanProvider.MoveEpisodeFile(file, true));