diff --git a/src/NzbDrone.Core.Test/DecisionEngineTests/HistorySpecificationFixture.cs b/src/NzbDrone.Core.Test/DecisionEngineTests/HistorySpecificationFixture.cs index acff376d1..babdad589 100644 --- a/src/NzbDrone.Core.Test/DecisionEngineTests/HistorySpecificationFixture.cs +++ b/src/NzbDrone.Core.Test/DecisionEngineTests/HistorySpecificationFixture.cs @@ -4,6 +4,7 @@ using FizzWare.NBuilder; using FluentAssertions; using Moq; using NUnit.Framework; +using NzbDrone.Core.Configuration; using NzbDrone.Core.DecisionEngine.Specifications.RssSync; using NzbDrone.Core.History; using NzbDrone.Core.IndexerSearch.Definitions; @@ -63,6 +64,10 @@ namespace NzbDrone.Core.Test.DecisionEngineTests _upgradableQuality = new QualityModel(Quality.SDTV, new Revision(version: 1)); _notupgradableQuality = new QualityModel(Quality.HDTV1080p, new Revision(version: 2)); + + Mocker.GetMock() + .SetupGet(s => s.EnableCompletedDownloadHandling) + .Returns(true); } private void GivenMostRecentForEpisode(int episodeId, string downloadId, QualityModel quality, DateTime date, HistoryEventType eventType) @@ -71,6 +76,13 @@ namespace NzbDrone.Core.Test.DecisionEngineTests .Returns(new History.History { DownloadId = downloadId, Quality = quality, Date = date, EventType = eventType }); } + private void GivenCdhDisabled() + { + Mocker.GetMock() + .SetupGet(s => s.EnableCompletedDownloadHandling) + .Returns(false); + } + [Test] public void should_return_true_if_it_is_a_search() { @@ -92,9 +104,9 @@ namespace NzbDrone.Core.Test.DecisionEngineTests } [Test] - public void should_return_true_if_latest_history_has_a_download_id() + public void should_return_true_if_latest_history_has_a_download_id_and_cdh_is_enabled() { - GivenMostRecentForEpisode(FIRST_EPISODE_ID, "test", _notupgradableQuality, DateTime.UtcNow, HistoryEventType.Grabbed); + GivenMostRecentForEpisode(FIRST_EPISODE_ID, "test", _upgradableQuality, DateTime.UtcNow, HistoryEventType.Grabbed); _upgradeHistory.IsSatisfiedBy(_parseResultMulti, null).Accepted.Should().BeTrue(); } @@ -174,5 +186,34 @@ namespace NzbDrone.Core.Test.DecisionEngineTests GivenMostRecentForEpisode(FIRST_EPISODE_ID, string.Empty, _notupgradableQuality, DateTime.UtcNow.AddHours(-1), HistoryEventType.Grabbed); _upgradeHistory.IsSatisfiedBy(_parseResultMulti, null).Accepted.Should().BeFalse(); } + + [Test] + public void should_return_false_if_latest_history_has_a_download_id_and_cdh_is_disabled() + { + GivenCdhDisabled(); + GivenMostRecentForEpisode(FIRST_EPISODE_ID, "test", _upgradableQuality, DateTime.UtcNow.AddDays(-100), HistoryEventType.Grabbed); + _upgradeHistory.IsSatisfiedBy(_parseResultMulti, null).Accepted.Should().BeTrue(); + } + + [Test] + public void should_return_false_if_cutoff_already_met_and_cdh_is_disabled() + { + GivenCdhDisabled(); + _fakeSeries.Profile = new Profile { Cutoff = Quality.WEBDL1080p, Items = Qualities.QualityFixture.GetDefaultQualities() }; + _parseResultSingle.ParsedEpisodeInfo.Quality = new QualityModel(Quality.Bluray1080p, new Revision(version: 1)); + _upgradableQuality = new QualityModel(Quality.WEBDL1080p, new Revision(version: 1)); + + GivenMostRecentForEpisode(FIRST_EPISODE_ID, "test", _upgradableQuality, DateTime.UtcNow.AddDays(-100), HistoryEventType.Grabbed); + + _upgradeHistory.IsSatisfiedBy(_parseResultSingle, null).Accepted.Should().BeFalse(); + } + + [Test] + public void should_return_false_if_only_episode_is_not_upgradable_and_cdh_is_disabled() + { + GivenCdhDisabled(); + GivenMostRecentForEpisode(FIRST_EPISODE_ID, "test", _notupgradableQuality, DateTime.UtcNow.AddDays(-100), HistoryEventType.Grabbed); + _upgradeHistory.IsSatisfiedBy(_parseResultSingle, null).Accepted.Should().BeFalse(); + } } } diff --git a/src/NzbDrone.Core/DecisionEngine/Specifications/RssSync/HistorySpecification.cs b/src/NzbDrone.Core/DecisionEngine/Specifications/RssSync/HistorySpecification.cs index a78e73350..28e971d8b 100644 --- a/src/NzbDrone.Core/DecisionEngine/Specifications/RssSync/HistorySpecification.cs +++ b/src/NzbDrone.Core/DecisionEngine/Specifications/RssSync/HistorySpecification.cs @@ -1,6 +1,7 @@ using System; using NLog; using NzbDrone.Common.Extensions; +using NzbDrone.Core.Configuration; using NzbDrone.Core.History; using NzbDrone.Core.IndexerSearch.Definitions; using NzbDrone.Core.Parser.Model; @@ -11,14 +12,17 @@ namespace NzbDrone.Core.DecisionEngine.Specifications.RssSync { private readonly IHistoryService _historyService; private readonly QualityUpgradableSpecification _qualityUpgradableSpecification; + private readonly IConfigService _configService; private readonly Logger _logger; public HistorySpecification(IHistoryService historyService, QualityUpgradableSpecification qualityUpgradableSpecification, + IConfigService configService, Logger logger) { _historyService = historyService; _qualityUpgradableSpecification = qualityUpgradableSpecification; + _configService = configService; _logger = logger; } @@ -32,22 +36,43 @@ namespace NzbDrone.Core.DecisionEngine.Specifications.RssSync return Decision.Accept(); } + var cdhEnabled = _configService.EnableCompletedDownloadHandling; + _logger.Debug("Performing history status check on report"); foreach (var episode in subject.Episodes) { _logger.Debug("Checking current status of episode [{0}] in history", episode.Id); var mostRecent = _historyService.MostRecentForEpisode(episode.Id); - if (mostRecent != null && mostRecent.EventType == HistoryEventType.Grabbed && mostRecent.DownloadId.IsNullOrWhiteSpace() && mostRecent.Date.After(DateTime.UtcNow.AddHours(-12))) + if (mostRecent != null && mostRecent.EventType == HistoryEventType.Grabbed) { - if (!_qualityUpgradableSpecification.CutoffNotMet(subject.Series.Profile, mostRecent.Quality, subject.ParsedEpisodeInfo.Quality)) + var recentBlackhole = mostRecent.DownloadId.IsNullOrWhiteSpace() && mostRecent.Date.After(DateTime.UtcNow.AddHours(-12)); + var cutoffUnmet = _qualityUpgradableSpecification.CutoffNotMet(subject.Series.Profile, mostRecent.Quality, subject.ParsedEpisodeInfo.Quality); + var upgradeable = _qualityUpgradableSpecification.IsUpgradable(subject.Series.Profile, mostRecent.Quality, subject.ParsedEpisodeInfo.Quality); + + if (!recentBlackhole && cdhEnabled) { - return Decision.Reject("Recent grab event in history already meets cutoff: {0}", mostRecent.Quality); + continue; } - if (!_qualityUpgradableSpecification.IsUpgradable(subject.Series.Profile, mostRecent.Quality, subject.ParsedEpisodeInfo.Quality)) + if (!cutoffUnmet) { - return Decision.Reject("Recent grab event in history is of equal or higher quality: {0}", mostRecent.Quality); + if (recentBlackhole) + { + return Decision.Reject("Recent grab event in history already meets cutoff: {0}", mostRecent.Quality); + } + + return Decision.Reject("CDH is disabled and grab event in history already meets cutoff: {0}", mostRecent.Quality); + } + + if (!upgradeable) + { + if (recentBlackhole) + { + return Decision.Reject("Recent grab event in history is of equal or higher quality: {0}", mostRecent.Quality); + } + + return Decision.Reject("CDH is disabled and grab event in history is of equal or higher quality: {0}", mostRecent.Quality); } } }