diff --git a/src/NzbDrone.Core.Test/MediaFiles/EpisodeImport/Specifications/DifferentQualitySpecificationFixture.cs b/src/NzbDrone.Core.Test/MediaFiles/EpisodeImport/Specifications/DifferentQualitySpecificationFixture.cs new file mode 100644 index 000000000..9d79515bc --- /dev/null +++ b/src/NzbDrone.Core.Test/MediaFiles/EpisodeImport/Specifications/DifferentQualitySpecificationFixture.cs @@ -0,0 +1,106 @@ +using System.Collections.Generic; +using FizzWare.NBuilder; +using FluentAssertions; +using Marr.Data; +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.Profiles.Qualities; +using NzbDrone.Core.Qualities; +using NzbDrone.Core.Test.Framework; +using NzbDrone.Core.Tv; + +namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Specifications +{ + [TestFixture] + public class DifferentQualitySpecificationFixture : CoreTest + { + private LocalEpisode _localEpisode; + private DownloadClientItem _downloadClientItem; + + [SetUp] + public void Setup() + { + var qualityProfile = new QualityProfile + { + Cutoff = Quality.Bluray1080p.Id, + Items = Qualities.QualityFixture.GetDefaultQualities(Quality.DVD, Quality.HDTV720p, Quality.Bluray1080p) + }; + + var fakeSeries = Builder.CreateNew() + .With(c => c.QualityProfile = qualityProfile) + .Build(); + + _localEpisode = Builder.CreateNew() + .With(l => l.Quality = new QualityModel(Quality.Bluray1080p)) + .With(l => l.DownloadClientEpisodeInfo = new ParsedEpisodeInfo()) + .With(l => l.Series = fakeSeries) + .Build(); + + _downloadClientItem = Builder.CreateNew() + .Build(); + } + + private void GivenGrabbedEpisodeHistory(QualityModel quality) + { + var history = Builder.CreateListOfSize(1) + .TheFirst(1) + .With(h => h.Quality = quality) + .With(h => h.EventType = EpisodeHistoryEventType.Grabbed) + .BuildList(); + + Mocker.GetMock() + .Setup(s => s.FindByDownloadId(It.IsAny())) + .Returns(history); + } + + [Test] + public void should_be_accepted_if_no_download_client_item() + { + Subject.IsSatisfiedBy(_localEpisode, null).Accepted.Should().BeTrue(); + } + + [Test] + public void should_be_accepted_if_quality_does_not_match_for_full_season_pack() + { + GivenGrabbedEpisodeHistory(new QualityModel(Quality.SDTV)); + _localEpisode.DownloadClientEpisodeInfo.FullSeason = true; + + Subject.IsSatisfiedBy(_localEpisode, _downloadClientItem).Accepted.Should().BeTrue(); + } + + [Test] + public void should_be_accepted_if_no_grabbed_episode_history() + { + Mocker.GetMock() + .Setup(s => s.FindByDownloadId(It.IsAny())) + .Returns(new List()); + + _localEpisode.Episodes = Builder.CreateListOfSize(1) + .TheFirst(1) + .With(e => e.EpisodeFileId = 0) + .BuildList(); + + Subject.IsSatisfiedBy(_localEpisode, _downloadClientItem).Accepted.Should().BeTrue(); + } + + [Test] + public void should_be_accepted_if_quality_matches() + { + GivenGrabbedEpisodeHistory(_localEpisode.Quality); + + Subject.IsSatisfiedBy(_localEpisode, _downloadClientItem).Accepted.Should().BeTrue(); + } + + [Test] + public void should_be_rejected_if_quality_does_not_match() + { + GivenGrabbedEpisodeHistory(new QualityModel(Quality.SDTV)); + + Subject.IsSatisfiedBy(_localEpisode, _downloadClientItem).Accepted.Should().BeFalse(); + } + } +} \ No newline at end of file diff --git a/src/NzbDrone.Core.Test/MediaFiles/EpisodeImport/Specifications/SameFileSpecificationFixture.cs b/src/NzbDrone.Core.Test/MediaFiles/EpisodeImport/Specifications/SameFileSpecificationFixture.cs deleted file mode 100644 index 1c52b0740..000000000 --- a/src/NzbDrone.Core.Test/MediaFiles/EpisodeImport/Specifications/SameFileSpecificationFixture.cs +++ /dev/null @@ -1,110 +0,0 @@ -using System.Linq; -using FizzWare.NBuilder; -using FluentAssertions; -using Marr.Data; -using NUnit.Framework; -using NzbDrone.Core.Datastore; -using NzbDrone.Core.MediaFiles; -using NzbDrone.Core.MediaFiles.EpisodeImport.Specifications; -using NzbDrone.Core.Parser.Model; -using NzbDrone.Core.Test.Framework; -using NzbDrone.Core.Tv; - -namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Specifications -{ - [TestFixture] - public class SameFileSpecificationFixture : CoreTest - { - private LocalEpisode _localEpisode; - - [SetUp] - public void Setup() - { - _localEpisode = Builder.CreateNew() - .With(l => l.Size = 150.Megabytes()) - .Build(); - } - - [Test] - public void should_be_accepted_if_no_existing_file() - { - _localEpisode.Episodes = Builder.CreateListOfSize(1) - .TheFirst(1) - .With(e => e.EpisodeFileId = 0) - .BuildList(); - - Subject.IsSatisfiedBy(_localEpisode, null).Accepted.Should().BeTrue(); - } - - [Test] - public void should_be_accepted_if_multiple_existing_files() - { - _localEpisode.Episodes = Builder.CreateListOfSize(2) - .TheFirst(1) - .With(e => e.EpisodeFileId = 1) - .With(e => e.EpisodeFile = new LazyLoaded( - new EpisodeFile - { - Size = _localEpisode.Size - })) - .TheNext(1) - .With(e => e.EpisodeFileId = 2) - .With(e => e.EpisodeFile = new LazyLoaded( - new EpisodeFile - { - Size = _localEpisode.Size - })) - .Build() - .ToList(); - - Subject.IsSatisfiedBy(_localEpisode, null).Accepted.Should().BeTrue(); - } - - [Test] - public void should_be_accepted_if_file_size_is_different() - { - _localEpisode.Episodes = Builder.CreateListOfSize(1) - .TheFirst(1) - .With(e => e.EpisodeFileId = 1) - .With(e => e.EpisodeFile = new LazyLoaded( - new EpisodeFile - { - Size = _localEpisode.Size + 100.Megabytes() - })) - .Build() - .ToList(); - - Subject.IsSatisfiedBy(_localEpisode, null).Accepted.Should().BeTrue(); - } - - [Test] - public void should_be_reject_if_file_size_is_the_same() - { - _localEpisode.Episodes = Builder.CreateListOfSize(1) - .TheFirst(1) - .With(e => e.EpisodeFileId = 1) - .With(e => e.EpisodeFile = new LazyLoaded( - new EpisodeFile - { - Size = _localEpisode.Size - })) - .Build() - .ToList(); - - Subject.IsSatisfiedBy(_localEpisode, null).Accepted.Should().BeFalse(); - } - - [Test] - public void should_be_accepted_if_file_cannot_be_fetched() - { - _localEpisode.Episodes = Builder.CreateListOfSize(1) - .TheFirst(1) - .With(e => e.EpisodeFileId = 1) - .With(e => e.EpisodeFile = new LazyLoaded((EpisodeFile)null)) - .Build() - .ToList(); - - Subject.IsSatisfiedBy(_localEpisode, null).Accepted.Should().BeTrue(); - } - } -} \ No newline at end of file diff --git a/src/NzbDrone.Core/Download/IgnoredDownloadService.cs b/src/NzbDrone.Core/Download/IgnoredDownloadService.cs index b06b1637b..324c67c25 100644 --- a/src/NzbDrone.Core/Download/IgnoredDownloadService.cs +++ b/src/NzbDrone.Core/Download/IgnoredDownloadService.cs @@ -26,14 +26,15 @@ namespace NzbDrone.Core.Download public bool IgnoreDownload(TrackedDownload trackedDownload) { var series = trackedDownload.RemoteEpisode.Series; - var episodes = trackedDownload.RemoteEpisode.Episodes; - if (series == null || episodes.Empty()) + if (series == null) { - _logger.Warn("Unable to ignore download for unknown series/episode"); + _logger.Warn("Unable to ignore download for unknown series"); return false; } + var episodes = trackedDownload.RemoteEpisode.Episodes; + var downloadIgnoredEvent = new DownloadIgnoredEvent { SeriesId = series.Id, diff --git a/src/NzbDrone.Core/MediaFiles/EpisodeImport/Specifications/DifferentQualitySpecification.cs b/src/NzbDrone.Core/MediaFiles/EpisodeImport/Specifications/DifferentQualitySpecification.cs new file mode 100644 index 000000000..5e66e5288 --- /dev/null +++ b/src/NzbDrone.Core/MediaFiles/EpisodeImport/Specifications/DifferentQualitySpecification.cs @@ -0,0 +1,58 @@ +using System.Linq; +using NLog; +using NzbDrone.Core.DecisionEngine; +using NzbDrone.Core.Download; +using NzbDrone.Core.History; +using NzbDrone.Core.Parser.Model; +using NzbDrone.Core.Qualities; + +namespace NzbDrone.Core.MediaFiles.EpisodeImport.Specifications +{ + public class DifferentQualitySpecification : IImportDecisionEngineSpecification + { + private readonly IHistoryService _historyService; + private readonly Logger _logger; + + public DifferentQualitySpecification(IHistoryService historyService, Logger logger) + { + _historyService = historyService; + _logger = logger; + } + public Decision IsSatisfiedBy(LocalEpisode localEpisode, DownloadClientItem downloadClientItem) + { + if (downloadClientItem == null) + { + _logger.Debug("No download client item, skipping"); + return Decision.Accept(); + } + + if (localEpisode.DownloadClientEpisodeInfo?.FullSeason == true) + { + _logger.Debug("Full season download, skipping"); + return Decision.Accept(); + } + + var test = _historyService.FindByDownloadId(downloadClientItem.DownloadId); + var grabbedEpisodeHistory = _historyService.FindByDownloadId(downloadClientItem.DownloadId) + .OrderByDescending(h => h.Date) + .FirstOrDefault(h => h.EventType == EpisodeHistoryEventType.Grabbed); + + if (grabbedEpisodeHistory == null) + { + _logger.Debug("No grabbed history for this download item, skipping"); + return Decision.Accept(); + } + + var qualityComparer = new QualityModelComparer(localEpisode.Series.QualityProfile); + var qualityCompare = qualityComparer.Compare(localEpisode.Quality, grabbedEpisodeHistory.Quality); + + if (qualityCompare != 0) + { + _logger.Debug("Quality of file ({0}) does not match quality of grabbed history ({1})", localEpisode.Quality, grabbedEpisodeHistory.Quality); + return Decision.Reject("Not an upgrade for existing episode file(s)"); + } + + return Decision.Accept(); + } + } +} diff --git a/src/NzbDrone.Core/MediaFiles/EpisodeImport/Specifications/SameFileSpecification.cs b/src/NzbDrone.Core/MediaFiles/EpisodeImport/Specifications/SameFileSpecification.cs deleted file mode 100644 index 1aa54e804..000000000 --- a/src/NzbDrone.Core/MediaFiles/EpisodeImport/Specifications/SameFileSpecification.cs +++ /dev/null @@ -1,52 +0,0 @@ -using System.Linq; -using NLog; -using NzbDrone.Core.DecisionEngine; -using NzbDrone.Core.Download; -using NzbDrone.Core.Parser.Model; - -namespace NzbDrone.Core.MediaFiles.EpisodeImport.Specifications -{ - public class SameFileSpecification : IImportDecisionEngineSpecification - { - private readonly Logger _logger; - - public SameFileSpecification(Logger logger) - { - _logger = logger; - } - public Decision IsSatisfiedBy(LocalEpisode localEpisode, DownloadClientItem downloadClientItem) - { - var episodeFiles = localEpisode.Episodes.Where(e => e.EpisodeFileId != 0).Select(e => e.EpisodeFile).ToList(); - - if (episodeFiles.Count == 0) - { - _logger.Debug("No existing episode file, skipping"); - return Decision.Accept(); - } - - if (episodeFiles.Count > 1) - { - _logger.Debug("More than one existing episode file, skipping."); - return Decision.Accept(); - } - - var episodeFile = episodeFiles.First().Value; - - if (episodeFile == null) - { - var episode = localEpisode.Episodes.First(); - _logger.Trace("Unable to get episode file details from the DB. EpisodeId: {0} EpisodeFileId: {1}", episode.Id, episode.EpisodeFileId); - - return Decision.Accept(); - } - - if (episodeFiles.First().Value.Size == localEpisode.Size) - { - _logger.Debug("'{0}' Has the same filesize as existing file", localEpisode.Path); - return Decision.Reject("Has the same filesize as existing file"); - } - - return Decision.Accept(); - } - } -}