From d554e9ec83778d77481f6870e68882193e7793cc Mon Sep 17 00:00:00 2001 From: Mark McDowall Date: Thu, 25 Aug 2011 23:23:21 -0700 Subject: [PATCH] Delete existing files on import if equal or better quality otherwise skip importing. If the folder is not deleted after processing it is renamed so it will not be processed repeatedly. --- .../DiskScanProviderTest_ImportFile.cs | 185 +++++++++++++++++- NzbDrone.Core/Providers/DiskScanProvider.cs | 16 +- .../Providers/Jobs/PostDownloadScanJob.cs | 15 +- 3 files changed, 205 insertions(+), 11 deletions(-) diff --git a/NzbDrone.Core.Test/DiskScanProviderTest_ImportFile.cs b/NzbDrone.Core.Test/DiskScanProviderTest_ImportFile.cs index 3ea583061..848d02ffd 100644 --- a/NzbDrone.Core.Test/DiskScanProviderTest_ImportFile.cs +++ b/NzbDrone.Core.Test/DiskScanProviderTest_ImportFile.cs @@ -80,9 +80,6 @@ public void import_new_file_with_better_same_quality_should_succeed(QualityTypes VerifyFileImport(result, mocker, fakeEpisode, size); } - - - [TestCase("WEEDS.S03E01.DUAL.DVD.XviD.AC3.-HELLYWOOD.avi")] [TestCase("WEEDS.S03E01.DUAL.SDTV.XviD.AC3.-HELLYWOOD.avi")] public void import_new_file_episode_has_same_or_better_quality_should_skip(string fileName) @@ -116,7 +113,6 @@ public void import_new_file_episode_has_same_or_better_quality_should_skip(strin VerifySkipImport(result, mocker); } - [Test] public void import_unparsable_file_should_skip() { @@ -215,6 +211,186 @@ public void import_file_with_no_episode_in_db_should_skip() VerifySkipImport(result, mocker); } + [TestCase("WEEDS.S03E01.DUAL.DVD.XviD.AC3.-HELLYWOOD.avi")] + [TestCase("WEEDS.S03E01.DUAL.bluray.x264.AC3.-HELLYWOOD.mkv")] + public void import_new_file_episode_has_better_quality_than_existing(string fileName) + { + + //Fakes + var fakeSeries = Builder.CreateNew().Build(); + var fakeEpisode = Builder.CreateNew() + .With(c => c.EpisodeFile = Builder.CreateNew() + .With(e => e.Quality = QualityTypes.SDTV).Build() + ) + .Build(); + + //Mocks + var mocker = new AutoMoqer(); + + mocker.GetMock() + .Setup(e => e.GetSize(fileName)).Returns(12345).Verifiable(); + + mocker.GetMock() + .Setup(p => p.Exists(It.IsAny())) + .Returns(false); + + mocker.GetMock() + .Setup(e => e.GetEpisodesByParseResult(It.IsAny(), false)).Returns(new List { fakeEpisode }); + + //Act + var result = mocker.Resolve().ImportFile(fakeSeries, fileName); + + //Assert + VerifyFileImport(result, mocker, fakeEpisode, 12345); + mocker.GetMock().Verify(p => p.DeleteFile(It.IsAny()), Times.Once()); + } + + [TestCase("WEEDS.S03E01.DUAL.hdtv.XviD.AC3.-HELLYWOOD.avi")] + [TestCase("WEEDS.S03E01.DUAL.DVD.XviD.AC3.-HELLYWOOD.avi")] + [TestCase("WEEDS.S03E01.DUAL.bluray.x264.AC3.-HELLYWOOD.mkv")] + public void import_new_multi_part_file_episode_has_equal_or_better_quality_than_existing(string fileName) + { + //Fakes + var fakeSeries = Builder.CreateNew().Build(); + + var fakeEpisodes = Builder.CreateListOfSize(2) + .WhereAll() + .Have(e => e.EpisodeFile = Builder.CreateNew() + .With(f => f.Quality = QualityTypes.SDTV) + .Build()) + .Build(); + + //Mocks + var mocker = new AutoMoqer(); + + mocker.GetMock() + .Setup(e => e.GetSize(fileName)).Returns(12345).Verifiable(); + + mocker.GetMock() + .Setup(p => p.Exists(It.IsAny())) + .Returns(false); + + mocker.GetMock() + .Setup(e => e.GetEpisodesByParseResult(It.IsAny(), false)).Returns(fakeEpisodes); + + //Act + var result = mocker.Resolve().ImportFile(fakeSeries, fileName); + + //Assert + VerifyFileImport(result, mocker, fakeEpisodes[0], 12345); + mocker.GetMock().Verify(p => p.DeleteFile(It.IsAny()), Times.Once()); + } + + [TestCase("WEEDS.S03E01.DUAL.DVD.XviD.AC3.-HELLYWOOD.avi")] + [TestCase("WEEDS.S03E01.DUAL.HDTV.XviD.AC3.-HELLYWOOD.avi")] + public void skip_import_new_multi_part_file_episode_existing_has_better_quality(string fileName) + { + //Fakes + var fakeSeries = Builder.CreateNew().Build(); + + var fakeEpisodes = Builder.CreateListOfSize(2) + .WhereAll() + .Have(e => e.EpisodeFile = Builder.CreateNew() + .With(f => f.Quality = QualityTypes.Bluray720p) + .Build()) + .Build(); + + //Mocks + var mocker = new AutoMoqer(); + + mocker.GetMock() + .Setup(e => e.GetSize(fileName)).Returns(12345).Verifiable(); + + mocker.GetMock() + .Setup(p => p.Exists(It.IsAny())) + .Returns(false); + + mocker.GetMock() + .Setup(e => e.GetEpisodesByParseResult(It.IsAny(), false)).Returns(fakeEpisodes); + + //Act + var result = mocker.Resolve().ImportFile(fakeSeries, fileName); + + //Assert + VerifySkipImport(result, mocker); + } + + [Test] + public void import_new_multi_part_file_episode_replace_two_files() + { + const string fileName = "WEEDS.S03E01E02.DUAL.bluray.x264.AC3.-HELLYWOOD.mkv"; + + //Fakes + var fakeSeries = Builder.CreateNew().Build(); + + var fakeEpisodeFiles = Builder.CreateListOfSize(2) + .WhereAll() + .Have(e => e.Quality = QualityTypes.SDTV) + .Build(); + + var fakeEpisode1 = Builder.CreateNew() + .With(c => c.EpisodeFile = fakeEpisodeFiles[0]) + .Build(); + + var fakeEpisode2 = Builder.CreateNew() + .With(c => c.EpisodeFile = fakeEpisodeFiles[1]) + .Build(); + + //Mocks + var mocker = new AutoMoqer(); + + mocker.GetMock() + .Setup(e => e.GetSize(fileName)).Returns(12345).Verifiable(); + + mocker.GetMock() + .Setup(p => p.Exists(It.IsAny())) + .Returns(false); + + mocker.GetMock() + .Setup(e => e.GetEpisodesByParseResult(It.IsAny(), false)).Returns(new List { fakeEpisode1, fakeEpisode2 }); + + //Act + var result = mocker.Resolve().ImportFile(fakeSeries, fileName); + + //Assert + VerifyFileImport(result, mocker, fakeEpisode1, 12345); + mocker.GetMock().Verify(p => p.DeleteFile(It.IsAny()), Times.Exactly(2)); + } + + [Test] + public void import_new_episode_no_existing_episode_file() + { + const string fileName = "WEEDS.S03E01E02.DUAL.bluray.x264.AC3.-HELLYWOOD.mkv"; + + //Fakes + var fakeSeries = Builder.CreateNew().Build(); + + var fakeEpisode = Builder.CreateNew() + .With(e => e.EpisodeFileId = 0) + .With(e => e.EpisodeFile = null) + .Build(); + + //Mocks + var mocker = new AutoMoqer(); + + mocker.GetMock() + .Setup(e => e.GetSize(fileName)).Returns(12345).Verifiable(); + + mocker.GetMock() + .Setup(p => p.Exists(It.IsAny())) + .Returns(false); + + mocker.GetMock() + .Setup(e => e.GetEpisodesByParseResult(It.IsAny(), false)).Returns(new List { fakeEpisode}); + + //Act + var result = mocker.Resolve().ImportFile(fakeSeries, fileName); + + //Assert + VerifyFileImport(result, mocker, fakeEpisode, 12345); + mocker.GetMock().Verify(p => p.DeleteFile(It.IsAny()), Times.Never()); + } + private static void VerifyFileImport(EpisodeFile result, AutoMoqer mocker, Episode fakeEpisode, int size) { mocker.VerifyAllMocks(); @@ -236,6 +412,7 @@ private static void VerifySkipImport(EpisodeFile result, AutoMoqer mocker) result.Should().BeNull(); mocker.GetMock().Verify(p => p.Add(It.IsAny()), Times.Never()); mocker.GetMock().Verify(p => p.UpdateEpisode(It.IsAny()), Times.Never()); + mocker.GetMock().Verify(p => p.DeleteFile(It.IsAny()), Times.Never()); } } } diff --git a/NzbDrone.Core/Providers/DiskScanProvider.cs b/NzbDrone.Core/Providers/DiskScanProvider.cs index 49dd9bc8f..31737d3da 100644 --- a/NzbDrone.Core/Providers/DiskScanProvider.cs +++ b/NzbDrone.Core/Providers/DiskScanProvider.cs @@ -82,7 +82,6 @@ public virtual List Scan(Series series, string path) return importedFiles; } - public virtual EpisodeFile ImportFile(Series series, string filePath) { Logger.Trace("Importing file to database [{0}]", filePath); @@ -114,13 +113,22 @@ public virtual EpisodeFile ImportFile(Series series, string filePath) if (episodes.Count <= 0) { - Logger.Debug("Can't find any matching episodes in the database. skipping. {0}", filePath); + Logger.Debug("Can't find any matching episodes in the database. Skipping {0}", filePath); return null; } - if (episodes.Any(e => e.EpisodeFile != null && e.EpisodeFile.QualityWrapper > parseResult.Quality)) + //Make sure this file is an upgrade for ALL episodes already on disk + if (episodes.All(e => e.EpisodeFile == null || e.EpisodeFile.QualityWrapper <= parseResult.Quality)) { - Logger.Trace("File with better quality is already attached. skipping {0}", filePath); + Logger.Debug("Deleting the existing file(s) on disk to upgrade to: {0}", filePath); + //Do the delete for files where there is already an episode on disk + episodes.Where(e => e.EpisodeFile != null).Select(e => e.EpisodeFile.Path).Distinct().ToList().ForEach(p => _diskProvider.DeleteFile(p)); + } + + else + { + //Skip this file because its not an upgrade + Logger.Trace("This file isn't an upgrade for all episodes. Skipping {0}", filePath); return null; } diff --git a/NzbDrone.Core/Providers/Jobs/PostDownloadScanJob.cs b/NzbDrone.Core/Providers/Jobs/PostDownloadScanJob.cs index f57e7d2b7..0e10a3bea 100644 --- a/NzbDrone.Core/Providers/Jobs/PostDownloadScanJob.cs +++ b/NzbDrone.Core/Providers/Jobs/PostDownloadScanJob.cs @@ -73,6 +73,12 @@ public virtual void Start(ProgressNotification notification, int targetId, int s continue; } + if (subfolderInfo.Name.StartsWith("_NzbDrone_", StringComparison.CurrentCultureIgnoreCase)) + { + Logger.Debug("Folder [{0}] is marked as already processedby NzbDrone. skipping.", subfolder); + continue; + } + //Parse the Folder name var seriesName = Parser.ParseSeriesName(subfolderInfo.Name); var series = _seriesProvider.FindSeries(seriesName); @@ -88,10 +94,13 @@ public virtual void Start(ProgressNotification notification, int targetId, int s //Delete the folder only if folder is small enough if (_diskProvider.GetDirectorySize(subfolder) < 10.Megabytes()) - { _diskProvider.DeleteFolder(subfolder, true); - } + + //Otherwise rename the folder to say it was already processed once by NzbDrone so it will not be continually processed + else + _diskProvider.MoveDirectory(subfolderInfo.FullName, Path.Combine(subfolderInfo.Parent.FullName, "_NzbDrone_" + subfolderInfo.Name)); } + catch (Exception e) { Logger.ErrorException("An error has occurred while importing " + subfolder, e); @@ -99,4 +108,4 @@ public virtual void Start(ProgressNotification notification, int targetId, int s } } } -} +} \ No newline at end of file