From b638e09f242cff7f0e2910b96e8fb8182fedabca Mon Sep 17 00:00:00 2001 From: Mark McDowall Date: Wed, 3 Sep 2014 17:57:27 -0700 Subject: [PATCH] Fixed: Don't mark releases as imported unless at least one file is imported --- .../CompletedDownloadServiceFixture.cs | 67 ++++++++++++++++++- .../ImportDecisionMakerFixture.cs | 35 ++++++++-- .../Download/CompletedDownloadService.cs | 2 +- .../EpisodeImport/ImportDecisionMaker.cs | 15 ++++- .../Metadata/ExistingMetadataService.cs | 31 ++++++--- src/NzbDrone.Core/NzbDrone.Core.csproj | 1 + .../Parser/EpisodeNotFoundException.cs | 11 +++ src/NzbDrone.Core/Parser/ParsingService.cs | 4 +- 8 files changed, 143 insertions(+), 23 deletions(-) create mode 100644 src/NzbDrone.Core/Parser/EpisodeNotFoundException.cs diff --git a/src/NzbDrone.Core.Test/Download/CompletedDownloadServiceFixture.cs b/src/NzbDrone.Core.Test/Download/CompletedDownloadServiceFixture.cs index bac1c092d..2d841db69 100644 --- a/src/NzbDrone.Core.Test/Download/CompletedDownloadServiceFixture.cs +++ b/src/NzbDrone.Core.Test/Download/CompletedDownloadServiceFixture.cs @@ -449,7 +449,39 @@ namespace NzbDrone.Core.Test.Download } [Test] - public void should_not_mark_as_successful_if_no_files_were_imported() + public void should_not_mark_as_imported_if_all_files_were_rejected() + { + GivenCompletedDownloadClientHistory(); + + var history = Builder.CreateListOfSize(1) + .Build() + .ToList(); + + GivenGrabbedHistory(history); + GivenNoImportedHistory(); + + Mocker.GetMock() + .Setup(v => v.ProcessFolder(It.IsAny(), It.IsAny())) + .Returns(new List + { + new ImportResult( + new ImportDecision(new LocalEpisode() {Path = @"C:\TestPath\Droned.S01E01.mkv"}, "Rejected!"), + "Test Failure") + }); + + history.First().Data.Add("downloadClient", "SabnzbdClient"); + history.First().Data.Add("downloadClientId", _completed.First().DownloadClientId); + + Subject.Execute(new CheckForFinishedDownloadCommand()); + + Mocker.GetMock() + .Verify(c => c.DeleteFolder(It.IsAny(), true), Times.Never()); + + ExceptionVerification.ExpectedErrors(1); + } + + [Test] + public void should_not_mark_as_imported_if_all_files_were_skipped() { GivenCompletedDownloadClientHistory(); @@ -479,5 +511,38 @@ namespace NzbDrone.Core.Test.Download ExceptionVerification.ExpectedErrors(1); } + + [Test] + public void should_not_mark_as_imported_if_some_files_were_skipped() + { + GivenCompletedDownloadClientHistory(); + + var history = Builder.CreateListOfSize(1) + .Build() + .ToList(); + + GivenGrabbedHistory(history); + GivenNoImportedHistory(); + + Mocker.GetMock() + .Setup(v => v.ProcessFolder(It.IsAny(), It.IsAny())) + .Returns(new List + { + new ImportResult(new ImportDecision(new LocalEpisode() {Path = @"C:\TestPath\Droned.S01E01.mkv"})), + new ImportResult( + new ImportDecision(new LocalEpisode() {Path = @"C:\TestPath\Droned.S01E01.mkv"}), + "Test Failure") + }); + + history.First().Data.Add("downloadClient", "SabnzbdClient"); + history.First().Data.Add("downloadClientId", _completed.First().DownloadClientId); + + Subject.Execute(new CheckForFinishedDownloadCommand()); + + Mocker.GetMock() + .Verify(c => c.DeleteFolder(It.IsAny(), true), Times.Never()); + + ExceptionVerification.ExpectedErrors(1); + } } } diff --git a/src/NzbDrone.Core.Test/MediaFiles/EpisodeImport/ImportDecisionMakerFixture.cs b/src/NzbDrone.Core.Test/MediaFiles/EpisodeImport/ImportDecisionMakerFixture.cs index 8d4637aa2..6f887ad37 100644 --- a/src/NzbDrone.Core.Test/MediaFiles/EpisodeImport/ImportDecisionMakerFixture.cs +++ b/src/NzbDrone.Core.Test/MediaFiles/EpisodeImport/ImportDecisionMakerFixture.cs @@ -81,7 +81,6 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport .Setup(c => c.GetLocalEpisode(It.IsAny(), It.IsAny(), It.IsAny())) .Returns(_localEpisode); - Mocker.GetMock() .Setup(c => c.FilterExistingFiles(_videoFiles, It.IsAny())) .Returns(_videoFiles); @@ -147,12 +146,13 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport } [Test] - public void failed_parse_shouldnt_blowup_the_process() + public void should_not_blowup_the_process_due_to_failed_parse() { GivenSpecifications(_pass1); - Mocker.GetMock().Setup(c => c.GetLocalEpisode(It.IsAny(), It.IsAny(), It.IsAny())) - .Throws(); + Mocker.GetMock() + .Setup(c => c.GetLocalEpisode(It.IsAny(), It.IsAny(), It.IsAny())) + .Throws(); _videoFiles = new List { @@ -161,7 +161,6 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport "The.Office.S03E115.DVDRip.XviD-OSiTV" }; - Mocker.GetMock() .Setup(c => c.FilterExistingFiles(_videoFiles, It.IsAny())) .Returns(_videoFiles); @@ -206,5 +205,31 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport result.Single().LocalEpisode.Quality.Should().Be(expectedQuality); } + + [Test] + public void should_not_throw_if_episodes_are_not_found() + { + GivenSpecifications(_pass1); + + Mocker.GetMock() + .Setup(c => c.GetLocalEpisode(It.IsAny(), It.IsAny(), It.IsAny())) + .Throws(new EpisodeNotFoundException("Episode not found")); + + _videoFiles = new List + { + "The.Office.S03E115.DVDRip.XviD-OSiTV", + "The.Office.S03E115.DVDRip.XviD-OSiTV", + "The.Office.S03E115.DVDRip.XviD-OSiTV" + }; + + Mocker.GetMock() + .Setup(c => c.FilterExistingFiles(_videoFiles, It.IsAny())) + .Returns(_videoFiles); + + Subject.GetImportDecisions(_videoFiles, _series, false); + + Mocker.GetMock() + .Verify(c => c.GetLocalEpisode(It.IsAny(), It.IsAny(), It.IsAny()), Times.Exactly(_videoFiles.Count)); + } } } \ No newline at end of file diff --git a/src/NzbDrone.Core/Download/CompletedDownloadService.cs b/src/NzbDrone.Core/Download/CompletedDownloadService.cs index a838c10d7..6c774ea1e 100644 --- a/src/NzbDrone.Core/Download/CompletedDownloadService.cs +++ b/src/NzbDrone.Core/Download/CompletedDownloadService.cs @@ -187,7 +187,7 @@ namespace NzbDrone.Core.Download { UpdateStatusMessage(trackedDownload, LogLevel.Error, "No files found are eligible for import in {0}", trackedDownload.DownloadItem.OutputPath); } - else if (importResults.All(v => v.Result == ImportResultType.Imported || v.Result == ImportResultType.Rejected)) + else if (importResults.Any(v => v.Result == ImportResultType.Imported) && importResults.All(v => v.Result == ImportResultType.Imported || v.Result == ImportResultType.Rejected)) { UpdateStatusMessage(trackedDownload, LogLevel.Info, "Imported {0} files.", importResults.Count(v => v.Result == ImportResultType.Imported)); diff --git a/src/NzbDrone.Core/MediaFiles/EpisodeImport/ImportDecisionMaker.cs b/src/NzbDrone.Core/MediaFiles/EpisodeImport/ImportDecisionMaker.cs index 0806825a8..b6a29ed43 100644 --- a/src/NzbDrone.Core/MediaFiles/EpisodeImport/ImportDecisionMaker.cs +++ b/src/NzbDrone.Core/MediaFiles/EpisodeImport/ImportDecisionMaker.cs @@ -60,10 +60,12 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport try { var parsedEpisode = _parsingService.GetLocalEpisode(file, series, sceneSource); - + if (parsedEpisode != null) { - if (quality != null && new QualityModelComparer(parsedEpisode.Series.Profile).Compare(quality, parsedEpisode.Quality) > 0) + if (quality != null && + new QualityModelComparer(parsedEpisode.Series.Profile).Compare(quality, + parsedEpisode.Quality) > 0) { _logger.Debug("Using quality from folder: {0}", quality); parsedEpisode.Quality = quality; @@ -85,9 +87,16 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport decision = new ImportDecision(parsedEpisode, "Unable to parse file"); } } + catch (EpisodeNotFoundException e) + { + var parsedEpisode = new LocalEpisode(); + parsedEpisode.Path = file; + + decision = new ImportDecision(parsedEpisode, e.Message); + } catch (Exception e) { - _logger.ErrorException("Couldn't import file." + file, e); + _logger.ErrorException("Couldn't import file. " + file, e); } if (decision != null) diff --git a/src/NzbDrone.Core/Metadata/ExistingMetadataService.cs b/src/NzbDrone.Core/Metadata/ExistingMetadataService.cs index 00642823d..353f886a7 100644 --- a/src/NzbDrone.Core/Metadata/ExistingMetadataService.cs +++ b/src/NzbDrone.Core/Metadata/ExistingMetadataService.cs @@ -59,21 +59,30 @@ namespace NzbDrone.Core.Metadata if (metadata.Type == MetadataType.EpisodeImage || metadata.Type == MetadataType.EpisodeMetadata) { - var localEpisode = _parsingService.GetLocalEpisode(possibleMetadataFile, message.Series, false); + try + { + var localEpisode = _parsingService.GetLocalEpisode(possibleMetadataFile, message.Series, false); - if (localEpisode == null) + if (localEpisode == null) + { + _logger.Debug("Unable to parse meta data file: {0}", possibleMetadataFile); + break; + } + + if (localEpisode.Episodes.DistinctBy(e => e.EpisodeFileId).Count() > 1) + { + _logger.Debug("Metadata file: {0} does not match existing files.", possibleMetadataFile); + break; + } + + metadata.EpisodeFileId = localEpisode.Episodes.First().EpisodeFileId; + + } + catch (EpisodeNotFoundException e) { _logger.Debug("Cannot find related episodes for: {0}", possibleMetadataFile); - break; + continue; } - - if (localEpisode.Episodes.DistinctBy(e => e.EpisodeFileId).Count() > 1) - { - _logger.Debug("Metadata file: {0} does not match existing files.", possibleMetadataFile); - break; - } - - metadata.EpisodeFileId = localEpisode.Episodes.First().EpisodeFileId; } metadataFiles.Add(metadata); diff --git a/src/NzbDrone.Core/NzbDrone.Core.csproj b/src/NzbDrone.Core/NzbDrone.Core.csproj index c764fd0ac..5bc3166ab 100644 --- a/src/NzbDrone.Core/NzbDrone.Core.csproj +++ b/src/NzbDrone.Core/NzbDrone.Core.csproj @@ -652,6 +652,7 @@ + diff --git a/src/NzbDrone.Core/Parser/EpisodeNotFoundException.cs b/src/NzbDrone.Core/Parser/EpisodeNotFoundException.cs new file mode 100644 index 000000000..8f04af3e1 --- /dev/null +++ b/src/NzbDrone.Core/Parser/EpisodeNotFoundException.cs @@ -0,0 +1,11 @@ +using NzbDrone.Common.Exceptions; + +namespace NzbDrone.Core.Parser +{ + public class EpisodeNotFoundException : NzbDroneException + { + public EpisodeNotFoundException(string message, params object[] args) : base(message, args) + { + } + } +} diff --git a/src/NzbDrone.Core/Parser/ParsingService.cs b/src/NzbDrone.Core/Parser/ParsingService.cs index ebe85906d..d62e9e02e 100644 --- a/src/NzbDrone.Core/Parser/ParsingService.cs +++ b/src/NzbDrone.Core/Parser/ParsingService.cs @@ -62,10 +62,10 @@ namespace NzbDrone.Core.Parser var episodes = GetEpisodes(parsedEpisodeInfo, series, sceneSource); - if (!episodes.Any()) + if (episodes.Empty()) { _logger.Debug("No matching episodes found for: {0}", parsedEpisodeInfo); - return null; + throw new EpisodeNotFoundException("Unable to find episodes for file: {0}", parsedEpisodeInfo); } return new LocalEpisode