From 0274778679a8fd485a651eea9d293463528244fd Mon Sep 17 00:00:00 2001 From: Mark McDowall Date: Sat, 19 Aug 2023 21:49:17 -0700 Subject: [PATCH] Fixed: Don't reimport the same file from the same release unless grabbed again Closes #5625 --- .../AlreadyImportedSpecificationFixture.cs | 129 ++++++++++++++++++ .../AlreadyImportedSpecification.cs | 29 ++-- 2 files changed, 147 insertions(+), 11 deletions(-) create mode 100644 src/NzbDrone.Core.Test/MediaFiles/EpisodeImport/Specifications/AlreadyImportedSpecificationFixture.cs diff --git a/src/NzbDrone.Core.Test/MediaFiles/EpisodeImport/Specifications/AlreadyImportedSpecificationFixture.cs b/src/NzbDrone.Core.Test/MediaFiles/EpisodeImport/Specifications/AlreadyImportedSpecificationFixture.cs new file mode 100644 index 000000000..9e276d909 --- /dev/null +++ b/src/NzbDrone.Core.Test/MediaFiles/EpisodeImport/Specifications/AlreadyImportedSpecificationFixture.cs @@ -0,0 +1,129 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using FizzWare.NBuilder; +using FluentAssertions; +using Moq; +using NUnit.Framework; +using NzbDrone.Core.Download; +using NzbDrone.Core.History; +using NzbDrone.Core.MediaFiles.EpisodeImport.Specifications; +using NzbDrone.Core.Parser.Model; +using NzbDrone.Core.Test.Framework; +using NzbDrone.Core.Tv; +using NzbDrone.Test.Common; + +namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Specifications +{ + [TestFixture] + public class AlreadyImportedSpecificationFixture : CoreTest + { + private Series _series; + private Episode _episode; + private LocalEpisode _localEpisode; + private DownloadClientItem _downloadClientItem; + + [SetUp] + public void Setup() + { + _series = Builder.CreateNew() + .With(s => s.SeriesType = SeriesTypes.Standard) + .With(s => s.Path = @"C:\Test\TV\30 Rock".AsOsAgnostic()) + .Build(); + + _episode = Builder.CreateNew() + .With(e => e.SeasonNumber = 1) + .With(e => e.AirDateUtc = DateTime.UtcNow) + .Build(); + + _localEpisode = new LocalEpisode + { + Path = @"C:\Test\Unsorted\30 Rock\30.rock.s01e01.avi".AsOsAgnostic(), + Episodes = new List { _episode }, + Series = _series + }; + + _downloadClientItem = Builder.CreateNew() + .Build(); + } + + private void GivenHistory(List history) + { + Mocker.GetMock() + .Setup(s => s.FindByEpisodeId(It.IsAny())) + .Returns(history); + } + + [Test] + public void should_accepted_if_download_client_item_is_null() + { + Subject.IsSatisfiedBy(_localEpisode, null).Accepted.Should().BeTrue(); + } + + [Test] + public void should_accept_if_episode_does_not_have_file() + { + _episode.EpisodeFileId = 0; + + Subject.IsSatisfiedBy(_localEpisode, _downloadClientItem).Accepted.Should().BeTrue(); + } + + [Test] + public void should_accept_if_episode_has_not_been_imported() + { + var history = Builder.CreateListOfSize(1) + .All() + .With(h => h.EpisodeId = _episode.Id) + .With(h => h.EventType = EpisodeHistoryEventType.Grabbed) + .Build() + .ToList(); + + GivenHistory(history); + + Subject.IsSatisfiedBy(_localEpisode, _downloadClientItem).Accepted.Should().BeTrue(); + } + + [Test] + public void should_accept_if_episode_was_grabbed_after_being_imported() + { + var history = Builder.CreateListOfSize(3) + .All() + .With(h => h.EpisodeId = _episode.Id) + .TheFirst(1) + .With(h => h.EventType = EpisodeHistoryEventType.Grabbed) + .With(h => h.Date = DateTime.UtcNow) + .TheNext(1) + .With(h => h.EventType = EpisodeHistoryEventType.DownloadFolderImported) + .With(h => h.Date = DateTime.UtcNow.AddDays(-1)) + .TheNext(1) + .With(h => h.EventType = EpisodeHistoryEventType.Grabbed) + .With(h => h.Date = DateTime.UtcNow.AddDays(-2)) + .Build() + .ToList(); + + GivenHistory(history); + + Subject.IsSatisfiedBy(_localEpisode, _downloadClientItem).Accepted.Should().BeTrue(); + } + + [Test] + public void should_reject_if_episode_imported_after_being_grabbed() + { + var history = Builder.CreateListOfSize(2) + .All() + .With(h => h.EpisodeId = _episode.Id) + .TheFirst(1) + .With(h => h.EventType = EpisodeHistoryEventType.DownloadFolderImported) + .With(h => h.Date = DateTime.UtcNow.AddDays(-1)) + .TheNext(1) + .With(h => h.EventType = EpisodeHistoryEventType.Grabbed) + .With(h => h.Date = DateTime.UtcNow.AddDays(-2)) + .Build() + .ToList(); + + GivenHistory(history); + + Subject.IsSatisfiedBy(_localEpisode, _downloadClientItem).Accepted.Should().BeTrue(); + } + } +} diff --git a/src/NzbDrone.Core/MediaFiles/EpisodeImport/Specifications/AlreadyImportedSpecification.cs b/src/NzbDrone.Core/MediaFiles/EpisodeImport/Specifications/AlreadyImportedSpecification.cs index e1ec23a40..891cef89a 100644 --- a/src/NzbDrone.Core/MediaFiles/EpisodeImport/Specifications/AlreadyImportedSpecification.cs +++ b/src/NzbDrone.Core/MediaFiles/EpisodeImport/Specifications/AlreadyImportedSpecification.cs @@ -39,8 +39,11 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport.Specifications } var episodeHistory = _historyService.FindByEpisodeId(episode.Id); - var lastImported = episodeHistory.FirstOrDefault(h => h.EventType == EpisodeHistoryEventType.DownloadFolderImported); - var lastGrabbed = episodeHistory.FirstOrDefault(h => h.EventType == EpisodeHistoryEventType.Grabbed); + var lastImported = episodeHistory.FirstOrDefault(h => + h.DownloadId == downloadClientItem.DownloadId && + h.EventType == EpisodeHistoryEventType.DownloadFolderImported); + var lastGrabbed = episodeHistory.FirstOrDefault(h => + h.DownloadId == downloadClientItem.DownloadId && h.EventType == EpisodeHistoryEventType.Grabbed); if (lastImported == null) { @@ -48,17 +51,21 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport.Specifications continue; } - // If the release was grabbed again after importing don't reject it - if (lastGrabbed != null && lastGrabbed.Date.After(lastImported.Date)) + if (lastGrabbed != null) { - _logger.Trace("Episode file was grabbed again after importing"); - continue; - } + // If the release was grabbed again after importing don't reject it + if (lastGrabbed.Date.After(lastImported.Date)) + { + _logger.Trace("Episode file was grabbed again after importing"); + continue; + } - if (lastImported.DownloadId == downloadClientItem.DownloadId) - { - _logger.Debug("Episode file previously imported at {0}", lastImported.Date); - return Decision.Reject("Episode file already imported at {0}", lastImported.Date.ToLocalTime()); + // If the release was imported after the last grab reject it + if (lastImported.Date.After(lastGrabbed.Date)) + { + _logger.Debug("Episode file previously imported at {0}", lastImported.Date); + return Decision.Reject("Episode file already imported at {0}", lastImported.Date.ToLocalTime()); + } } }