From b5bf5eae26ebe6b5a907a1ce05a0a73b11413c36 Mon Sep 17 00:00:00 2001 From: Mark McDowall Date: Wed, 15 Mar 2023 23:26:07 -0700 Subject: [PATCH] New: Don't import movies that don't match grab history Closes #8228 (cherry picked from commit 978618f041c478121f8e014910ad092f8e648596) --- .../MatchesGrabSpecificationFixture.cs | 103 ++++++++++++++++++ .../MatchesGrabSpecification.cs | 56 ++++++++++ 2 files changed, 159 insertions(+) create mode 100644 src/NzbDrone.Core.Test/MediaFiles/MovieImport/Specifications/MatchesGrabSpecificationFixture.cs create mode 100644 src/NzbDrone.Core/MediaFiles/MovieImport/Specifications/MatchesGrabSpecification.cs diff --git a/src/NzbDrone.Core.Test/MediaFiles/MovieImport/Specifications/MatchesGrabSpecificationFixture.cs b/src/NzbDrone.Core.Test/MediaFiles/MovieImport/Specifications/MatchesGrabSpecificationFixture.cs new file mode 100644 index 000000000..ad0c40332 --- /dev/null +++ b/src/NzbDrone.Core.Test/MediaFiles/MovieImport/Specifications/MatchesGrabSpecificationFixture.cs @@ -0,0 +1,103 @@ +using System.Collections.Generic; +using FizzWare.NBuilder; +using FluentAssertions; +using Moq; +using NUnit.Framework; +using NzbDrone.Core.Download; +using NzbDrone.Core.History; +using NzbDrone.Core.MediaFiles.MovieImport.Specifications; +using NzbDrone.Core.Movies; +using NzbDrone.Core.Parser.Model; +using NzbDrone.Core.Test.Framework; +using NzbDrone.Test.Common; + +namespace NzbDrone.Core.Test.MediaFiles.MovieImport.Specifications +{ + [TestFixture] + public class MatchesGrabSpecificationFixture : CoreTest + { + private Movie _movie1; + private Movie _movie2; + private Movie _movie3; + private LocalMovie _localMovie; + private DownloadClientItem _downloadClientItem; + + [SetUp] + public void Setup() + { + _movie1 = Builder.CreateNew() + .With(e => e.Id = 1) + .Build(); + + _movie2 = Builder.CreateNew() + .With(e => e.Id = 2) + .Build(); + + _movie3 = Builder.CreateNew() + .With(e => e.Id = 3) + .Build(); + + _localMovie = Builder.CreateNew() + .With(l => l.Path = @"C:\Test\Unsorted\Series.Title.S01E01.720p.HDTV-Sonarr\S01E05.mkv".AsOsAgnostic()) + .With(l => l.Movie = _movie1) + .Build(); + + _downloadClientItem = Builder.CreateNew().Build(); + } + + private void GivenHistoryForMovies(params Movie[] movies) + { + var history = new List(); + + foreach (var movie in movies) + { + history.Add(Builder.CreateNew() + .With(h => h.EventType = MovieHistoryEventType.Grabbed) + .With(h => h.MovieId = movie.Id) + .Build()); + } + + Mocker.GetMock() + .Setup(s => s.FindByDownloadId(It.IsAny())) + .Returns(history); + } + + [Test] + public void should_be_accepted_for_existing_file() + { + _localMovie.ExistingFile = true; + + Subject.IsSatisfiedBy(_localMovie, null).Accepted.Should().BeTrue(); + } + + [Test] + public void should_be_accepted_if_no_download_client_item() + { + Subject.IsSatisfiedBy(_localMovie, null).Accepted.Should().BeTrue(); + } + + [Test] + public void should_be_accepted_if_no_grab_history() + { + GivenHistoryForMovies(); + + Subject.IsSatisfiedBy(_localMovie, _downloadClientItem).Accepted.Should().BeTrue(); + } + + [Test] + public void should_be_accepted_if_file_episode_matches_single_grab_history() + { + GivenHistoryForMovies(_movie1); + + Subject.IsSatisfiedBy(_localMovie, _downloadClientItem).Accepted.Should().BeTrue(); + } + + [Test] + public void should_be_rejected_if_file_episode_does_not_match_single_grab_history() + { + GivenHistoryForMovies(_movie2); + + Subject.IsSatisfiedBy(_localMovie, _downloadClientItem).Accepted.Should().BeFalse(); + } + } +} diff --git a/src/NzbDrone.Core/MediaFiles/MovieImport/Specifications/MatchesGrabSpecification.cs b/src/NzbDrone.Core/MediaFiles/MovieImport/Specifications/MatchesGrabSpecification.cs new file mode 100644 index 000000000..1b689c035 --- /dev/null +++ b/src/NzbDrone.Core/MediaFiles/MovieImport/Specifications/MatchesGrabSpecification.cs @@ -0,0 +1,56 @@ +using System.Linq; +using NLog; +using NzbDrone.Common.Extensions; +using NzbDrone.Core.DecisionEngine; +using NzbDrone.Core.Download; +using NzbDrone.Core.History; +using NzbDrone.Core.Parser; +using NzbDrone.Core.Parser.Model; + +namespace NzbDrone.Core.MediaFiles.MovieImport.Specifications +{ + public class MatchesGrabSpecification : IImportDecisionEngineSpecification + { + private readonly Logger _logger; + private readonly IParsingService _parsingService; + private readonly IHistoryService _historyService; + + public MatchesGrabSpecification(IParsingService parsingService, IHistoryService historyService, Logger logger) + { + _logger = logger; + _parsingService = parsingService; + _historyService = historyService; + } + + public Decision IsSatisfiedBy(LocalMovie localMovie, DownloadClientItem downloadClientItem) + { + if (localMovie.ExistingFile) + { + return Decision.Accept(); + } + + if (downloadClientItem == null) + { + return Decision.Accept(); + } + + var grabbedHistory = _historyService.FindByDownloadId(downloadClientItem.DownloadId) + .Where(h => h.EventType == MovieHistoryEventType.Grabbed) + .ToList(); + + if (grabbedHistory.Empty()) + { + return Decision.Accept(); + } + + if (grabbedHistory.All(o => o.MovieId != localMovie.Movie.Id)) + { + _logger.Debug("Unexpected movie(s) in file: {0}", localMovie.Movie.ToString()); + + return Decision.Reject("Movie {0} was not found in the grabbed release: {1}", localMovie.Movie.ToString(), grabbedHistory.First().SourceTitle); + } + + return Decision.Accept(); + } + } +}