diff --git a/src/NzbDrone.Core.Test/DecisionEngineTests/DownloadDecisionMakerFixture.cs b/src/NzbDrone.Core.Test/DecisionEngineTests/DownloadDecisionMakerFixture.cs index f195a2c99..f71a6e2d5 100644 --- a/src/NzbDrone.Core.Test/DecisionEngineTests/DownloadDecisionMakerFixture.cs +++ b/src/NzbDrone.Core.Test/DecisionEngineTests/DownloadDecisionMakerFixture.cs @@ -22,38 +22,38 @@ public class DownloadDecisionMakerFixture : CoreTest private List _reports; private RemoteEpisode _remoteEpisode; - private Mock _pass1; - private Mock _pass2; - private Mock _pass3; + private Mock _pass1; + private Mock _pass2; + private Mock _pass3; - private Mock _fail1; - private Mock _fail2; - private Mock _fail3; + private Mock _fail1; + private Mock _fail2; + private Mock _fail3; - private Mock _failDelayed1; + private Mock _failDelayed1; [SetUp] public void Setup() { - _pass1 = new Mock(); - _pass2 = new Mock(); - _pass3 = new Mock(); + _pass1 = new Mock(); + _pass2 = new Mock(); + _pass3 = new Mock(); - _fail1 = new Mock(); - _fail2 = new Mock(); - _fail3 = new Mock(); + _fail1 = new Mock(); + _fail2 = new Mock(); + _fail3 = new Mock(); - _failDelayed1 = new Mock(); + _failDelayed1 = new Mock(); - _pass1.Setup(c => c.IsSatisfiedBy(It.IsAny(), null)).Returns(Decision.Accept); - _pass2.Setup(c => c.IsSatisfiedBy(It.IsAny(), null)).Returns(Decision.Accept); - _pass3.Setup(c => c.IsSatisfiedBy(It.IsAny(), null)).Returns(Decision.Accept); + _pass1.Setup(c => c.IsSatisfiedBy(It.IsAny(), null)).Returns(DownloadSpecDecision.Accept); + _pass2.Setup(c => c.IsSatisfiedBy(It.IsAny(), null)).Returns(DownloadSpecDecision.Accept); + _pass3.Setup(c => c.IsSatisfiedBy(It.IsAny(), null)).Returns(DownloadSpecDecision.Accept); - _fail1.Setup(c => c.IsSatisfiedBy(It.IsAny(), null)).Returns(Decision.Reject("fail1")); - _fail2.Setup(c => c.IsSatisfiedBy(It.IsAny(), null)).Returns(Decision.Reject("fail2")); - _fail3.Setup(c => c.IsSatisfiedBy(It.IsAny(), null)).Returns(Decision.Reject("fail3")); + _fail1.Setup(c => c.IsSatisfiedBy(It.IsAny(), null)).Returns(DownloadSpecDecision.Reject(DownloadRejectionReason.Unknown, "fail1")); + _fail2.Setup(c => c.IsSatisfiedBy(It.IsAny(), null)).Returns(DownloadSpecDecision.Reject(DownloadRejectionReason.Unknown, "fail2")); + _fail3.Setup(c => c.IsSatisfiedBy(It.IsAny(), null)).Returns(DownloadSpecDecision.Reject(DownloadRejectionReason.Unknown, "fail3")); - _failDelayed1.Setup(c => c.IsSatisfiedBy(It.IsAny(), null)).Returns(Decision.Reject("failDelayed1")); + _failDelayed1.Setup(c => c.IsSatisfiedBy(It.IsAny(), null)).Returns(DownloadSpecDecision.Reject(DownloadRejectionReason.MinimumAgeDelay, "failDelayed1")); _failDelayed1.SetupGet(c => c.Priority).Returns(SpecificationPriority.Disk); _reports = new List { new ReleaseInfo { Title = "The.Office.S03E115.DVDRip.XviD-OSiTV" } }; @@ -68,9 +68,9 @@ public void Setup() .Returns(_remoteEpisode); } - private void GivenSpecifications(params Mock[] mocks) + private void GivenSpecifications(params Mock[] mocks) { - Mocker.SetConstant>(mocks.Select(c => c.Object)); + Mocker.SetConstant>(mocks.Select(c => c.Object)); } [Test] @@ -273,7 +273,7 @@ public void should_only_include_reports_for_requested_episodes() Episodes = episodes.Where(v => v.SceneEpisodeNumber == p.EpisodeNumbers.First()).ToList() }); - Mocker.SetConstant>(new List + Mocker.SetConstant>(new List { Mocker.Resolve() }); @@ -345,7 +345,7 @@ public void should_return_unknown_series_rejection_if_series_title_is_an_alias_f var result = Subject.GetRssDecision(_reports); result.Should().HaveCount(1); - result.First().Rejections.First().Reason.Should().Contain("12345"); + result.First().Rejections.First().Message.Should().Contain("12345"); } } } diff --git a/src/NzbDrone.Core.Test/Download/CompletedDownloadServiceTests/ImportFixture.cs b/src/NzbDrone.Core.Test/Download/CompletedDownloadServiceTests/ImportFixture.cs index 56d64eb81..0544d7246 100644 --- a/src/NzbDrone.Core.Test/Download/CompletedDownloadServiceTests/ImportFixture.cs +++ b/src/NzbDrone.Core.Test/Download/CompletedDownloadServiceTests/ImportFixture.cs @@ -4,7 +4,6 @@ using Moq; using NUnit.Framework; using NzbDrone.Common.Disk; -using NzbDrone.Core.DecisionEngine; using NzbDrone.Core.Download; using NzbDrone.Core.Download.TrackedDownloads; using NzbDrone.Core.History; @@ -122,11 +121,11 @@ public void should_not_mark_as_imported_if_all_files_were_rejected() { new ImportResult( new ImportDecision( - new LocalEpisode { Path = @"C:\TestPath\Droned.S01E01.mkv", Episodes = { _episode1 } }, new Rejection("Rejected!")), "Test Failure"), + new LocalEpisode { Path = @"C:\TestPath\Droned.S01E01.mkv", Episodes = { _episode1 } }, new ImportRejection(ImportRejectionReason.Unknown, "Rejected!")), "Test Failure"), new ImportResult( new ImportDecision( - new LocalEpisode { Path = @"C:\TestPath\Droned.S01E02.mkv", Episodes = { _episode2 } }, new Rejection("Rejected!")), "Test Failure") + new LocalEpisode { Path = @"C:\TestPath\Droned.S01E02.mkv", Episodes = { _episode2 } }, new ImportRejection(ImportRejectionReason.Unknown, "Rejected!")), "Test Failure") }); Subject.Import(_trackedDownload); @@ -146,11 +145,11 @@ public void should_not_mark_as_imported_if_no_episodes_were_parsed() { new ImportResult( new ImportDecision( - new LocalEpisode { Path = @"C:\TestPath\Droned.S01E01.mkv", Episodes = { _episode1 } }, new Rejection("Rejected!")), "Test Failure"), + new LocalEpisode { Path = @"C:\TestPath\Droned.S01E01.mkv", Episodes = { _episode1 } }, new ImportRejection(ImportRejectionReason.Unknown, "Rejected!")), "Test Failure"), new ImportResult( new ImportDecision( - new LocalEpisode { Path = @"C:\TestPath\Droned.S01E02.mkv", Episodes = { _episode2 } }, new Rejection("Rejected!")), "Test Failure") + new LocalEpisode { Path = @"C:\TestPath\Droned.S01E02.mkv", Episodes = { _episode2 } }, new ImportRejection(ImportRejectionReason.Unknown, "Rejected!")), "Test Failure") }); _trackedDownload.RemoteEpisode.Episodes.Clear(); diff --git a/src/NzbDrone.Core.Test/Download/DownloadApprovedReportsTests/DownloadApprovedFixture.cs b/src/NzbDrone.Core.Test/Download/DownloadApprovedReportsTests/DownloadApprovedFixture.cs index b158fda2e..7ac1affbc 100644 --- a/src/NzbDrone.Core.Test/Download/DownloadApprovedReportsTests/DownloadApprovedFixture.cs +++ b/src/NzbDrone.Core.Test/Download/DownloadApprovedReportsTests/DownloadApprovedFixture.cs @@ -186,8 +186,8 @@ public async Task should_not_add_to_downloaded_list_when_download_fails() public void should_return_an_empty_list_when_none_are_approved() { var decisions = new List(); - decisions.Add(new DownloadDecision(null, new Rejection("Failure!"))); - decisions.Add(new DownloadDecision(null, new Rejection("Failure!"))); + decisions.Add(new DownloadDecision(null, new DownloadRejection(DownloadRejectionReason.Unknown, "Failure!"))); + decisions.Add(new DownloadDecision(null, new DownloadRejection(DownloadRejectionReason.Unknown, "Failure!"))); Subject.GetQualifiedReports(decisions).Should().BeEmpty(); } @@ -199,7 +199,7 @@ public async Task should_not_grab_if_pending() var remoteEpisode = GetRemoteEpisode(episodes, new QualityModel(Quality.HDTV720p)); var decisions = new List(); - decisions.Add(new DownloadDecision(remoteEpisode, new Rejection("Failure!", RejectionType.Temporary))); + decisions.Add(new DownloadDecision(remoteEpisode, new DownloadRejection(DownloadRejectionReason.Unknown, "Failure!", RejectionType.Temporary))); await Subject.ProcessDecisions(decisions); Mocker.GetMock().Verify(v => v.DownloadReport(It.IsAny(), null), Times.Never()); @@ -213,7 +213,7 @@ public async Task should_not_add_to_pending_if_episode_was_grabbed() var decisions = new List(); decisions.Add(new DownloadDecision(remoteEpisode)); - decisions.Add(new DownloadDecision(remoteEpisode, new Rejection("Failure!", RejectionType.Temporary))); + decisions.Add(new DownloadDecision(remoteEpisode, new DownloadRejection(DownloadRejectionReason.Unknown, "Failure!", RejectionType.Temporary))); await Subject.ProcessDecisions(decisions); Mocker.GetMock().Verify(v => v.AddMany(It.IsAny>>()), Times.Never()); @@ -226,8 +226,8 @@ public async Task should_add_to_pending_even_if_already_added_to_pending() var remoteEpisode = GetRemoteEpisode(episodes, new QualityModel(Quality.HDTV720p)); var decisions = new List(); - decisions.Add(new DownloadDecision(remoteEpisode, new Rejection("Failure!", RejectionType.Temporary))); - decisions.Add(new DownloadDecision(remoteEpisode, new Rejection("Failure!", RejectionType.Temporary))); + decisions.Add(new DownloadDecision(remoteEpisode, new DownloadRejection(DownloadRejectionReason.Unknown, "Failure!", RejectionType.Temporary))); + decisions.Add(new DownloadDecision(remoteEpisode, new DownloadRejection(DownloadRejectionReason.Unknown, "Failure!", RejectionType.Temporary))); await Subject.ProcessDecisions(decisions); Mocker.GetMock().Verify(v => v.AddMany(It.IsAny>>()), Times.Once()); diff --git a/src/NzbDrone.Core.Test/Download/Pending/PendingReleaseServiceTests/AddFixture.cs b/src/NzbDrone.Core.Test/Download/Pending/PendingReleaseServiceTests/AddFixture.cs index bbf1a6bf8..8adcaf3cd 100644 --- a/src/NzbDrone.Core.Test/Download/Pending/PendingReleaseServiceTests/AddFixture.cs +++ b/src/NzbDrone.Core.Test/Download/Pending/PendingReleaseServiceTests/AddFixture.cs @@ -63,7 +63,7 @@ public void Setup() _remoteEpisode.ParsedEpisodeInfo = _parsedEpisodeInfo; _remoteEpisode.Release = _release; - _temporarilyRejected = new DownloadDecision(_remoteEpisode, new Rejection("Temp Rejected", RejectionType.Temporary)); + _temporarilyRejected = new DownloadDecision(_remoteEpisode, new DownloadRejection(DownloadRejectionReason.MinimumAgeDelay, "Temp Rejected", RejectionType.Temporary)); _heldReleases = new List(); diff --git a/src/NzbDrone.Core.Test/Download/Pending/PendingReleaseServiceTests/RemoveGrabbedFixture.cs b/src/NzbDrone.Core.Test/Download/Pending/PendingReleaseServiceTests/RemoveGrabbedFixture.cs index b1007418c..d7e806eb0 100644 --- a/src/NzbDrone.Core.Test/Download/Pending/PendingReleaseServiceTests/RemoveGrabbedFixture.cs +++ b/src/NzbDrone.Core.Test/Download/Pending/PendingReleaseServiceTests/RemoveGrabbedFixture.cs @@ -64,7 +64,7 @@ public void Setup() _remoteEpisode.ParsedEpisodeInfo = _parsedEpisodeInfo; _remoteEpisode.Release = _release; - _temporarilyRejected = new DownloadDecision(_remoteEpisode, new Rejection("Temp Rejected", RejectionType.Temporary)); + _temporarilyRejected = new DownloadDecision(_remoteEpisode, new DownloadRejection(DownloadRejectionReason.MinimumAgeDelay, "Temp Rejected", RejectionType.Temporary)); _heldReleases = new List(); diff --git a/src/NzbDrone.Core.Test/Download/Pending/PendingReleaseServiceTests/RemoveRejectedFixture.cs b/src/NzbDrone.Core.Test/Download/Pending/PendingReleaseServiceTests/RemoveRejectedFixture.cs index 07d3dcb0c..6a914208a 100644 --- a/src/NzbDrone.Core.Test/Download/Pending/PendingReleaseServiceTests/RemoveRejectedFixture.cs +++ b/src/NzbDrone.Core.Test/Download/Pending/PendingReleaseServiceTests/RemoveRejectedFixture.cs @@ -63,7 +63,7 @@ public void Setup() _remoteEpisode.ParsedEpisodeInfo = _parsedEpisodeInfo; _remoteEpisode.Release = _release; - _temporarilyRejected = new DownloadDecision(_remoteEpisode, new Rejection("Temp Rejected", RejectionType.Temporary)); + _temporarilyRejected = new DownloadDecision(_remoteEpisode, new DownloadRejection(DownloadRejectionReason.MinimumAgeDelay, "Temp Rejected", RejectionType.Temporary)); Mocker.GetMock() .Setup(s => s.All()) diff --git a/src/NzbDrone.Core.Test/MediaFiles/EpisodeImport/ImportApprovedEpisodesFixture.cs b/src/NzbDrone.Core.Test/MediaFiles/EpisodeImport/ImportApprovedEpisodesFixture.cs index 0561e765a..7ae77797b 100644 --- a/src/NzbDrone.Core.Test/MediaFiles/EpisodeImport/ImportApprovedEpisodesFixture.cs +++ b/src/NzbDrone.Core.Test/MediaFiles/EpisodeImport/ImportApprovedEpisodesFixture.cs @@ -7,7 +7,6 @@ using NUnit.Framework; using NzbDrone.Common.Disk; using NzbDrone.Common.Extensions; -using NzbDrone.Core.DecisionEngine; using NzbDrone.Core.Download; using NzbDrone.Core.History; using NzbDrone.Core.MediaFiles; @@ -47,9 +46,9 @@ public void Setup() var episodes = Builder.CreateListOfSize(5) .Build(); - _rejectedDecisions.Add(new ImportDecision(new LocalEpisode(), new Rejection("Rejected!"))); - _rejectedDecisions.Add(new ImportDecision(new LocalEpisode(), new Rejection("Rejected!"))); - _rejectedDecisions.Add(new ImportDecision(new LocalEpisode(), new Rejection("Rejected!"))); + _rejectedDecisions.Add(new ImportDecision(new LocalEpisode(), new ImportRejection(ImportRejectionReason.Unknown, "Rejected!"))); + _rejectedDecisions.Add(new ImportDecision(new LocalEpisode(), new ImportRejection(ImportRejectionReason.Unknown, "Rejected!"))); + _rejectedDecisions.Add(new ImportDecision(new LocalEpisode(), new ImportRejection(ImportRejectionReason.Unknown, "Rejected!"))); _rejectedDecisions.ForEach(r => r.LocalEpisode.FileEpisodeInfo = new ParsedEpisodeInfo()); foreach (var episode in episodes) diff --git a/src/NzbDrone.Core.Test/MediaFiles/EpisodeImport/ImportDecisionMakerFixture.cs b/src/NzbDrone.Core.Test/MediaFiles/EpisodeImport/ImportDecisionMakerFixture.cs index 7fddbd1fe..51d181abe 100644 --- a/src/NzbDrone.Core.Test/MediaFiles/EpisodeImport/ImportDecisionMakerFixture.cs +++ b/src/NzbDrone.Core.Test/MediaFiles/EpisodeImport/ImportDecisionMakerFixture.cs @@ -4,7 +4,6 @@ using FluentAssertions; using Moq; using NUnit.Framework; -using NzbDrone.Core.DecisionEngine; using NzbDrone.Core.Download; using NzbDrone.Core.Languages; using NzbDrone.Core.MediaFiles; @@ -46,13 +45,13 @@ public void Setup() _fail2 = new Mock(); _fail3 = new Mock(); - _pass1.Setup(c => c.IsSatisfiedBy(It.IsAny(), It.IsAny())).Returns(Decision.Accept()); - _pass2.Setup(c => c.IsSatisfiedBy(It.IsAny(), It.IsAny())).Returns(Decision.Accept()); - _pass3.Setup(c => c.IsSatisfiedBy(It.IsAny(), It.IsAny())).Returns(Decision.Accept()); + _pass1.Setup(c => c.IsSatisfiedBy(It.IsAny(), It.IsAny())).Returns(ImportSpecDecision.Accept()); + _pass2.Setup(c => c.IsSatisfiedBy(It.IsAny(), It.IsAny())).Returns(ImportSpecDecision.Accept()); + _pass3.Setup(c => c.IsSatisfiedBy(It.IsAny(), It.IsAny())).Returns(ImportSpecDecision.Accept()); - _fail1.Setup(c => c.IsSatisfiedBy(It.IsAny(), It.IsAny())).Returns(Decision.Reject("_fail1")); - _fail2.Setup(c => c.IsSatisfiedBy(It.IsAny(), It.IsAny())).Returns(Decision.Reject("_fail2")); - _fail3.Setup(c => c.IsSatisfiedBy(It.IsAny(), It.IsAny())).Returns(Decision.Reject("_fail3")); + _fail1.Setup(c => c.IsSatisfiedBy(It.IsAny(), It.IsAny())).Returns(ImportSpecDecision.Reject(ImportRejectionReason.Unknown, "_fail1")); + _fail2.Setup(c => c.IsSatisfiedBy(It.IsAny(), It.IsAny())).Returns(ImportSpecDecision.Reject(ImportRejectionReason.Unknown, "_fail2")); + _fail3.Setup(c => c.IsSatisfiedBy(It.IsAny(), It.IsAny())).Returns(ImportSpecDecision.Reject(ImportRejectionReason.Unknown, "_fail3")); _series = Builder.CreateNew() .With(e => e.Path = @"C:\Test\Series".AsOsAgnostic()) diff --git a/src/NzbDrone.Core/DecisionEngine/Decision.cs b/src/NzbDrone.Core/DecisionEngine/Decision.cs deleted file mode 100644 index 160e2599d..000000000 --- a/src/NzbDrone.Core/DecisionEngine/Decision.cs +++ /dev/null @@ -1,32 +0,0 @@ -namespace NzbDrone.Core.DecisionEngine -{ - public class Decision - { - public bool Accepted { get; private set; } - public string Reason { get; private set; } - - private static readonly Decision AcceptDecision = new Decision { Accepted = true }; - private Decision() - { - } - - public static Decision Accept() - { - return AcceptDecision; - } - - public static Decision Reject(string reason, params object[] args) - { - return Reject(string.Format(reason, args)); - } - - public static Decision Reject(string reason) - { - return new Decision - { - Accepted = false, - Reason = reason - }; - } - } -} diff --git a/src/NzbDrone.Core/DecisionEngine/DownloadDecision.cs b/src/NzbDrone.Core/DecisionEngine/DownloadDecision.cs index 01aa647cf..096a8e6c7 100644 --- a/src/NzbDrone.Core/DecisionEngine/DownloadDecision.cs +++ b/src/NzbDrone.Core/DecisionEngine/DownloadDecision.cs @@ -7,7 +7,7 @@ namespace NzbDrone.Core.DecisionEngine public class DownloadDecision { public RemoteEpisode RemoteEpisode { get; private set; } - public IEnumerable Rejections { get; private set; } + public IEnumerable Rejections { get; private set; } public bool Approved => !Rejections.Any(); @@ -27,7 +27,7 @@ public bool Rejected } } - public DownloadDecision(RemoteEpisode episode, params Rejection[] rejections) + public DownloadDecision(RemoteEpisode episode, params DownloadRejection[] rejections) { RemoteEpisode = episode; Rejections = rejections.ToList(); diff --git a/src/NzbDrone.Core/DecisionEngine/DownloadDecisionMaker.cs b/src/NzbDrone.Core/DecisionEngine/DownloadDecisionMaker.cs index e5ee8ac03..57ef05786 100644 --- a/src/NzbDrone.Core/DecisionEngine/DownloadDecisionMaker.cs +++ b/src/NzbDrone.Core/DecisionEngine/DownloadDecisionMaker.cs @@ -23,14 +23,14 @@ public interface IMakeDownloadDecision public class DownloadDecisionMaker : IMakeDownloadDecision { - private readonly IEnumerable _specifications; + private readonly IEnumerable _specifications; private readonly IParsingService _parsingService; private readonly ICustomFormatCalculationService _formatCalculator; private readonly IRemoteEpisodeAggregationService _aggregationService; private readonly ISceneMappingService _sceneMappingService; private readonly Logger _logger; - public DownloadDecisionMaker(IEnumerable specifications, + public DownloadDecisionMaker(IEnumerable specifications, IParsingService parsingService, ICustomFormatCalculationService formatService, IRemoteEpisodeAggregationService aggregationService, @@ -95,19 +95,20 @@ private IEnumerable GetDecisions(List reports, bo if (remoteEpisode.Series == null) { - var reason = "Unknown Series"; var matchingTvdbId = _sceneMappingService.FindTvdbId(parsedEpisodeInfo.SeriesTitle, parsedEpisodeInfo.ReleaseTitle, parsedEpisodeInfo.SeasonNumber); if (matchingTvdbId.HasValue) { - reason = $"{parsedEpisodeInfo.SeriesTitle} matches an alias for series with TVDB ID: {matchingTvdbId}"; + decision = new DownloadDecision(remoteEpisode, new DownloadRejection(DownloadRejectionReason.MatchesAnotherSeries, $"{parsedEpisodeInfo.SeriesTitle} matches an alias for series with TVDB ID: {matchingTvdbId}")); + } + else + { + decision = new DownloadDecision(remoteEpisode, new DownloadRejection(DownloadRejectionReason.UnknownSeries, "Unknown Series")); } - - decision = new DownloadDecision(remoteEpisode, new Rejection(reason)); } else if (remoteEpisode.Episodes.Empty()) { - decision = new DownloadDecision(remoteEpisode, new Rejection("Unable to identify correct episode(s) using release name and scene mappings")); + decision = new DownloadDecision(remoteEpisode, new DownloadRejection(DownloadRejectionReason.UnknownEpisode, "Unable to identify correct episode(s) using release name and scene mappings")); } else { @@ -141,7 +142,7 @@ private IEnumerable GetDecisions(List reports, bo Languages = parsedEpisodeInfo.Languages }; - decision = new DownloadDecision(remoteEpisode, new Rejection("Unable to parse release")); + decision = new DownloadDecision(remoteEpisode, new DownloadRejection(DownloadRejectionReason.UnableToParse, "Unable to parse release")); } } } @@ -150,7 +151,7 @@ private IEnumerable GetDecisions(List reports, bo _logger.Error(e, "Couldn't process release."); var remoteEpisode = new RemoteEpisode { Release = report }; - decision = new DownloadDecision(remoteEpisode, new Rejection("Unexpected error processing release")); + decision = new DownloadDecision(remoteEpisode, new DownloadRejection(DownloadRejectionReason.Error, "Unexpected error processing release")); } reportNumber++; @@ -193,7 +194,7 @@ private IEnumerable GetDecisions(List reports, bo private DownloadDecision GetDecisionForReport(RemoteEpisode remoteEpisode, SearchCriteriaBase searchCriteria = null) { - var reasons = Array.Empty(); + var reasons = Array.Empty(); foreach (var specifications in _specifications.GroupBy(v => v.Priority).OrderBy(v => v.Key)) { @@ -210,7 +211,7 @@ private DownloadDecision GetDecisionForReport(RemoteEpisode remoteEpisode, Searc return new DownloadDecision(remoteEpisode, reasons.ToArray()); } - private Rejection EvaluateSpec(IDecisionEngineSpecification spec, RemoteEpisode remoteEpisode, SearchCriteriaBase searchCriteriaBase = null) + private DownloadRejection EvaluateSpec(IDownloadDecisionEngineSpecification spec, RemoteEpisode remoteEpisode, SearchCriteriaBase searchCriteriaBase = null) { try { @@ -218,7 +219,7 @@ private Rejection EvaluateSpec(IDecisionEngineSpecification spec, RemoteEpisode if (!result.Accepted) { - return new Rejection(result.Reason, spec.Type); + return new DownloadRejection(result.Reason, result.Message, spec.Type); } } catch (Exception e) @@ -226,7 +227,7 @@ private Rejection EvaluateSpec(IDecisionEngineSpecification spec, RemoteEpisode e.Data.Add("report", remoteEpisode.Release.ToJson()); e.Data.Add("parsed", remoteEpisode.ParsedEpisodeInfo.ToJson()); _logger.Error(e, "Couldn't evaluate decision on {0}", remoteEpisode.Release.Title); - return new Rejection($"{spec.GetType().Name}: {e.Message}"); + return new DownloadRejection(DownloadRejectionReason.DecisionError, $"{spec.GetType().Name}: {e.Message}"); } return null; diff --git a/src/NzbDrone.Core/DecisionEngine/DownloadRejection.cs b/src/NzbDrone.Core/DecisionEngine/DownloadRejection.cs new file mode 100644 index 000000000..fbaeccdf1 --- /dev/null +++ b/src/NzbDrone.Core/DecisionEngine/DownloadRejection.cs @@ -0,0 +1,9 @@ +namespace NzbDrone.Core.DecisionEngine; + +public class DownloadRejection : Rejection +{ + public DownloadRejection(DownloadRejectionReason reason, string message, RejectionType type = RejectionType.Permanent) + : base(reason, message, type) + { + } +} diff --git a/src/NzbDrone.Core/DecisionEngine/DownloadRejectionReason.cs b/src/NzbDrone.Core/DecisionEngine/DownloadRejectionReason.cs new file mode 100644 index 000000000..72e83b93f --- /dev/null +++ b/src/NzbDrone.Core/DecisionEngine/DownloadRejectionReason.cs @@ -0,0 +1,75 @@ +namespace NzbDrone.Core.DecisionEngine; + +public enum DownloadRejectionReason +{ + Unknown, + UnknownSeries, + UnknownEpisode, + MatchesAnotherSeries, + UnableToParse, + Error, + DecisionError, + MinimumAgeDelay, + SeriesNotMonitored, + EpisodeNotMonitored, + HistoryRecentCutoffMet, + HistoryCdhDisabledCutoffMet, + HistoryHigherPreference, + HistoryHigherRevision, + HistoryCutoffMet, + HistoryCustomFormatCutoffMet, + HistoryCustomFormatScore, + HistoryCustomFormatScoreIncrement, + NoMatchingTag, + PropersDisabled, + ProperForOldFile, + WrongEpisode, + WrongSeason, + WrongSeries, + FullSeason, + UnknownRuntime, + BelowMinimumSize, + AboveMaximumSize, + AlreadyImportedSameHash, + AlreadyImportedSameName, + UnknownReleaseGroup, + ReleaseGroupDoesNotMatch, + IndexerDisabled, + Blocklisted, + CustomFormatMinimumScore, + MinimumFreeSpace, + FullSeasonNotAired, + MaximumSizeExceeded, + MinimumAge, + MaximumAge, + MultiSeason, + Sample, + ProtocolDisabled, + QualityNotWanted, + QualityUpgradesDisabled, + QueueHigherPreference, + QueueHigherRevision, + QueueCutoffMet, + QueueCustomFormatCutoffMet, + QueueCustomFormatScore, + QueueCustomFormatScoreIncrement, + QueueNoUpgrades, + QueuePropersDisabled, + Raw, + MustContainMissing, + MustNotContainPresent, + RepackDisabled, + RepackUnknownReleaseGroup, + RepackReleaseGroupDoesNotMatch, + ExistingFileHasMoreEpisodes, + AmbiguousNumbering, + NotSeasonPack, + SplitEpisode, + MinimumSeeders, + DiskHigherPreference, + DiskHigherRevision, + DiskCutoffMet, + DiskCustomFormatCutoffMet, + DiskCustomFormatScore, + DiskCustomFormatScoreIncrement, +} diff --git a/src/NzbDrone.Core/DecisionEngine/DownloadSpecDecision.cs b/src/NzbDrone.Core/DecisionEngine/DownloadSpecDecision.cs new file mode 100644 index 000000000..53653293d --- /dev/null +++ b/src/NzbDrone.Core/DecisionEngine/DownloadSpecDecision.cs @@ -0,0 +1,34 @@ +namespace NzbDrone.Core.DecisionEngine +{ + public class DownloadSpecDecision + { + public bool Accepted { get; private set; } + public DownloadRejectionReason Reason { get; set; } + public string Message { get; private set; } + + private static readonly DownloadSpecDecision AcceptDownloadSpecDecision = new () { Accepted = true }; + private DownloadSpecDecision() + { + } + + public static DownloadSpecDecision Accept() + { + return AcceptDownloadSpecDecision; + } + + public static DownloadSpecDecision Reject(DownloadRejectionReason reason, string message, params object[] args) + { + return Reject(reason, string.Format(message, args)); + } + + public static DownloadSpecDecision Reject(DownloadRejectionReason reason, string message) + { + return new DownloadSpecDecision + { + Accepted = false, + Reason = reason, + Message = message + }; + } + } +} diff --git a/src/NzbDrone.Core/DecisionEngine/Rejection.cs b/src/NzbDrone.Core/DecisionEngine/Rejection.cs index 723968f9b..1f7057ea2 100644 --- a/src/NzbDrone.Core/DecisionEngine/Rejection.cs +++ b/src/NzbDrone.Core/DecisionEngine/Rejection.cs @@ -1,19 +1,21 @@ namespace NzbDrone.Core.DecisionEngine { - public class Rejection + public class Rejection { - public string Reason { get; set; } + public TRejectionReason Reason { get; set; } + public string Message { get; set; } public RejectionType Type { get; set; } - public Rejection(string reason, RejectionType type = RejectionType.Permanent) + public Rejection(TRejectionReason reason, string message, RejectionType type = RejectionType.Permanent) { Reason = reason; + Message = message; Type = type; } public override string ToString() { - return string.Format("[{0}] {1}", Type, Reason); + return string.Format("[{0}] {1}", Type, Message); } } } diff --git a/src/NzbDrone.Core/DecisionEngine/Specifications/AcceptableSizeSpecification.cs b/src/NzbDrone.Core/DecisionEngine/Specifications/AcceptableSizeSpecification.cs index 04688ce14..2fea42d5d 100644 --- a/src/NzbDrone.Core/DecisionEngine/Specifications/AcceptableSizeSpecification.cs +++ b/src/NzbDrone.Core/DecisionEngine/Specifications/AcceptableSizeSpecification.cs @@ -8,7 +8,7 @@ namespace NzbDrone.Core.DecisionEngine.Specifications { - public class AcceptableSizeSpecification : IDecisionEngineSpecification + public class AcceptableSizeSpecification : IDownloadDecisionEngineSpecification { private readonly IQualityDefinitionService _qualityDefinitionService; private readonly IEpisodeService _episodeService; @@ -24,7 +24,7 @@ public AcceptableSizeSpecification(IQualityDefinitionService qualityDefinitionSe public SpecificationPriority Priority => SpecificationPriority.Default; public RejectionType Type => RejectionType.Permanent; - public Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria) + public DownloadSpecDecision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria) { _logger.Debug("Beginning size check for: {0}", subject); @@ -33,13 +33,13 @@ public Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCr if (subject.ParsedEpisodeInfo.Special) { _logger.Debug("Special release found, skipping size check."); - return Decision.Accept(); + return DownloadSpecDecision.Accept(); } if (subject.Release.Size == 0) { _logger.Debug("Release has unknown size, skipping size check"); - return Decision.Accept(); + return DownloadSpecDecision.Accept(); } var seriesRuntime = subject.Series.Runtime; @@ -75,7 +75,7 @@ public Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCr if (runtime == 0) { _logger.Debug("Runtime of all episodes is 0, unable to validate size until it is available, rejecting"); - return Decision.Reject("Runtime of all episodes is 0, unable to validate size until it is available"); + return DownloadSpecDecision.Reject(DownloadRejectionReason.UnknownRuntime, "Runtime of all episodes is 0, unable to validate size until it is available"); } var qualityDefinition = _qualityDefinitionService.Get(quality); @@ -93,7 +93,7 @@ public Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCr var runtimeMessage = subject.Episodes.Count == 1 ? $"{runtime}min" : $"{subject.Episodes.Count}x {runtime}min"; _logger.Debug("Item: {0}, Size: {1} is smaller than minimum allowed size ({2} bytes for {3}), rejecting.", subject, subject.Release.Size, minSize, runtimeMessage); - return Decision.Reject("{0} is smaller than minimum allowed {1} (for {2})", subject.Release.Size.SizeSuffix(), minSize.SizeSuffix(), runtimeMessage); + return DownloadSpecDecision.Reject(DownloadRejectionReason.BelowMinimumSize, "{0} is smaller than minimum allowed {1} (for {2})", subject.Release.Size.SizeSuffix(), minSize.SizeSuffix(), runtimeMessage); } } @@ -114,12 +114,12 @@ public Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCr var runtimeMessage = subject.Episodes.Count == 1 ? $"{runtime}min" : $"{subject.Episodes.Count}x {runtime}min"; _logger.Debug("Item: {0}, Size: {1} is greater than maximum allowed size ({2} for {3}), rejecting", subject, subject.Release.Size, maxSize, runtimeMessage); - return Decision.Reject("{0} is larger than maximum allowed {1} (for {2})", subject.Release.Size.SizeSuffix(), maxSize.SizeSuffix(), runtimeMessage); + return DownloadSpecDecision.Reject(DownloadRejectionReason.AboveMaximumSize, "{0} is larger than maximum allowed {1} (for {2})", subject.Release.Size.SizeSuffix(), maxSize.SizeSuffix(), runtimeMessage); } } _logger.Debug("Item: {0}, meets size constraints", subject); - return Decision.Accept(); + return DownloadSpecDecision.Accept(); } } } diff --git a/src/NzbDrone.Core/DecisionEngine/Specifications/AlreadyImportedSpecification.cs b/src/NzbDrone.Core/DecisionEngine/Specifications/AlreadyImportedSpecification.cs index 00bad3ba3..6a4b7cd96 100644 --- a/src/NzbDrone.Core/DecisionEngine/Specifications/AlreadyImportedSpecification.cs +++ b/src/NzbDrone.Core/DecisionEngine/Specifications/AlreadyImportedSpecification.cs @@ -9,7 +9,7 @@ namespace NzbDrone.Core.DecisionEngine.Specifications { - public class AlreadyImportedSpecification : IDecisionEngineSpecification + public class AlreadyImportedSpecification : IDownloadDecisionEngineSpecification { private readonly IHistoryService _historyService; private readonly IConfigService _configService; @@ -27,14 +27,14 @@ public AlreadyImportedSpecification(IHistoryService historyService, public SpecificationPriority Priority => SpecificationPriority.Database; public RejectionType Type => RejectionType.Permanent; - public Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria) + public DownloadSpecDecision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria) { var cdhEnabled = _configService.EnableCompletedDownloadHandling; if (!cdhEnabled) { _logger.Debug("Skipping already imported check because CDH is disabled"); - return Decision.Accept(); + return DownloadSpecDecision.Accept(); } _logger.Debug("Performing already imported check on report"); @@ -80,7 +80,7 @@ public Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCr if (torrentInfo?.InfoHash != null && torrentInfo.InfoHash.ToUpper() == lastGrabbed.DownloadId) { _logger.Debug("Has same torrent hash as a grabbed and imported release"); - return Decision.Reject("Has same torrent hash as a grabbed and imported release"); + return DownloadSpecDecision.Reject(DownloadRejectionReason.AlreadyImportedSameHash, "Has same torrent hash as a grabbed and imported release"); } } @@ -90,11 +90,11 @@ public Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCr if (release.Title.Equals(lastGrabbed.SourceTitle, StringComparison.InvariantCultureIgnoreCase)) { _logger.Debug("Has same release name as a grabbed and imported release"); - return Decision.Reject("Has same release name as a grabbed and imported release"); + return DownloadSpecDecision.Reject(DownloadRejectionReason.AlreadyImportedSameName, "Has same release name as a grabbed and imported release"); } } - return Decision.Accept(); + return DownloadSpecDecision.Accept(); } } } diff --git a/src/NzbDrone.Core/DecisionEngine/Specifications/AnimeVersionUpgradeSpecification.cs b/src/NzbDrone.Core/DecisionEngine/Specifications/AnimeVersionUpgradeSpecification.cs index d08f8de20..61e7a5535 100644 --- a/src/NzbDrone.Core/DecisionEngine/Specifications/AnimeVersionUpgradeSpecification.cs +++ b/src/NzbDrone.Core/DecisionEngine/Specifications/AnimeVersionUpgradeSpecification.cs @@ -9,7 +9,7 @@ namespace NzbDrone.Core.DecisionEngine.Specifications { - public class AnimeVersionUpgradeSpecification : IDecisionEngineSpecification + public class AnimeVersionUpgradeSpecification : IDownloadDecisionEngineSpecification { private readonly UpgradableSpecification _upgradableSpecification; private readonly IConfigService _configService; @@ -25,13 +25,13 @@ public AnimeVersionUpgradeSpecification(UpgradableSpecification upgradableSpecif public SpecificationPriority Priority => SpecificationPriority.Default; public RejectionType Type => RejectionType.Permanent; - public virtual Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria) + public virtual DownloadSpecDecision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria) { var releaseGroup = subject.ParsedEpisodeInfo.ReleaseGroup; if (subject.Series.SeriesType != SeriesTypes.Anime) { - return Decision.Accept(); + return DownloadSpecDecision.Accept(); } var downloadPropersAndRepacks = _configService.DownloadPropersAndRepacks; @@ -39,7 +39,7 @@ public virtual Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase if (downloadPropersAndRepacks == ProperDownloadTypes.DoNotPrefer) { _logger.Debug("Version upgrades are not preferred, skipping check"); - return Decision.Accept(); + return DownloadSpecDecision.Accept(); } foreach (var file in subject.Episodes.Where(c => c.EpisodeFileId != 0).Select(c => c.EpisodeFile.Value)) @@ -54,24 +54,24 @@ public virtual Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase if (file.ReleaseGroup.IsNullOrWhiteSpace()) { _logger.Debug("Unable to compare release group, existing file's release group is unknown"); - return Decision.Reject("Existing release group is unknown"); + return DownloadSpecDecision.Reject(DownloadRejectionReason.UnknownReleaseGroup, "Existing release group is unknown"); } if (releaseGroup.IsNullOrWhiteSpace()) { _logger.Debug("Unable to compare release group, release's release group is unknown"); - return Decision.Reject("Release group is unknown"); + return DownloadSpecDecision.Reject(DownloadRejectionReason.UnknownReleaseGroup, "Release group is unknown"); } if (file.ReleaseGroup != releaseGroup) { _logger.Debug("Existing Release group is: {0} - release's release group is: {1}", file.ReleaseGroup, releaseGroup); - return Decision.Reject("{0} does not match existing release group {1}", releaseGroup, file.ReleaseGroup); + return DownloadSpecDecision.Reject(DownloadRejectionReason.ReleaseGroupDoesNotMatch, "{0} does not match existing release group {1}", releaseGroup, file.ReleaseGroup); } } } - return Decision.Accept(); + return DownloadSpecDecision.Accept(); } } } diff --git a/src/NzbDrone.Core/DecisionEngine/Specifications/BlockedIndexerSpecification.cs b/src/NzbDrone.Core/DecisionEngine/Specifications/BlockedIndexerSpecification.cs index 9f7f0bc20..db5e59d6e 100644 --- a/src/NzbDrone.Core/DecisionEngine/Specifications/BlockedIndexerSpecification.cs +++ b/src/NzbDrone.Core/DecisionEngine/Specifications/BlockedIndexerSpecification.cs @@ -9,7 +9,7 @@ namespace NzbDrone.Core.DecisionEngine.Specifications { - public class BlockedIndexerSpecification : IDecisionEngineSpecification + public class BlockedIndexerSpecification : IDownloadDecisionEngineSpecification { private readonly IIndexerStatusService _indexerStatusService; private readonly Logger _logger; @@ -27,15 +27,15 @@ public BlockedIndexerSpecification(IIndexerStatusService indexerStatusService, I public SpecificationPriority Priority => SpecificationPriority.Database; public RejectionType Type => RejectionType.Temporary; - public virtual Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria) + public virtual DownloadSpecDecision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria) { var status = _blockedIndexerCache.Find(subject.Release.IndexerId.ToString()); if (status != null) { - return Decision.Reject($"Indexer {subject.Release.Indexer} is blocked till {status.DisabledTill} due to failures, cannot grab release."); + return DownloadSpecDecision.Reject(DownloadRejectionReason.IndexerDisabled, $"Indexer {subject.Release.Indexer} is blocked till {status.DisabledTill} due to failures, cannot grab release."); } - return Decision.Accept(); + return DownloadSpecDecision.Accept(); } private IDictionary FetchBlockedIndexer() diff --git a/src/NzbDrone.Core/DecisionEngine/Specifications/BlocklistSpecification.cs b/src/NzbDrone.Core/DecisionEngine/Specifications/BlocklistSpecification.cs index 422c4a7dd..61069b5b5 100644 --- a/src/NzbDrone.Core/DecisionEngine/Specifications/BlocklistSpecification.cs +++ b/src/NzbDrone.Core/DecisionEngine/Specifications/BlocklistSpecification.cs @@ -5,7 +5,7 @@ namespace NzbDrone.Core.DecisionEngine.Specifications { - public class BlocklistSpecification : IDecisionEngineSpecification + public class BlocklistSpecification : IDownloadDecisionEngineSpecification { private readonly IBlocklistService _blocklistService; private readonly Logger _logger; @@ -19,15 +19,15 @@ public BlocklistSpecification(IBlocklistService blocklistService, Logger logger) public SpecificationPriority Priority => SpecificationPriority.Database; public RejectionType Type => RejectionType.Permanent; - public Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria) + public DownloadSpecDecision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria) { if (_blocklistService.Blocklisted(subject.Series.Id, subject.Release)) { _logger.Debug("{0} is blocklisted, rejecting.", subject.Release.Title); - return Decision.Reject("Release is blocklisted"); + return DownloadSpecDecision.Reject(DownloadRejectionReason.Blocklisted, "Release is blocklisted"); } - return Decision.Accept(); + return DownloadSpecDecision.Accept(); } } } diff --git a/src/NzbDrone.Core/DecisionEngine/Specifications/CustomFormatAllowedByProfileSpecification.cs b/src/NzbDrone.Core/DecisionEngine/Specifications/CustomFormatAllowedByProfileSpecification.cs index a047abb3f..425c183d1 100644 --- a/src/NzbDrone.Core/DecisionEngine/Specifications/CustomFormatAllowedByProfileSpecification.cs +++ b/src/NzbDrone.Core/DecisionEngine/Specifications/CustomFormatAllowedByProfileSpecification.cs @@ -4,22 +4,22 @@ namespace NzbDrone.Core.DecisionEngine.Specifications { - public class CustomFormatAllowedbyProfileSpecification : IDecisionEngineSpecification + public class CustomFormatAllowedbyProfileSpecification : IDownloadDecisionEngineSpecification { public SpecificationPriority Priority => SpecificationPriority.Default; public RejectionType Type => RejectionType.Permanent; - public virtual Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria) + public virtual DownloadSpecDecision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria) { var minScore = subject.Series.QualityProfile.Value.MinFormatScore; var score = subject.CustomFormatScore; if (score < minScore) { - return Decision.Reject("Custom Formats {0} have score {1} below Series profile minimum {2}", subject.CustomFormats.ConcatToString(), score, minScore); + return DownloadSpecDecision.Reject(DownloadRejectionReason.CustomFormatMinimumScore, "Custom Formats {0} have score {1} below Series profile minimum {2}", subject.CustomFormats.ConcatToString(), score, minScore); } - return Decision.Accept(); + return DownloadSpecDecision.Accept(); } } } diff --git a/src/NzbDrone.Core/DecisionEngine/Specifications/FreeSpaceSpecification.cs b/src/NzbDrone.Core/DecisionEngine/Specifications/FreeSpaceSpecification.cs index 2d3d3b082..944bde33a 100644 --- a/src/NzbDrone.Core/DecisionEngine/Specifications/FreeSpaceSpecification.cs +++ b/src/NzbDrone.Core/DecisionEngine/Specifications/FreeSpaceSpecification.cs @@ -8,7 +8,7 @@ namespace NzbDrone.Core.DecisionEngine.Specifications { - public class FreeSpaceSpecification : IDecisionEngineSpecification + public class FreeSpaceSpecification : IDownloadDecisionEngineSpecification { private readonly IConfigService _configService; private readonly IDiskProvider _diskProvider; @@ -24,12 +24,12 @@ public FreeSpaceSpecification(IConfigService configService, IDiskProvider diskPr public SpecificationPriority Priority => SpecificationPriority.Default; public RejectionType Type => RejectionType.Permanent; - public Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria) + public DownloadSpecDecision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria) { if (_configService.SkipFreeSpaceCheckWhenImporting) { _logger.Debug("Skipping free space check"); - return Decision.Accept(); + return DownloadSpecDecision.Accept(); } var size = subject.Release.Size; @@ -49,7 +49,7 @@ public Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCr { _logger.Debug("Unable to get available space for {0}. Skipping", path); - return Decision.Accept(); + return DownloadSpecDecision.Accept(); } var minimumSpace = _configService.MinimumFreeSpaceWhenImporting.Megabytes(); @@ -60,7 +60,7 @@ public Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCr var message = "Importing after download will exceed available disk space"; _logger.Debug(message); - return Decision.Reject(message); + return DownloadSpecDecision.Reject(DownloadRejectionReason.MinimumFreeSpace, message); } if (remainingSpace < minimumSpace) @@ -68,10 +68,10 @@ public Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCr var message = $"Not enough free space ({minimumSpace.SizeSuffix()}) to import after download: {remainingSpace.SizeSuffix()}. (Settings: Media Management: Minimum Free Space)"; _logger.Debug(message); - return Decision.Reject(message); + return DownloadSpecDecision.Reject(DownloadRejectionReason.MinimumFreeSpace, message); } - return Decision.Accept(); + return DownloadSpecDecision.Accept(); } } } diff --git a/src/NzbDrone.Core/DecisionEngine/Specifications/FullSeasonSpecification.cs b/src/NzbDrone.Core/DecisionEngine/Specifications/FullSeasonSpecification.cs index 084ab0847..91f886ae2 100644 --- a/src/NzbDrone.Core/DecisionEngine/Specifications/FullSeasonSpecification.cs +++ b/src/NzbDrone.Core/DecisionEngine/Specifications/FullSeasonSpecification.cs @@ -7,7 +7,7 @@ namespace NzbDrone.Core.DecisionEngine.Specifications { - public class FullSeasonSpecification : IDecisionEngineSpecification + public class FullSeasonSpecification : IDownloadDecisionEngineSpecification { private readonly Logger _logger; @@ -19,7 +19,7 @@ public FullSeasonSpecification(Logger logger) public SpecificationPriority Priority => SpecificationPriority.Default; public RejectionType Type => RejectionType.Permanent; - public virtual Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria) + public virtual DownloadSpecDecision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria) { if (subject.ParsedEpisodeInfo.FullSeason) { @@ -28,11 +28,11 @@ public virtual Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase if (subject.Episodes.Any(e => !e.AirDateUtc.HasValue || e.AirDateUtc.Value.After(DateTime.UtcNow.AddHours(24)))) { _logger.Debug("Full season release {0} rejected. All episodes haven't aired yet.", subject.Release.Title); - return Decision.Reject("Full season release rejected. All episodes haven't aired yet."); + return DownloadSpecDecision.Reject(DownloadRejectionReason.FullSeasonNotAired, "Full season release rejected. All episodes haven't aired yet."); } } - return Decision.Accept(); + return DownloadSpecDecision.Accept(); } } } diff --git a/src/NzbDrone.Core/DecisionEngine/Specifications/IDecisionEngineSpecification.cs b/src/NzbDrone.Core/DecisionEngine/Specifications/IDownloadDecisionEngineSpecification.cs similarity index 59% rename from src/NzbDrone.Core/DecisionEngine/Specifications/IDecisionEngineSpecification.cs rename to src/NzbDrone.Core/DecisionEngine/Specifications/IDownloadDecisionEngineSpecification.cs index 2a1312191..9eecf0a93 100644 --- a/src/NzbDrone.Core/DecisionEngine/Specifications/IDecisionEngineSpecification.cs +++ b/src/NzbDrone.Core/DecisionEngine/Specifications/IDownloadDecisionEngineSpecification.cs @@ -3,12 +3,12 @@ namespace NzbDrone.Core.DecisionEngine.Specifications { - public interface IDecisionEngineSpecification + public interface IDownloadDecisionEngineSpecification { RejectionType Type { get; } SpecificationPriority Priority { get; } - Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria); + DownloadSpecDecision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria); } } diff --git a/src/NzbDrone.Core/DecisionEngine/Specifications/MaximumSizeSpecification.cs b/src/NzbDrone.Core/DecisionEngine/Specifications/MaximumSizeSpecification.cs index 553d06ab6..d9c0b420b 100644 --- a/src/NzbDrone.Core/DecisionEngine/Specifications/MaximumSizeSpecification.cs +++ b/src/NzbDrone.Core/DecisionEngine/Specifications/MaximumSizeSpecification.cs @@ -6,7 +6,7 @@ namespace NzbDrone.Core.DecisionEngine.Specifications { - public class MaximumSizeSpecification : IDecisionEngineSpecification + public class MaximumSizeSpecification : IDownloadDecisionEngineSpecification { private readonly IConfigService _configService; private readonly Logger _logger; @@ -20,7 +20,7 @@ public MaximumSizeSpecification(IConfigService configService, Logger logger) public SpecificationPriority Priority => SpecificationPriority.Default; public RejectionType Type => RejectionType.Permanent; - public Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria) + public DownloadSpecDecision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria) { var size = subject.Release.Size; var maximumSize = _configService.MaximumSize.Megabytes(); @@ -28,13 +28,13 @@ public Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCr if (maximumSize == 0) { _logger.Debug("Maximum size is not set."); - return Decision.Accept(); + return DownloadSpecDecision.Accept(); } if (size == 0) { _logger.Debug("Release has unknown size, skipping size check."); - return Decision.Accept(); + return DownloadSpecDecision.Accept(); } _logger.Debug("Checking if release meets maximum size requirements. {0}", size.SizeSuffix()); @@ -44,10 +44,10 @@ public Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCr var message = $"{size.SizeSuffix()} is too big, maximum size is {maximumSize.SizeSuffix()} (Settings->Indexers->Maximum Size)"; _logger.Debug(message); - return Decision.Reject(message); + return DownloadSpecDecision.Reject(DownloadRejectionReason.MaximumSizeExceeded, message); } - return Decision.Accept(); + return DownloadSpecDecision.Accept(); } } } diff --git a/src/NzbDrone.Core/DecisionEngine/Specifications/MinimumAgeSpecification.cs b/src/NzbDrone.Core/DecisionEngine/Specifications/MinimumAgeSpecification.cs index 854e4e5ed..61972c6b5 100644 --- a/src/NzbDrone.Core/DecisionEngine/Specifications/MinimumAgeSpecification.cs +++ b/src/NzbDrone.Core/DecisionEngine/Specifications/MinimumAgeSpecification.cs @@ -6,7 +6,7 @@ namespace NzbDrone.Core.DecisionEngine.Specifications { - public class MinimumAgeSpecification : IDecisionEngineSpecification + public class MinimumAgeSpecification : IDownloadDecisionEngineSpecification { private readonly IConfigService _configService; private readonly Logger _logger; @@ -20,12 +20,12 @@ public MinimumAgeSpecification(IConfigService configService, Logger logger) public SpecificationPriority Priority => SpecificationPriority.Default; public RejectionType Type => RejectionType.Temporary; - public virtual Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria) + public virtual DownloadSpecDecision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria) { if (subject.Release.DownloadProtocol != Indexers.DownloadProtocol.Usenet) { _logger.Debug("Not checking minimum age requirement for non-usenet report"); - return Decision.Accept(); + return DownloadSpecDecision.Accept(); } var age = subject.Release.AgeMinutes; @@ -35,7 +35,7 @@ public virtual Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase if (minimumAge == 0) { _logger.Debug("Minimum age is not set."); - return Decision.Accept(); + return DownloadSpecDecision.Accept(); } _logger.Debug("Checking if report meets minimum age requirements. {0}", ageRounded); @@ -43,12 +43,12 @@ public virtual Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase if (age < minimumAge) { _logger.Debug("Only {0} minutes old, minimum age is {1} minutes", ageRounded, minimumAge); - return Decision.Reject("Only {0} minutes old, minimum age is {1} minutes", ageRounded, minimumAge); + return DownloadSpecDecision.Reject(DownloadRejectionReason.MinimumAge, "Only {0} minutes old, minimum age is {1} minutes", ageRounded, minimumAge); } _logger.Debug("Release is {0} minutes old, greater than minimum age of {1} minutes", ageRounded, minimumAge); - return Decision.Accept(); + return DownloadSpecDecision.Accept(); } } } diff --git a/src/NzbDrone.Core/DecisionEngine/Specifications/MultiSeasonSpecification.cs b/src/NzbDrone.Core/DecisionEngine/Specifications/MultiSeasonSpecification.cs index 1c6479436..ba117324a 100644 --- a/src/NzbDrone.Core/DecisionEngine/Specifications/MultiSeasonSpecification.cs +++ b/src/NzbDrone.Core/DecisionEngine/Specifications/MultiSeasonSpecification.cs @@ -4,7 +4,7 @@ namespace NzbDrone.Core.DecisionEngine.Specifications { - public class MultiSeasonSpecification : IDecisionEngineSpecification + public class MultiSeasonSpecification : IDownloadDecisionEngineSpecification { private readonly Logger _logger; @@ -16,15 +16,15 @@ public MultiSeasonSpecification(Logger logger) public SpecificationPriority Priority => SpecificationPriority.Default; public RejectionType Type => RejectionType.Permanent; - public virtual Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria) + public virtual DownloadSpecDecision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria) { if (subject.ParsedEpisodeInfo.IsMultiSeason) { _logger.Debug("Multi-season release {0} rejected. Not supported", subject.Release.Title); - return Decision.Reject("Multi-season releases are not supported"); + return DownloadSpecDecision.Reject(DownloadRejectionReason.MultiSeason, "Multi-season releases are not supported"); } - return Decision.Accept(); + return DownloadSpecDecision.Accept(); } } } diff --git a/src/NzbDrone.Core/DecisionEngine/Specifications/NotSampleSpecification.cs b/src/NzbDrone.Core/DecisionEngine/Specifications/NotSampleSpecification.cs index 7340df169..5a22882a5 100644 --- a/src/NzbDrone.Core/DecisionEngine/Specifications/NotSampleSpecification.cs +++ b/src/NzbDrone.Core/DecisionEngine/Specifications/NotSampleSpecification.cs @@ -5,7 +5,7 @@ namespace NzbDrone.Core.DecisionEngine.Specifications { - public class NotSampleSpecification : IDecisionEngineSpecification + public class NotSampleSpecification : IDownloadDecisionEngineSpecification { private readonly Logger _logger; @@ -17,15 +17,15 @@ public NotSampleSpecification(Logger logger) _logger = logger; } - public Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria) + public DownloadSpecDecision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria) { if (subject.Release.Title.ToLower().Contains("sample") && subject.Release.Size < 70.Megabytes()) { _logger.Debug("Sample release, rejecting."); - return Decision.Reject("Sample"); + return DownloadSpecDecision.Reject(DownloadRejectionReason.Sample, "Sample"); } - return Decision.Accept(); + return DownloadSpecDecision.Accept(); } } } diff --git a/src/NzbDrone.Core/DecisionEngine/Specifications/ProtocolSpecification.cs b/src/NzbDrone.Core/DecisionEngine/Specifications/ProtocolSpecification.cs index ba956ecbd..e950611ff 100644 --- a/src/NzbDrone.Core/DecisionEngine/Specifications/ProtocolSpecification.cs +++ b/src/NzbDrone.Core/DecisionEngine/Specifications/ProtocolSpecification.cs @@ -6,7 +6,7 @@ namespace NzbDrone.Core.DecisionEngine.Specifications { - public class ProtocolSpecification : IDecisionEngineSpecification + public class ProtocolSpecification : IDownloadDecisionEngineSpecification { private readonly IDelayProfileService _delayProfileService; private readonly Logger _logger; @@ -21,23 +21,23 @@ public ProtocolSpecification(IDelayProfileService delayProfileService, public SpecificationPriority Priority => SpecificationPriority.Default; public RejectionType Type => RejectionType.Permanent; - public virtual Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria) + public virtual DownloadSpecDecision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria) { var delayProfile = _delayProfileService.BestForTags(subject.Series.Tags); if (subject.Release.DownloadProtocol == DownloadProtocol.Usenet && !delayProfile.EnableUsenet) { _logger.Debug("[{0}] Usenet is not enabled for this series", subject.Release.Title); - return Decision.Reject("Usenet is not enabled for this series"); + return DownloadSpecDecision.Reject(DownloadRejectionReason.ProtocolDisabled, "Usenet is not enabled for this series"); } if (subject.Release.DownloadProtocol == DownloadProtocol.Torrent && !delayProfile.EnableTorrent) { _logger.Debug("[{0}] Torrent is not enabled for this series", subject.Release.Title); - return Decision.Reject("Torrent is not enabled for this series"); + return DownloadSpecDecision.Reject(DownloadRejectionReason.ProtocolDisabled, "Torrent is not enabled for this series"); } - return Decision.Accept(); + return DownloadSpecDecision.Accept(); } } } diff --git a/src/NzbDrone.Core/DecisionEngine/Specifications/QualityAllowedByProfileSpecification.cs b/src/NzbDrone.Core/DecisionEngine/Specifications/QualityAllowedByProfileSpecification.cs index 4adab86a0..6e2057868 100644 --- a/src/NzbDrone.Core/DecisionEngine/Specifications/QualityAllowedByProfileSpecification.cs +++ b/src/NzbDrone.Core/DecisionEngine/Specifications/QualityAllowedByProfileSpecification.cs @@ -4,7 +4,7 @@ namespace NzbDrone.Core.DecisionEngine.Specifications { - public class QualityAllowedByProfileSpecification : IDecisionEngineSpecification + public class QualityAllowedByProfileSpecification : IDownloadDecisionEngineSpecification { private readonly Logger _logger; @@ -16,7 +16,7 @@ public QualityAllowedByProfileSpecification(Logger logger) public SpecificationPriority Priority => SpecificationPriority.Default; public RejectionType Type => RejectionType.Permanent; - public virtual Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria) + public virtual DownloadSpecDecision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria) { _logger.Debug("Checking if report meets quality requirements. {0}", subject.ParsedEpisodeInfo.Quality); @@ -27,10 +27,10 @@ public virtual Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase if (!qualityOrGroup.Allowed) { _logger.Debug("Quality {0} rejected by Series' quality profile", subject.ParsedEpisodeInfo.Quality); - return Decision.Reject("{0} is not wanted in profile", subject.ParsedEpisodeInfo.Quality.Quality); + return DownloadSpecDecision.Reject(DownloadRejectionReason.QualityNotWanted, "{0} is not wanted in profile", subject.ParsedEpisodeInfo.Quality.Quality); } - return Decision.Accept(); + return DownloadSpecDecision.Accept(); } } } diff --git a/src/NzbDrone.Core/DecisionEngine/Specifications/QueueSpecification.cs b/src/NzbDrone.Core/DecisionEngine/Specifications/QueueSpecification.cs index 97a1993e8..66160260e 100644 --- a/src/NzbDrone.Core/DecisionEngine/Specifications/QueueSpecification.cs +++ b/src/NzbDrone.Core/DecisionEngine/Specifications/QueueSpecification.cs @@ -10,7 +10,7 @@ namespace NzbDrone.Core.DecisionEngine.Specifications { - public class QueueSpecification : IDecisionEngineSpecification + public class QueueSpecification : IDownloadDecisionEngineSpecification { private readonly IQueueService _queueService; private readonly UpgradableSpecification _upgradableSpecification; @@ -34,7 +34,7 @@ public QueueSpecification(IQueueService queueService, public SpecificationPriority Priority => SpecificationPriority.Default; public RejectionType Type => RejectionType.Permanent; - public Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria) + public DownloadSpecDecision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria) { var queue = _queueService.GetQueue(); var matchingEpisode = queue.Where(q => q.RemoteEpisode?.Series != null && @@ -65,7 +65,7 @@ public Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCr queuedItemCustomFormats, subject.ParsedEpisodeInfo.Quality)) { - return Decision.Reject("Release in queue already meets cutoff: {0}", remoteEpisode.ParsedEpisodeInfo.Quality); + return DownloadSpecDecision.Reject(DownloadRejectionReason.QueueCutoffMet, "Release in queue already meets cutoff: {0}", remoteEpisode.ParsedEpisodeInfo.Quality); } _logger.Debug("Checking if release is higher quality than queued release. Queued: {0}", remoteEpisode.ParsedEpisodeInfo.Quality); @@ -79,22 +79,22 @@ public Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCr switch (upgradeableRejectReason) { case UpgradeableRejectReason.BetterQuality: - return Decision.Reject("Release in queue is of equal or higher preference: {0}", remoteEpisode.ParsedEpisodeInfo.Quality); + return DownloadSpecDecision.Reject(DownloadRejectionReason.QueueHigherPreference, "Release in queue is of equal or higher preference: {0}", remoteEpisode.ParsedEpisodeInfo.Quality); case UpgradeableRejectReason.BetterRevision: - return Decision.Reject("Release in queue is of equal or higher revision: {0}", remoteEpisode.ParsedEpisodeInfo.Quality.Revision); + return DownloadSpecDecision.Reject(DownloadRejectionReason.QueueHigherRevision, "Release in queue is of equal or higher revision: {0}", remoteEpisode.ParsedEpisodeInfo.Quality.Revision); case UpgradeableRejectReason.QualityCutoff: - return Decision.Reject("Release in queue meets quality cutoff: {0}", qualityProfile.Items[qualityProfile.GetIndex(qualityProfile.Cutoff).Index]); + return DownloadSpecDecision.Reject(DownloadRejectionReason.QueueCutoffMet, "Release in queue meets quality cutoff: {0}", qualityProfile.Items[qualityProfile.GetIndex(qualityProfile.Cutoff).Index]); case UpgradeableRejectReason.CustomFormatCutoff: - return Decision.Reject("Release in queue meets Custom Format cutoff: {0}", qualityProfile.CutoffFormatScore); + return DownloadSpecDecision.Reject(DownloadRejectionReason.QueueCustomFormatCutoffMet, "Release in queue meets Custom Format cutoff: {0}", qualityProfile.CutoffFormatScore); case UpgradeableRejectReason.CustomFormatScore: - return Decision.Reject("Release in queue has an equal or higher Custom Format score: {0}", qualityProfile.CalculateCustomFormatScore(queuedItemCustomFormats)); + return DownloadSpecDecision.Reject(DownloadRejectionReason.QueueCustomFormatScore, "Release in queue has an equal or higher Custom Format score: {0}", qualityProfile.CalculateCustomFormatScore(queuedItemCustomFormats)); case UpgradeableRejectReason.MinCustomFormatScore: - return Decision.Reject("Release in queue has Custom Format score within Custom Format score increment: {0}", qualityProfile.MinUpgradeFormatScore); + return DownloadSpecDecision.Reject(DownloadRejectionReason.QueueCustomFormatScoreIncrement, "Release in queue has Custom Format score within Custom Format score increment: {0}", qualityProfile.MinUpgradeFormatScore); } _logger.Debug("Checking if profiles allow upgrading. Queued: {0}", remoteEpisode.ParsedEpisodeInfo.Quality); @@ -105,7 +105,7 @@ public Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCr subject.ParsedEpisodeInfo.Quality, subject.CustomFormats)) { - return Decision.Reject("Another release is queued and the Quality profile does not allow upgrades"); + return DownloadSpecDecision.Reject(DownloadRejectionReason.QueueNoUpgrades, "Another release is queued and the Quality profile does not allow upgrades"); } if (_upgradableSpecification.IsRevisionUpgrade(remoteEpisode.ParsedEpisodeInfo.Quality, subject.ParsedEpisodeInfo.Quality)) @@ -113,12 +113,12 @@ public Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCr if (_configService.DownloadPropersAndRepacks == ProperDownloadTypes.DoNotUpgrade) { _logger.Debug("Auto downloading of propers is disabled"); - return Decision.Reject("Proper downloading is disabled"); + return DownloadSpecDecision.Reject(DownloadRejectionReason.QueuePropersDisabled, "Proper downloading is disabled"); } } } - return Decision.Accept(); + return DownloadSpecDecision.Accept(); } } } diff --git a/src/NzbDrone.Core/DecisionEngine/Specifications/RawDiskSpecification.cs b/src/NzbDrone.Core/DecisionEngine/Specifications/RawDiskSpecification.cs index ed4fb1545..681a7ce29 100644 --- a/src/NzbDrone.Core/DecisionEngine/Specifications/RawDiskSpecification.cs +++ b/src/NzbDrone.Core/DecisionEngine/Specifications/RawDiskSpecification.cs @@ -7,7 +7,7 @@ namespace NzbDrone.Core.DecisionEngine.Specifications { - public class RawDiskSpecification : IDecisionEngineSpecification + public class RawDiskSpecification : IDownloadDecisionEngineSpecification { private static readonly Regex[] DiscRegex = new[] { @@ -29,11 +29,11 @@ public RawDiskSpecification(Logger logger) public SpecificationPriority Priority => SpecificationPriority.Default; public RejectionType Type => RejectionType.Permanent; - public virtual Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria) + public virtual DownloadSpecDecision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria) { if (subject.Release == null) { - return Decision.Accept(); + return DownloadSpecDecision.Accept(); } foreach (var regex in DiscRegex) @@ -41,28 +41,28 @@ public virtual Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase if (regex.IsMatch(subject.Release.Title)) { _logger.Debug("Release contains raw Bluray/DVD, rejecting."); - return Decision.Reject("Raw Bluray/DVD release"); + return DownloadSpecDecision.Reject(DownloadRejectionReason.Raw, "Raw Bluray/DVD release"); } } if (subject.Release.Container.IsNullOrWhiteSpace()) { - return Decision.Accept(); + return DownloadSpecDecision.Accept(); } if (_dvdContainerTypes.Contains(subject.Release.Container.ToLower())) { _logger.Debug("Release contains raw DVD, rejecting."); - return Decision.Reject("Raw DVD release"); + return DownloadSpecDecision.Reject(DownloadRejectionReason.Raw, "Raw DVD release"); } if (_blurayContainerTypes.Contains(subject.Release.Container.ToLower())) { _logger.Debug("Release contains raw Bluray, rejecting."); - return Decision.Reject("Raw Bluray release"); + return DownloadSpecDecision.Reject(DownloadRejectionReason.Raw, "Raw Bluray release"); } - return Decision.Accept(); + return DownloadSpecDecision.Accept(); } } } diff --git a/src/NzbDrone.Core/DecisionEngine/Specifications/ReleaseRestrictionsSpecification.cs b/src/NzbDrone.Core/DecisionEngine/Specifications/ReleaseRestrictionsSpecification.cs index 2fcad98ef..a125d4f01 100644 --- a/src/NzbDrone.Core/DecisionEngine/Specifications/ReleaseRestrictionsSpecification.cs +++ b/src/NzbDrone.Core/DecisionEngine/Specifications/ReleaseRestrictionsSpecification.cs @@ -8,7 +8,7 @@ namespace NzbDrone.Core.DecisionEngine.Specifications { - public class ReleaseRestrictionsSpecification : IDecisionEngineSpecification + public class ReleaseRestrictionsSpecification : IDownloadDecisionEngineSpecification { private readonly Logger _logger; private readonly IReleaseProfileService _releaseProfileService; @@ -24,7 +24,7 @@ public ReleaseRestrictionsSpecification(ITermMatcherService termMatcherService, public SpecificationPriority Priority => SpecificationPriority.Default; public RejectionType Type => RejectionType.Permanent; - public virtual Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria) + public virtual DownloadSpecDecision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria) { _logger.Debug("Checking if release meets restrictions: {0}", subject); @@ -43,7 +43,7 @@ public virtual Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase { var terms = string.Join(", ", requiredTerms); _logger.Debug("[{0}] does not contain one of the required terms: {1}", title, terms); - return Decision.Reject("Does not contain one of the required terms: {0}", terms); + return DownloadSpecDecision.Reject(DownloadRejectionReason.MustContainMissing, "Does not contain one of the required terms: {0}", terms); } } @@ -56,12 +56,12 @@ public virtual Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase { var terms = string.Join(", ", foundTerms); _logger.Debug("[{0}] contains these ignored terms: {1}", title, terms); - return Decision.Reject("Contains these ignored terms: {0}", terms); + return DownloadSpecDecision.Reject(DownloadRejectionReason.MustNotContainPresent, "Contains these ignored terms: {0}", terms); } } _logger.Debug("[{0}] No restrictions apply, allowing", subject); - return Decision.Accept(); + return DownloadSpecDecision.Accept(); } private List ContainsAny(List terms, string title) diff --git a/src/NzbDrone.Core/DecisionEngine/Specifications/RepackSpecification.cs b/src/NzbDrone.Core/DecisionEngine/Specifications/RepackSpecification.cs index b82c7e564..bda0cdeed 100644 --- a/src/NzbDrone.Core/DecisionEngine/Specifications/RepackSpecification.cs +++ b/src/NzbDrone.Core/DecisionEngine/Specifications/RepackSpecification.cs @@ -9,7 +9,7 @@ namespace NzbDrone.Core.DecisionEngine.Specifications { - public class RepackSpecification : IDecisionEngineSpecification + public class RepackSpecification : IDownloadDecisionEngineSpecification { private readonly UpgradableSpecification _upgradableSpecification; private readonly IConfigService _configService; @@ -25,11 +25,11 @@ public RepackSpecification(UpgradableSpecification upgradableSpecification, ICon public SpecificationPriority Priority => SpecificationPriority.Database; public RejectionType Type => RejectionType.Permanent; - public Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria) + public DownloadSpecDecision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria) { if (!subject.ParsedEpisodeInfo.Quality.Revision.IsRepack) { - return Decision.Accept(); + return DownloadSpecDecision.Accept(); } var downloadPropersAndRepacks = _configService.DownloadPropersAndRepacks; @@ -37,7 +37,7 @@ public Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCr if (downloadPropersAndRepacks == ProperDownloadTypes.DoNotPrefer) { _logger.Debug("Repacks are not preferred, skipping check"); - return Decision.Accept(); + return DownloadSpecDecision.Accept(); } foreach (var file in subject.Episodes.Where(c => c.EpisodeFileId != 0).Select(c => c.EpisodeFile.Value)) @@ -47,7 +47,7 @@ public Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCr if (downloadPropersAndRepacks == ProperDownloadTypes.DoNotUpgrade) { _logger.Debug("Auto downloading of repacks is disabled"); - return Decision.Reject("Repack downloading is disabled"); + return DownloadSpecDecision.Reject(DownloadRejectionReason.RepackDisabled, "Repack downloading is disabled"); } var releaseGroup = subject.ParsedEpisodeInfo.ReleaseGroup; @@ -55,12 +55,12 @@ public Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCr if (fileReleaseGroup.IsNullOrWhiteSpace()) { - return Decision.Reject("Unable to determine release group for the existing file"); + return DownloadSpecDecision.Reject(DownloadRejectionReason.RepackUnknownReleaseGroup, "Unable to determine release group for the existing file"); } if (releaseGroup.IsNullOrWhiteSpace()) { - return Decision.Reject("Unable to determine release group for this release"); + return DownloadSpecDecision.Reject(DownloadRejectionReason.RepackUnknownReleaseGroup, "Unable to determine release group for this release"); } if (!fileReleaseGroup.Equals(releaseGroup, StringComparison.InvariantCultureIgnoreCase)) @@ -69,7 +69,8 @@ public Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCr "Release is a repack for a different release group. Release Group: {0}. File release group: {1}", releaseGroup, fileReleaseGroup); - return Decision.Reject( + return DownloadSpecDecision.Reject( + DownloadRejectionReason.RepackReleaseGroupDoesNotMatch, "Release is a repack for a different release group. Release Group: {0}. File release group: {1}", releaseGroup, fileReleaseGroup); @@ -77,7 +78,7 @@ public Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCr } } - return Decision.Accept(); + return DownloadSpecDecision.Accept(); } } } diff --git a/src/NzbDrone.Core/DecisionEngine/Specifications/RetentionSpecification.cs b/src/NzbDrone.Core/DecisionEngine/Specifications/RetentionSpecification.cs index 99cf93f67..22fcf4253 100644 --- a/src/NzbDrone.Core/DecisionEngine/Specifications/RetentionSpecification.cs +++ b/src/NzbDrone.Core/DecisionEngine/Specifications/RetentionSpecification.cs @@ -5,7 +5,7 @@ namespace NzbDrone.Core.DecisionEngine.Specifications { - public class RetentionSpecification : IDecisionEngineSpecification + public class RetentionSpecification : IDownloadDecisionEngineSpecification { private readonly IConfigService _configService; private readonly Logger _logger; @@ -19,12 +19,12 @@ public RetentionSpecification(IConfigService configService, Logger logger) public SpecificationPriority Priority => SpecificationPriority.Default; public RejectionType Type => RejectionType.Permanent; - public virtual Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria) + public virtual DownloadSpecDecision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria) { if (subject.Release.DownloadProtocol != Indexers.DownloadProtocol.Usenet) { _logger.Debug("Not checking retention requirement for non-usenet report"); - return Decision.Accept(); + return DownloadSpecDecision.Accept(); } var age = subject.Release.Age; @@ -34,10 +34,10 @@ public virtual Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase if (retention > 0 && age > retention) { _logger.Debug("Report age: {0} rejected by user's retention limit", age); - return Decision.Reject("Older than configured retention"); + return DownloadSpecDecision.Reject(DownloadRejectionReason.MaximumAge, "Older than configured retention"); } - return Decision.Accept(); + return DownloadSpecDecision.Accept(); } } } diff --git a/src/NzbDrone.Core/DecisionEngine/Specifications/RssSync/DelaySpecification.cs b/src/NzbDrone.Core/DecisionEngine/Specifications/RssSync/DelaySpecification.cs index 07dce7b5c..1a9339bb3 100644 --- a/src/NzbDrone.Core/DecisionEngine/Specifications/RssSync/DelaySpecification.cs +++ b/src/NzbDrone.Core/DecisionEngine/Specifications/RssSync/DelaySpecification.cs @@ -8,7 +8,7 @@ namespace NzbDrone.Core.DecisionEngine.Specifications.RssSync { - public class DelaySpecification : IDecisionEngineSpecification + public class DelaySpecification : IDownloadDecisionEngineSpecification { private readonly IPendingReleaseService _pendingReleaseService; private readonly IDelayProfileService _delayProfileService; @@ -26,12 +26,12 @@ public DelaySpecification(IPendingReleaseService pendingReleaseService, public SpecificationPriority Priority => SpecificationPriority.Database; public RejectionType Type => RejectionType.Temporary; - public virtual Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria) + public virtual DownloadSpecDecision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria) { if (searchCriteria != null && searchCriteria.UserInvokedSearch) { _logger.Debug("Ignoring delay for user invoked search"); - return Decision.Accept(); + return DownloadSpecDecision.Accept(); } var qualityProfile = subject.Series.QualityProfile.Value; @@ -42,7 +42,7 @@ public virtual Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase if (delay == 0) { _logger.Debug("QualityProfile does not require a waiting period before download for {0}.", subject.Release.DownloadProtocol); - return Decision.Accept(); + return DownloadSpecDecision.Accept(); } var qualityComparer = new QualityModelComparer(qualityProfile); @@ -58,7 +58,7 @@ public virtual Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase if (qualityCompare == 0 && newQuality?.Revision.CompareTo(currentQuality.Revision) > 0) { _logger.Debug("New quality is a better revision for existing quality, skipping delay"); - return Decision.Accept(); + return DownloadSpecDecision.Accept(); } } } @@ -72,7 +72,7 @@ public virtual Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase if (isBestInProfile && isPreferredProtocol) { _logger.Debug("Quality is highest in profile for preferred protocol, will not delay"); - return Decision.Accept(); + return DownloadSpecDecision.Accept(); } } @@ -85,7 +85,7 @@ public virtual Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase if (score >= minimum && isPreferredProtocol) { _logger.Debug("Custom format score ({0}) meets minimum ({1}) for preferred protocol, will not delay", score, minimum); - return Decision.Accept(); + return DownloadSpecDecision.Accept(); } } @@ -95,16 +95,16 @@ public virtual Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase if (oldest != null && oldest.Release.AgeMinutes > delay) { - return Decision.Accept(); + return DownloadSpecDecision.Accept(); } if (subject.Release.AgeMinutes < delay) { _logger.Debug("Waiting for better quality release, There is a {0} minute delay on {1}", delay, subject.Release.DownloadProtocol); - return Decision.Reject("Waiting for better quality release"); + return DownloadSpecDecision.Reject(DownloadRejectionReason.MinimumAgeDelay, "Waiting for better quality release"); } - return Decision.Accept(); + return DownloadSpecDecision.Accept(); } } } diff --git a/src/NzbDrone.Core/DecisionEngine/Specifications/RssSync/DeletedEpisodeFileSpecification.cs b/src/NzbDrone.Core/DecisionEngine/Specifications/RssSync/DeletedEpisodeFileSpecification.cs index 7a2d3f5d6..2f8208fb4 100644 --- a/src/NzbDrone.Core/DecisionEngine/Specifications/RssSync/DeletedEpisodeFileSpecification.cs +++ b/src/NzbDrone.Core/DecisionEngine/Specifications/RssSync/DeletedEpisodeFileSpecification.cs @@ -10,7 +10,7 @@ namespace NzbDrone.Core.DecisionEngine.Specifications.RssSync { - public class DeletedEpisodeFileSpecification : IDecisionEngineSpecification + public class DeletedEpisodeFileSpecification : IDownloadDecisionEngineSpecification { private readonly IDiskProvider _diskProvider; private readonly IConfigService _configService; @@ -26,17 +26,17 @@ public DeletedEpisodeFileSpecification(IDiskProvider diskProvider, IConfigServic public SpecificationPriority Priority => SpecificationPriority.Disk; public RejectionType Type => RejectionType.Temporary; - public virtual Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria) + public virtual DownloadSpecDecision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria) { if (!_configService.AutoUnmonitorPreviouslyDownloadedEpisodes) { - return Decision.Accept(); + return DownloadSpecDecision.Accept(); } if (searchCriteria != null) { _logger.Debug("Skipping deleted episodefile check during search"); - return Decision.Accept(); + return DownloadSpecDecision.Accept(); } var missingEpisodeFiles = subject.Episodes @@ -54,10 +54,10 @@ public virtual Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase } _logger.Debug("Files for this episode exist in the database but not on disk, will be unmonitored on next diskscan. skipping."); - return Decision.Reject("Series is not monitored"); + return DownloadSpecDecision.Reject(DownloadRejectionReason.SeriesNotMonitored, "Series is not monitored"); } - return Decision.Accept(); + return DownloadSpecDecision.Accept(); } private bool IsEpisodeFileMissing(Series series, EpisodeFile episodeFile) diff --git a/src/NzbDrone.Core/DecisionEngine/Specifications/RssSync/HistorySpecification.cs b/src/NzbDrone.Core/DecisionEngine/Specifications/RssSync/HistorySpecification.cs index 34b2ace4b..f063c5e28 100644 --- a/src/NzbDrone.Core/DecisionEngine/Specifications/RssSync/HistorySpecification.cs +++ b/src/NzbDrone.Core/DecisionEngine/Specifications/RssSync/HistorySpecification.cs @@ -9,7 +9,7 @@ namespace NzbDrone.Core.DecisionEngine.Specifications.RssSync { - public class HistorySpecification : IDecisionEngineSpecification + public class HistorySpecification : IDownloadDecisionEngineSpecification { private readonly IHistoryService _historyService; private readonly UpgradableSpecification _upgradableSpecification; @@ -33,12 +33,12 @@ public HistorySpecification(IHistoryService historyService, public SpecificationPriority Priority => SpecificationPriority.Database; public RejectionType Type => RejectionType.Permanent; - public virtual Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria) + public virtual DownloadSpecDecision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria) { if (searchCriteria != null) { _logger.Debug("Skipping history check during search"); - return Decision.Accept(); + return DownloadSpecDecision.Accept(); } var cdhEnabled = _configService.EnableCompletedDownloadHandling; @@ -81,10 +81,10 @@ public virtual Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase { if (recent) { - return Decision.Reject("Recent grab event in history already meets cutoff: {0}", mostRecent.Quality); + return DownloadSpecDecision.Reject(DownloadRejectionReason.HistoryRecentCutoffMet, "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); + return DownloadSpecDecision.Reject(DownloadRejectionReason.HistoryCdhDisabledCutoffMet, "CDH is disabled and grab event in history already meets cutoff: {0}", mostRecent.Quality); } var rejectionSubject = recent ? "Recent" : "CDH is disabled and"; @@ -95,27 +95,27 @@ public virtual Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase continue; case UpgradeableRejectReason.BetterQuality: - return Decision.Reject("{0} grab event in history is of equal or higher preference: {1}", rejectionSubject, mostRecent.Quality); + return DownloadSpecDecision.Reject(DownloadRejectionReason.HistoryHigherPreference, "{0} grab event in history is of equal or higher preference: {1}", rejectionSubject, mostRecent.Quality); case UpgradeableRejectReason.BetterRevision: - return Decision.Reject("{0} grab event in history is of equal or higher revision: {1}", rejectionSubject, mostRecent.Quality.Revision); + return DownloadSpecDecision.Reject(DownloadRejectionReason.HistoryHigherRevision, "{0} grab event in history is of equal or higher revision: {1}", rejectionSubject, mostRecent.Quality.Revision); case UpgradeableRejectReason.QualityCutoff: - return Decision.Reject("{0} grab event in history meets quality cutoff: {1}", rejectionSubject, qualityProfile.Items[qualityProfile.GetIndex(qualityProfile.Cutoff).Index]); + return DownloadSpecDecision.Reject(DownloadRejectionReason.HistoryCutoffMet, "{0} grab event in history meets quality cutoff: {1}", rejectionSubject, qualityProfile.Items[qualityProfile.GetIndex(qualityProfile.Cutoff).Index]); case UpgradeableRejectReason.CustomFormatCutoff: - return Decision.Reject("{0} grab event in history meets Custom Format cutoff: {1}", rejectionSubject, qualityProfile.CutoffFormatScore); + return DownloadSpecDecision.Reject(DownloadRejectionReason.HistoryCustomFormatCutoffMet, "{0} grab event in history meets Custom Format cutoff: {1}", rejectionSubject, qualityProfile.CutoffFormatScore); case UpgradeableRejectReason.CustomFormatScore: - return Decision.Reject("{0} grab event in history has an equal or higher Custom Format score: {1}", rejectionSubject, qualityProfile.CalculateCustomFormatScore(customFormats)); + return DownloadSpecDecision.Reject(DownloadRejectionReason.HistoryCustomFormatScore, "{0} grab event in history has an equal or higher Custom Format score: {1}", rejectionSubject, qualityProfile.CalculateCustomFormatScore(customFormats)); case UpgradeableRejectReason.MinCustomFormatScore: - return Decision.Reject("{0} grab event in history has Custom Format score within Custom Format score increment: {1}", rejectionSubject, qualityProfile.MinUpgradeFormatScore); + return DownloadSpecDecision.Reject(DownloadRejectionReason.HistoryCustomFormatScoreIncrement, "{0} grab event in history has Custom Format score within Custom Format score increment: {1}", rejectionSubject, qualityProfile.MinUpgradeFormatScore); } } } - return Decision.Accept(); + return DownloadSpecDecision.Accept(); } } } diff --git a/src/NzbDrone.Core/DecisionEngine/Specifications/RssSync/IndexerTagSpecification.cs b/src/NzbDrone.Core/DecisionEngine/Specifications/RssSync/IndexerTagSpecification.cs index 2d861bad8..0bdac306f 100644 --- a/src/NzbDrone.Core/DecisionEngine/Specifications/RssSync/IndexerTagSpecification.cs +++ b/src/NzbDrone.Core/DecisionEngine/Specifications/RssSync/IndexerTagSpecification.cs @@ -8,7 +8,7 @@ namespace NzbDrone.Core.DecisionEngine.Specifications.RssSync { - public class IndexerTagSpecification : IDecisionEngineSpecification + public class IndexerTagSpecification : IDownloadDecisionEngineSpecification { private readonly Logger _logger; private readonly IIndexerFactory _indexerFactory; @@ -22,11 +22,11 @@ public IndexerTagSpecification(Logger logger, IIndexerFactory indexerFactory) public SpecificationPriority Priority => SpecificationPriority.Default; public RejectionType Type => RejectionType.Permanent; - public virtual Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria) + public virtual DownloadSpecDecision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria) { if (subject.Release == null || subject.Series?.Tags == null || subject.Release.IndexerId == 0) { - return Decision.Accept(); + return DownloadSpecDecision.Accept(); } IndexerDefinition indexer; @@ -37,7 +37,7 @@ public virtual Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase catch (ModelNotFoundException) { _logger.Debug("Indexer with id {0} does not exist, skipping indexer tags check", subject.Release.IndexerId); - return Decision.Accept(); + return DownloadSpecDecision.Accept(); } // If indexer has tags, check that at least one of them is present on the series @@ -47,10 +47,10 @@ public virtual Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase { _logger.Debug("Indexer {0} has tags. None of these are present on series {1}. Rejecting", subject.Release.Indexer, subject.Series); - return Decision.Reject("Series tags do not match any of the indexer tags"); + return DownloadSpecDecision.Reject(DownloadRejectionReason.NoMatchingTag, "Series tags do not match any of the indexer tags"); } - return Decision.Accept(); + return DownloadSpecDecision.Accept(); } } } diff --git a/src/NzbDrone.Core/DecisionEngine/Specifications/RssSync/MonitoredEpisodeSpecification.cs b/src/NzbDrone.Core/DecisionEngine/Specifications/RssSync/MonitoredEpisodeSpecification.cs index a0524bea2..cbd36198d 100644 --- a/src/NzbDrone.Core/DecisionEngine/Specifications/RssSync/MonitoredEpisodeSpecification.cs +++ b/src/NzbDrone.Core/DecisionEngine/Specifications/RssSync/MonitoredEpisodeSpecification.cs @@ -5,7 +5,7 @@ namespace NzbDrone.Core.DecisionEngine.Specifications.RssSync { - public class MonitoredEpisodeSpecification : IDecisionEngineSpecification + public class MonitoredEpisodeSpecification : IDownloadDecisionEngineSpecification { private readonly Logger _logger; @@ -17,33 +17,33 @@ public MonitoredEpisodeSpecification(Logger logger) public SpecificationPriority Priority => SpecificationPriority.Default; public RejectionType Type => RejectionType.Permanent; - public virtual Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria) + public virtual DownloadSpecDecision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria) { if (searchCriteria != null) { if (!searchCriteria.MonitoredEpisodesOnly) { _logger.Debug("Skipping monitored check during search"); - return Decision.Accept(); + return DownloadSpecDecision.Accept(); } } if (!subject.Series.Monitored) { _logger.Debug("{0} is present in the DB but not tracked. Rejecting", subject.Series); - return Decision.Reject("Series is not monitored"); + return DownloadSpecDecision.Reject(DownloadRejectionReason.SeriesNotMonitored, "Series is not monitored"); } var monitoredCount = subject.Episodes.Count(episode => episode.Monitored); if (monitoredCount == subject.Episodes.Count) { - return Decision.Accept(); + return DownloadSpecDecision.Accept(); } if (subject.Episodes.Count == 1) { _logger.Debug("Episode is not monitored. Rejecting", monitoredCount, subject.Episodes.Count); - return Decision.Reject("Episode is not monitored"); + return DownloadSpecDecision.Reject(DownloadRejectionReason.EpisodeNotMonitored, "Episode is not monitored"); } if (monitoredCount == 0) @@ -55,7 +55,7 @@ public virtual Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase _logger.Debug("Only {0}/{1} episodes in the release are monitored. Rejecting", monitoredCount, subject.Episodes.Count); } - return Decision.Reject("One or more episodes is not monitored"); + return DownloadSpecDecision.Reject(DownloadRejectionReason.EpisodeNotMonitored, "One or more episodes is not monitored"); } } } diff --git a/src/NzbDrone.Core/DecisionEngine/Specifications/RssSync/ProperSpecification.cs b/src/NzbDrone.Core/DecisionEngine/Specifications/RssSync/ProperSpecification.cs index d2e843c90..ea7fc99ab 100644 --- a/src/NzbDrone.Core/DecisionEngine/Specifications/RssSync/ProperSpecification.cs +++ b/src/NzbDrone.Core/DecisionEngine/Specifications/RssSync/ProperSpecification.cs @@ -8,7 +8,7 @@ namespace NzbDrone.Core.DecisionEngine.Specifications.RssSync { - public class ProperSpecification : IDecisionEngineSpecification + public class ProperSpecification : IDownloadDecisionEngineSpecification { private readonly UpgradableSpecification _upgradableSpecification; private readonly IConfigService _configService; @@ -24,11 +24,11 @@ public ProperSpecification(UpgradableSpecification upgradableSpecification, ICon public SpecificationPriority Priority => SpecificationPriority.Default; public RejectionType Type => RejectionType.Permanent; - public virtual Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria) + public virtual DownloadSpecDecision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria) { if (searchCriteria != null) { - return Decision.Accept(); + return DownloadSpecDecision.Accept(); } var downloadPropersAndRepacks = _configService.DownloadPropersAndRepacks; @@ -36,7 +36,7 @@ public virtual Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase if (downloadPropersAndRepacks == ProperDownloadTypes.DoNotPrefer) { _logger.Debug("Propers are not preferred, skipping check"); - return Decision.Accept(); + return DownloadSpecDecision.Accept(); } foreach (var file in subject.Episodes.Where(c => c.EpisodeFileId != 0).Select(c => c.EpisodeFile.Value)) @@ -46,18 +46,18 @@ public virtual Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase if (downloadPropersAndRepacks == ProperDownloadTypes.DoNotUpgrade) { _logger.Debug("Auto downloading of propers is disabled"); - return Decision.Reject("Proper downloading is disabled"); + return DownloadSpecDecision.Reject(DownloadRejectionReason.PropersDisabled, "Proper downloading is disabled"); } if (file.DateAdded < DateTime.Today.AddDays(-7)) { _logger.Debug("Proper for old file, rejecting: {0}", subject); - return Decision.Reject("Proper for old file"); + return DownloadSpecDecision.Reject(DownloadRejectionReason.ProperForOldFile, "Proper for old file"); } } } - return Decision.Accept(); + return DownloadSpecDecision.Accept(); } } } diff --git a/src/NzbDrone.Core/DecisionEngine/Specifications/SameEpisodesGrabSpecification.cs b/src/NzbDrone.Core/DecisionEngine/Specifications/SameEpisodesGrabSpecification.cs index 0ef769d01..7bddcd175 100644 --- a/src/NzbDrone.Core/DecisionEngine/Specifications/SameEpisodesGrabSpecification.cs +++ b/src/NzbDrone.Core/DecisionEngine/Specifications/SameEpisodesGrabSpecification.cs @@ -4,7 +4,7 @@ namespace NzbDrone.Core.DecisionEngine.Specifications { - public class SameEpisodesGrabSpecification : IDecisionEngineSpecification + public class SameEpisodesGrabSpecification : IDownloadDecisionEngineSpecification { private readonly SameEpisodesSpecification _sameEpisodesSpecification; private readonly Logger _logger; @@ -18,15 +18,15 @@ public SameEpisodesGrabSpecification(SameEpisodesSpecification sameEpisodesSpeci public SpecificationPriority Priority => SpecificationPriority.Default; public RejectionType Type => RejectionType.Permanent; - public virtual Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria) + public virtual DownloadSpecDecision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria) { if (_sameEpisodesSpecification.IsSatisfiedBy(subject.Episodes)) { - return Decision.Accept(); + return DownloadSpecDecision.Accept(); } _logger.Debug("Episode file on disk contains more episodes than this release contains"); - return Decision.Reject("Episode file on disk contains more episodes than this release contains"); + return DownloadSpecDecision.Reject(DownloadRejectionReason.ExistingFileHasMoreEpisodes, "Episode file on disk contains more episodes than this release contains"); } } } diff --git a/src/NzbDrone.Core/DecisionEngine/Specifications/SceneMappingSpecification.cs b/src/NzbDrone.Core/DecisionEngine/Specifications/SceneMappingSpecification.cs index 04bb2c712..271103b32 100644 --- a/src/NzbDrone.Core/DecisionEngine/Specifications/SceneMappingSpecification.cs +++ b/src/NzbDrone.Core/DecisionEngine/Specifications/SceneMappingSpecification.cs @@ -5,7 +5,7 @@ namespace NzbDrone.Core.DecisionEngine.Specifications { - public class SceneMappingSpecification : IDecisionEngineSpecification + public class SceneMappingSpecification : IDownloadDecisionEngineSpecification { private readonly Logger _logger; @@ -17,18 +17,18 @@ public SceneMappingSpecification(Logger logger) public SpecificationPriority Priority => SpecificationPriority.Default; public RejectionType Type => RejectionType.Temporary; // Temporary till there's a mapping - public Decision IsSatisfiedBy(RemoteEpisode remoteEpisode, SearchCriteriaBase searchCriteria) + public DownloadSpecDecision IsSatisfiedBy(RemoteEpisode remoteEpisode, SearchCriteriaBase searchCriteria) { if (remoteEpisode.SceneMapping == null) { _logger.Debug("No applicable scene mapping, skipping."); - return Decision.Accept(); + return DownloadSpecDecision.Accept(); } if (remoteEpisode.SceneMapping.SceneOrigin.IsNullOrWhiteSpace()) { _logger.Debug("No explicit scene origin in scene mapping."); - return Decision.Accept(); + return DownloadSpecDecision.Accept(); } var split = remoteEpisode.SceneMapping.SceneOrigin.Split(':'); @@ -50,11 +50,11 @@ public Decision IsSatisfiedBy(RemoteEpisode remoteEpisode, SearchCriteriaBase se if (remoteEpisode.SceneMapping.Comment.IsNotNullOrWhiteSpace()) { - return Decision.Reject("{0} has ambiguous numbering"); + return DownloadSpecDecision.Reject(DownloadRejectionReason.AmbiguousNumbering, "{0} has ambiguous numbering"); } else { - return Decision.Reject("Ambiguous numbering"); + return DownloadSpecDecision.Reject(DownloadRejectionReason.AmbiguousNumbering, "Ambiguous numbering"); } } @@ -65,7 +65,7 @@ public Decision IsSatisfiedBy(RemoteEpisode remoteEpisode, SearchCriteriaBase se _logger.Debug("SceneMapping origin is explicitly unknown, unsure what numbering scheme it uses but '{0}' will be assumed. Provide full release title to Sonarr/TheXEM team.", type); } - return Decision.Accept(); + return DownloadSpecDecision.Accept(); } } } diff --git a/src/NzbDrone.Core/DecisionEngine/Specifications/Search/EpisodeRequestedSpecification.cs b/src/NzbDrone.Core/DecisionEngine/Specifications/Search/EpisodeRequestedSpecification.cs index 65677471d..4d94ccb72 100644 --- a/src/NzbDrone.Core/DecisionEngine/Specifications/Search/EpisodeRequestedSpecification.cs +++ b/src/NzbDrone.Core/DecisionEngine/Specifications/Search/EpisodeRequestedSpecification.cs @@ -5,7 +5,7 @@ namespace NzbDrone.Core.DecisionEngine.Specifications.Search { - public class EpisodeRequestedSpecification : IDecisionEngineSpecification + public class EpisodeRequestedSpecification : IDownloadDecisionEngineSpecification { private readonly Logger _logger; @@ -17,11 +17,11 @@ public EpisodeRequestedSpecification(Logger logger) public SpecificationPriority Priority => SpecificationPriority.Default; public RejectionType Type => RejectionType.Permanent; - public Decision IsSatisfiedBy(RemoteEpisode remoteEpisode, SearchCriteriaBase searchCriteria) + public DownloadSpecDecision IsSatisfiedBy(RemoteEpisode remoteEpisode, SearchCriteriaBase searchCriteria) { if (searchCriteria == null) { - return Decision.Accept(); + return DownloadSpecDecision.Accept(); } var criteriaEpisodes = searchCriteria.Episodes.Select(v => v.Id).ToList(); @@ -37,20 +37,20 @@ public Decision IsSatisfiedBy(RemoteEpisode remoteEpisode, SearchCriteriaBase se if (episodes.Count > 1) { - return Decision.Reject($"Episode wasn't requested: {episodes.First().SeasonNumber}x{episodes.First().EpisodeNumber}-{episodes.Last().EpisodeNumber}"); + return DownloadSpecDecision.Reject(DownloadRejectionReason.WrongEpisode, $"Episode wasn't requested: {episodes.First().SeasonNumber}x{episodes.First().EpisodeNumber}-{episodes.Last().EpisodeNumber}"); } else { - return Decision.Reject($"Episode wasn't requested: {episodes.First().SeasonNumber}x{episodes.First().EpisodeNumber}"); + return DownloadSpecDecision.Reject(DownloadRejectionReason.WrongEpisode, $"Episode wasn't requested: {episodes.First().SeasonNumber}x{episodes.First().EpisodeNumber}"); } } else { - return Decision.Reject("Episode wasn't requested"); + return DownloadSpecDecision.Reject(DownloadRejectionReason.WrongEpisode, "Episode wasn't requested"); } } - return Decision.Accept(); + return DownloadSpecDecision.Accept(); } } } diff --git a/src/NzbDrone.Core/DecisionEngine/Specifications/Search/SeasonMatchSpecification.cs b/src/NzbDrone.Core/DecisionEngine/Specifications/Search/SeasonMatchSpecification.cs index 1acfd44f9..f89c41988 100644 --- a/src/NzbDrone.Core/DecisionEngine/Specifications/Search/SeasonMatchSpecification.cs +++ b/src/NzbDrone.Core/DecisionEngine/Specifications/Search/SeasonMatchSpecification.cs @@ -5,7 +5,7 @@ namespace NzbDrone.Core.DecisionEngine.Specifications.Search { - public class SeasonMatchSpecification : IDecisionEngineSpecification + public class SeasonMatchSpecification : IDownloadDecisionEngineSpecification { private readonly Logger _logger; private readonly ISceneMappingService _sceneMappingService; @@ -19,26 +19,26 @@ public SeasonMatchSpecification(ISceneMappingService sceneMappingService, Logger public SpecificationPriority Priority => SpecificationPriority.Default; public RejectionType Type => RejectionType.Permanent; - public Decision IsSatisfiedBy(RemoteEpisode remoteEpisode, SearchCriteriaBase searchCriteria) + public DownloadSpecDecision IsSatisfiedBy(RemoteEpisode remoteEpisode, SearchCriteriaBase searchCriteria) { if (searchCriteria == null) { - return Decision.Accept(); + return DownloadSpecDecision.Accept(); } var singleEpisodeSpec = searchCriteria as SeasonSearchCriteria; if (singleEpisodeSpec == null) { - return Decision.Accept(); + return DownloadSpecDecision.Accept(); } if (singleEpisodeSpec.SeasonNumber != remoteEpisode.ParsedEpisodeInfo.SeasonNumber) { _logger.Debug("Season number does not match searched season number, skipping."); - return Decision.Reject("Wrong season"); + return DownloadSpecDecision.Reject(DownloadRejectionReason.WrongSeason, "Wrong season"); } - return Decision.Accept(); + return DownloadSpecDecision.Accept(); } } } diff --git a/src/NzbDrone.Core/DecisionEngine/Specifications/Search/SeriesSpecification.cs b/src/NzbDrone.Core/DecisionEngine/Specifications/Search/SeriesSpecification.cs index 07afbaada..e1796eb41 100644 --- a/src/NzbDrone.Core/DecisionEngine/Specifications/Search/SeriesSpecification.cs +++ b/src/NzbDrone.Core/DecisionEngine/Specifications/Search/SeriesSpecification.cs @@ -4,7 +4,7 @@ namespace NzbDrone.Core.DecisionEngine.Specifications.Search { - public class SeriesSpecification : IDecisionEngineSpecification + public class SeriesSpecification : IDownloadDecisionEngineSpecification { private readonly Logger _logger; @@ -16,11 +16,11 @@ public SeriesSpecification(Logger logger) public SpecificationPriority Priority => SpecificationPriority.Default; public RejectionType Type => RejectionType.Permanent; - public Decision IsSatisfiedBy(RemoteEpisode remoteEpisode, SearchCriteriaBase searchCriteria) + public DownloadSpecDecision IsSatisfiedBy(RemoteEpisode remoteEpisode, SearchCriteriaBase searchCriteria) { if (searchCriteria == null) { - return Decision.Accept(); + return DownloadSpecDecision.Accept(); } _logger.Debug("Checking if series matches searched series"); @@ -28,10 +28,10 @@ public Decision IsSatisfiedBy(RemoteEpisode remoteEpisode, SearchCriteriaBase se if (remoteEpisode.Series.Id != searchCriteria.Series.Id) { _logger.Debug("Series {0} does not match {1}", remoteEpisode.Series, searchCriteria.Series); - return Decision.Reject("Wrong series"); + return DownloadSpecDecision.Reject(DownloadRejectionReason.WrongSeries, "Wrong series"); } - return Decision.Accept(); + return DownloadSpecDecision.Accept(); } } } diff --git a/src/NzbDrone.Core/DecisionEngine/Specifications/Search/SingleEpisodeSearchMatchSpecification.cs b/src/NzbDrone.Core/DecisionEngine/Specifications/Search/SingleEpisodeSearchMatchSpecification.cs index 1aa995ef7..bd76be698 100644 --- a/src/NzbDrone.Core/DecisionEngine/Specifications/Search/SingleEpisodeSearchMatchSpecification.cs +++ b/src/NzbDrone.Core/DecisionEngine/Specifications/Search/SingleEpisodeSearchMatchSpecification.cs @@ -6,7 +6,7 @@ namespace NzbDrone.Core.DecisionEngine.Specifications.Search { - public class SingleEpisodeSearchMatchSpecification : IDecisionEngineSpecification + public class SingleEpisodeSearchMatchSpecification : IDownloadDecisionEngineSpecification { private readonly Logger _logger; private readonly ISceneMappingService _sceneMappingService; @@ -20,11 +20,11 @@ public SingleEpisodeSearchMatchSpecification(ISceneMappingService sceneMappingSe public SpecificationPriority Priority => SpecificationPriority.Default; public RejectionType Type => RejectionType.Permanent; - public Decision IsSatisfiedBy(RemoteEpisode remoteEpisode, SearchCriteriaBase searchCriteria) + public DownloadSpecDecision IsSatisfiedBy(RemoteEpisode remoteEpisode, SearchCriteriaBase searchCriteria) { if (searchCriteria == null) { - return Decision.Accept(); + return DownloadSpecDecision.Accept(); } var singleEpisodeSpec = searchCriteria as SingleEpisodeSearchCriteria; @@ -39,41 +39,41 @@ public Decision IsSatisfiedBy(RemoteEpisode remoteEpisode, SearchCriteriaBase se return IsSatisfiedBy(remoteEpisode, animeEpisodeSpec); } - return Decision.Accept(); + return DownloadSpecDecision.Accept(); } - private Decision IsSatisfiedBy(RemoteEpisode remoteEpisode, SingleEpisodeSearchCriteria singleEpisodeSpec) + private DownloadSpecDecision IsSatisfiedBy(RemoteEpisode remoteEpisode, SingleEpisodeSearchCriteria singleEpisodeSpec) { if (singleEpisodeSpec.SeasonNumber != remoteEpisode.ParsedEpisodeInfo.SeasonNumber) { _logger.Debug("Season number does not match searched season number, skipping."); - return Decision.Reject("Wrong season"); + return DownloadSpecDecision.Reject(DownloadRejectionReason.WrongSeason, "Wrong season"); } if (!remoteEpisode.ParsedEpisodeInfo.EpisodeNumbers.Any()) { _logger.Debug("Full season result during single episode search, skipping."); - return Decision.Reject("Full season pack"); + return DownloadSpecDecision.Reject(DownloadRejectionReason.FullSeason, "Full season pack"); } if (!remoteEpisode.ParsedEpisodeInfo.EpisodeNumbers.Contains(singleEpisodeSpec.EpisodeNumber)) { _logger.Debug("Episode number does not match searched episode number, skipping."); - return Decision.Reject("Wrong episode"); + return DownloadSpecDecision.Reject(DownloadRejectionReason.WrongEpisode, "Wrong episode"); } - return Decision.Accept(); + return DownloadSpecDecision.Accept(); } - private Decision IsSatisfiedBy(RemoteEpisode remoteEpisode, AnimeEpisodeSearchCriteria animeEpisodeSpec) + private DownloadSpecDecision IsSatisfiedBy(RemoteEpisode remoteEpisode, AnimeEpisodeSearchCriteria animeEpisodeSpec) { if (remoteEpisode.ParsedEpisodeInfo.FullSeason && !animeEpisodeSpec.IsSeasonSearch) { _logger.Debug("Full season result during single episode search, skipping."); - return Decision.Reject("Full season pack"); + return DownloadSpecDecision.Reject(DownloadRejectionReason.FullSeason, "Full season pack"); } - return Decision.Accept(); + return DownloadSpecDecision.Accept(); } } } diff --git a/src/NzbDrone.Core/DecisionEngine/Specifications/SeasonPackOnlySpecification.cs b/src/NzbDrone.Core/DecisionEngine/Specifications/SeasonPackOnlySpecification.cs index 2934be358..12939332e 100644 --- a/src/NzbDrone.Core/DecisionEngine/Specifications/SeasonPackOnlySpecification.cs +++ b/src/NzbDrone.Core/DecisionEngine/Specifications/SeasonPackOnlySpecification.cs @@ -8,7 +8,7 @@ namespace NzbDrone.Core.DecisionEngine.Specifications { - public class SeasonPackOnlySpecification : IDecisionEngineSpecification + public class SeasonPackOnlySpecification : IDownloadDecisionEngineSpecification { private readonly Logger _logger; @@ -20,11 +20,11 @@ public SeasonPackOnlySpecification(Logger logger) public SpecificationPriority Priority => SpecificationPriority.Default; public RejectionType Type => RejectionType.Permanent; - public virtual Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria) + public virtual DownloadSpecDecision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria) { if (searchCriteria == null || searchCriteria.Episodes.Count == 1) { - return Decision.Accept(); + return DownloadSpecDecision.Accept(); } if (subject.Release.SeasonSearchMaximumSingleEpisodeAge > 0) @@ -37,12 +37,12 @@ public virtual Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase if (subset.Count > 0 && subset.Max(e => e.AirDateUtc).Value.Before(DateTime.UtcNow - TimeSpan.FromDays(subject.Release.SeasonSearchMaximumSingleEpisodeAge))) { _logger.Debug("Release {0}: last episode in this season aired more than {1} days ago, season pack required.", subject.Release.Title, subject.Release.SeasonSearchMaximumSingleEpisodeAge); - return Decision.Reject("Last episode in this season aired more than {0} days ago, season pack required.", subject.Release.SeasonSearchMaximumSingleEpisodeAge); + return DownloadSpecDecision.Reject(DownloadRejectionReason.NotSeasonPack, "Last episode in this season aired more than {0} days ago, season pack required.", subject.Release.SeasonSearchMaximumSingleEpisodeAge); } } } - return Decision.Accept(); + return DownloadSpecDecision.Accept(); } } } diff --git a/src/NzbDrone.Core/DecisionEngine/Specifications/SplitEpisodeSpecification.cs b/src/NzbDrone.Core/DecisionEngine/Specifications/SplitEpisodeSpecification.cs index fef3be741..c9bff29a3 100644 --- a/src/NzbDrone.Core/DecisionEngine/Specifications/SplitEpisodeSpecification.cs +++ b/src/NzbDrone.Core/DecisionEngine/Specifications/SplitEpisodeSpecification.cs @@ -4,7 +4,7 @@ namespace NzbDrone.Core.DecisionEngine.Specifications { - public class SplitEpisodeSpecification : IDecisionEngineSpecification + public class SplitEpisodeSpecification : IDownloadDecisionEngineSpecification { private readonly Logger _logger; @@ -16,15 +16,15 @@ public SplitEpisodeSpecification(Logger logger) public SpecificationPriority Priority => SpecificationPriority.Default; public RejectionType Type => RejectionType.Permanent; - public virtual Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria) + public virtual DownloadSpecDecision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria) { if (subject.ParsedEpisodeInfo.IsSplitEpisode) { _logger.Debug("Split episode release {0} rejected. Not supported", subject.Release.Title); - return Decision.Reject("Split episode releases are not supported"); + return DownloadSpecDecision.Reject(DownloadRejectionReason.SplitEpisode, "Split episode releases are not supported"); } - return Decision.Accept(); + return DownloadSpecDecision.Accept(); } } } diff --git a/src/NzbDrone.Core/DecisionEngine/Specifications/TorrentSeedingSpecification.cs b/src/NzbDrone.Core/DecisionEngine/Specifications/TorrentSeedingSpecification.cs index be0de3fdb..ee6df440b 100644 --- a/src/NzbDrone.Core/DecisionEngine/Specifications/TorrentSeedingSpecification.cs +++ b/src/NzbDrone.Core/DecisionEngine/Specifications/TorrentSeedingSpecification.cs @@ -6,7 +6,7 @@ namespace NzbDrone.Core.DecisionEngine.Specifications { - public class TorrentSeedingSpecification : IDecisionEngineSpecification + public class TorrentSeedingSpecification : IDownloadDecisionEngineSpecification { private readonly IIndexerFactory _indexerFactory; private readonly Logger _logger; @@ -20,13 +20,13 @@ public TorrentSeedingSpecification(IIndexerFactory indexerFactory, Logger logger public SpecificationPriority Priority => SpecificationPriority.Default; public RejectionType Type => RejectionType.Permanent; - public Decision IsSatisfiedBy(RemoteEpisode remoteEpisode, SearchCriteriaBase searchCriteria) + public DownloadSpecDecision IsSatisfiedBy(RemoteEpisode remoteEpisode, SearchCriteriaBase searchCriteria) { var torrentInfo = remoteEpisode.Release as TorrentInfo; if (torrentInfo == null || torrentInfo.IndexerId == 0) { - return Decision.Accept(); + return DownloadSpecDecision.Accept(); } IndexerDefinition indexer; @@ -37,7 +37,7 @@ public Decision IsSatisfiedBy(RemoteEpisode remoteEpisode, SearchCriteriaBase se catch (ModelNotFoundException) { _logger.Debug("Indexer with id {0} does not exist, skipping seeders check", torrentInfo.IndexerId); - return Decision.Accept(); + return DownloadSpecDecision.Accept(); } var torrentIndexerSettings = indexer.Settings as ITorrentIndexerSettings; @@ -49,11 +49,11 @@ public Decision IsSatisfiedBy(RemoteEpisode remoteEpisode, SearchCriteriaBase se if (torrentInfo.Seeders.HasValue && torrentInfo.Seeders.Value < minimumSeeders) { _logger.Debug("Not enough seeders: {0}. Minimum seeders: {1}", torrentInfo.Seeders, minimumSeeders); - return Decision.Reject("Not enough seeders: {0}. Minimum seeders: {1}", torrentInfo.Seeders, minimumSeeders); + return DownloadSpecDecision.Reject(DownloadRejectionReason.MinimumSeeders, "Not enough seeders: {0}. Minimum seeders: {1}", torrentInfo.Seeders, minimumSeeders); } } - return Decision.Accept(); + return DownloadSpecDecision.Accept(); } } } diff --git a/src/NzbDrone.Core/DecisionEngine/Specifications/UpgradeAllowedSpecification.cs b/src/NzbDrone.Core/DecisionEngine/Specifications/UpgradeAllowedSpecification.cs index f83e137cc..9de0cead4 100644 --- a/src/NzbDrone.Core/DecisionEngine/Specifications/UpgradeAllowedSpecification.cs +++ b/src/NzbDrone.Core/DecisionEngine/Specifications/UpgradeAllowedSpecification.cs @@ -6,7 +6,7 @@ namespace NzbDrone.Core.DecisionEngine.Specifications { - public class UpgradeAllowedSpecification : IDecisionEngineSpecification + public class UpgradeAllowedSpecification : IDownloadDecisionEngineSpecification { private readonly UpgradableSpecification _upgradableSpecification; private readonly ICustomFormatCalculationService _formatService; @@ -24,7 +24,7 @@ public UpgradeAllowedSpecification(UpgradableSpecification upgradableSpecificati public SpecificationPriority Priority => SpecificationPriority.Default; public RejectionType Type => RejectionType.Permanent; - public virtual Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria) + public virtual DownloadSpecDecision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria) { var qualityProfile = subject.Series.QualityProfile.Value; @@ -48,11 +48,11 @@ public virtual Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase { _logger.Debug("Upgrading is not allowed by the quality profile"); - return Decision.Reject("Existing file and the Quality profile does not allow upgrades"); + return DownloadSpecDecision.Reject(DownloadRejectionReason.QualityUpgradesDisabled, "Existing file and the Quality profile does not allow upgrades"); } } - return Decision.Accept(); + return DownloadSpecDecision.Accept(); } } } diff --git a/src/NzbDrone.Core/DecisionEngine/Specifications/UpgradeDiskSpecification.cs b/src/NzbDrone.Core/DecisionEngine/Specifications/UpgradeDiskSpecification.cs index c7afa3351..0185d4d87 100644 --- a/src/NzbDrone.Core/DecisionEngine/Specifications/UpgradeDiskSpecification.cs +++ b/src/NzbDrone.Core/DecisionEngine/Specifications/UpgradeDiskSpecification.cs @@ -6,7 +6,7 @@ namespace NzbDrone.Core.DecisionEngine.Specifications { - public class UpgradeDiskSpecification : IDecisionEngineSpecification + public class UpgradeDiskSpecification : IDownloadDecisionEngineSpecification { private readonly UpgradableSpecification _upgradableSpecification; private readonly ICustomFormatCalculationService _formatService; @@ -24,7 +24,7 @@ public UpgradeDiskSpecification(UpgradableSpecification upgradableSpecification, public SpecificationPriority Priority => SpecificationPriority.Default; public RejectionType Type => RejectionType.Permanent; - public virtual Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria) + public virtual DownloadSpecDecision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria) { var qualityProfile = subject.Series.QualityProfile.Value; @@ -48,7 +48,7 @@ public virtual Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase var cutoff = qualityProfile.UpgradeAllowed ? qualityProfile.Cutoff : qualityProfile.FirststAllowedQuality().Id; var qualityCutoff = qualityProfile.Items[qualityProfile.GetIndex(cutoff).Index]; - return Decision.Reject("Existing file meets cutoff: {0}", qualityCutoff); + return DownloadSpecDecision.Reject(DownloadRejectionReason.DiskCutoffMet, "Existing file meets cutoff: {0}", qualityCutoff); } var customFormats = _formatService.ParseCustomFormat(file); @@ -65,26 +65,26 @@ public virtual Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase continue; case UpgradeableRejectReason.BetterQuality: - return Decision.Reject("Existing file on disk is of equal or higher preference: {0}", file.Quality); + return DownloadSpecDecision.Reject(DownloadRejectionReason.DiskHigherPreference, "Existing file on disk is of equal or higher preference: {0}", file.Quality); case UpgradeableRejectReason.BetterRevision: - return Decision.Reject("Existing file on disk is of equal or higher revision: {0}", file.Quality.Revision); + return DownloadSpecDecision.Reject(DownloadRejectionReason.DiskHigherRevision, "Existing file on disk is of equal or higher revision: {0}", file.Quality.Revision); case UpgradeableRejectReason.QualityCutoff: - return Decision.Reject("Existing file on disk meets quality cutoff: {0}", qualityProfile.Items[qualityProfile.GetIndex(qualityProfile.Cutoff).Index]); + return DownloadSpecDecision.Reject(DownloadRejectionReason.DiskCutoffMet, "Existing file on disk meets quality cutoff: {0}", qualityProfile.Items[qualityProfile.GetIndex(qualityProfile.Cutoff).Index]); case UpgradeableRejectReason.CustomFormatCutoff: - return Decision.Reject("Existing file on disk meets Custom Format cutoff: {0}", qualityProfile.CutoffFormatScore); + return DownloadSpecDecision.Reject(DownloadRejectionReason.DiskCustomFormatCutoffMet, "Existing file on disk meets Custom Format cutoff: {0}", qualityProfile.CutoffFormatScore); case UpgradeableRejectReason.CustomFormatScore: - return Decision.Reject("Existing file on disk has a equal or higher Custom Format score: {0}", qualityProfile.CalculateCustomFormatScore(customFormats)); + return DownloadSpecDecision.Reject(DownloadRejectionReason.DiskCustomFormatScore, "Existing file on disk has a equal or higher Custom Format score: {0}", qualityProfile.CalculateCustomFormatScore(customFormats)); case UpgradeableRejectReason.MinCustomFormatScore: - return Decision.Reject("Existing file on disk has Custom Format score within Custom Format score increment: {0}", qualityProfile.MinUpgradeFormatScore); + return DownloadSpecDecision.Reject(DownloadRejectionReason.DiskCustomFormatScoreIncrement, "Existing file on disk has Custom Format score within Custom Format score increment: {0}", qualityProfile.MinUpgradeFormatScore); } } - return Decision.Accept(); + return DownloadSpecDecision.Accept(); } } } diff --git a/src/NzbDrone.Core/MediaFiles/DownloadedEpisodesImportService.cs b/src/NzbDrone.Core/MediaFiles/DownloadedEpisodesImportService.cs index f7afddebc..98d55064b 100644 --- a/src/NzbDrone.Core/MediaFiles/DownloadedEpisodesImportService.cs +++ b/src/NzbDrone.Core/MediaFiles/DownloadedEpisodesImportService.cs @@ -6,7 +6,6 @@ using NzbDrone.Common.Disk; using NzbDrone.Common.EnvironmentInfo; using NzbDrone.Common.Extensions; -using NzbDrone.Core.DecisionEngine; using NzbDrone.Core.Download; using NzbDrone.Core.MediaFiles.EpisodeImport; using NzbDrone.Core.Parser; @@ -178,7 +177,7 @@ private List ProcessFolder(DirectoryInfo directoryInfo, ImportMode _logger.Warn("Unable to process folder that is mapped to an existing series"); return new List { - RejectionResult("Import path is mapped to a series folder") + RejectionResult(ImportRejectionReason.SeriesFolder, "Import path is mapped to a series folder") }; } @@ -255,7 +254,7 @@ private List ProcessFile(FileInfo fileInfo, ImportMode importMode, return new List { - new ImportResult(new ImportDecision(new LocalEpisode { Path = fileInfo.FullName }, new Rejection("Invalid video file, filename starts with '._'")), "Invalid video file, filename starts with '._'") + new ImportResult(new ImportDecision(new LocalEpisode { Path = fileInfo.FullName }, new ImportRejection(ImportRejectionReason.InvalidFilePath, "Invalid video file, filename starts with '._'")), "Invalid video file, filename starts with '._'") }; } @@ -268,7 +267,7 @@ private List ProcessFile(FileInfo fileInfo, ImportMode importMode, return new List { new ImportResult(new ImportDecision(new LocalEpisode { Path = fileInfo.FullName }, - new Rejection($"Invalid video file, unsupported extension: '{extension}'")), + new ImportRejection(ImportRejectionReason.UnsupportedExtension, $"Invalid video file, unsupported extension: '{extension}'")), $"Invalid video file, unsupported extension: '{extension}'") }; } @@ -300,19 +299,19 @@ private string GetCleanedUpFolderName(string folder) private ImportResult FileIsLockedResult(string videoFile) { _logger.Debug("[{0}] is currently locked by another process, skipping", videoFile); - return new ImportResult(new ImportDecision(new LocalEpisode { Path = videoFile }, new Rejection("Locked file, try again later")), "Locked file, try again later"); + return new ImportResult(new ImportDecision(new LocalEpisode { Path = videoFile }, new ImportRejection(ImportRejectionReason.FileLocked, "Locked file, try again later")), "Locked file, try again later"); } private ImportResult UnknownSeriesResult(string message, string videoFile = null) { var localEpisode = videoFile == null ? null : new LocalEpisode { Path = videoFile }; - return new ImportResult(new ImportDecision(localEpisode, new Rejection("Unknown Series")), message); + return new ImportResult(new ImportDecision(localEpisode, new ImportRejection(ImportRejectionReason.UnknownSeries, "Unknown Series")), message); } - private ImportResult RejectionResult(string message) + private ImportResult RejectionResult(ImportRejectionReason reason, string message) { - return new ImportResult(new ImportDecision(null, new Rejection(message)), message); + return new ImportResult(new ImportDecision(null, new ImportRejection(reason, message)), message); } private ImportResult CheckEmptyResultForIssue(string folder) @@ -321,12 +320,12 @@ private ImportResult CheckEmptyResultForIssue(string folder) if (files.Any(file => FileExtensions.ExecutableExtensions.Contains(Path.GetExtension(file)))) { - return RejectionResult("Caution: Found executable file"); + return RejectionResult(ImportRejectionReason.ExecutableFile, "Caution: Found executable file"); } if (files.Any(file => FileExtensions.ArchiveExtensions.Contains(Path.GetExtension(file)))) { - return RejectionResult("Found archive file, might need to be extracted"); + return RejectionResult(ImportRejectionReason.ArchiveFile, "Found archive file, might need to be extracted"); } return null; diff --git a/src/NzbDrone.Core/MediaFiles/EpisodeImport/IImportDecisionEngineSpecification.cs b/src/NzbDrone.Core/MediaFiles/EpisodeImport/IImportDecisionEngineSpecification.cs index 9778664cb..68378ebc5 100644 --- a/src/NzbDrone.Core/MediaFiles/EpisodeImport/IImportDecisionEngineSpecification.cs +++ b/src/NzbDrone.Core/MediaFiles/EpisodeImport/IImportDecisionEngineSpecification.cs @@ -1,11 +1,10 @@ -using NzbDrone.Core.DecisionEngine; -using NzbDrone.Core.Download; +using NzbDrone.Core.Download; using NzbDrone.Core.Parser.Model; namespace NzbDrone.Core.MediaFiles.EpisodeImport { public interface IImportDecisionEngineSpecification { - Decision IsSatisfiedBy(LocalEpisode localEpisode, DownloadClientItem downloadClientItem); + ImportSpecDecision IsSatisfiedBy(LocalEpisode localEpisode, DownloadClientItem downloadClientItem); } } diff --git a/src/NzbDrone.Core/MediaFiles/EpisodeImport/ImportApprovedEpisodes.cs b/src/NzbDrone.Core/MediaFiles/EpisodeImport/ImportApprovedEpisodes.cs index f5419dbf6..90f15a348 100644 --- a/src/NzbDrone.Core/MediaFiles/EpisodeImport/ImportApprovedEpisodes.cs +++ b/src/NzbDrone.Core/MediaFiles/EpisodeImport/ImportApprovedEpisodes.cs @@ -224,7 +224,7 @@ public List Import(List decisions, bool newDownloa // Adding all the rejected decisions importResults.AddRange(decisions.Where(c => !c.Approved) - .Select(d => new ImportResult(d, d.Rejections.Select(r => r.Reason).ToArray()))); + .Select(d => new ImportResult(d, d.Rejections.Select(r => r.Message).ToArray()))); return importResults; } diff --git a/src/NzbDrone.Core/MediaFiles/EpisodeImport/ImportDecision.cs b/src/NzbDrone.Core/MediaFiles/EpisodeImport/ImportDecision.cs index 5e4e2ede2..8a0ad0034 100644 --- a/src/NzbDrone.Core/MediaFiles/EpisodeImport/ImportDecision.cs +++ b/src/NzbDrone.Core/MediaFiles/EpisodeImport/ImportDecision.cs @@ -1,7 +1,6 @@ using System.Collections.Generic; using System.Linq; using NzbDrone.Common.Extensions; -using NzbDrone.Core.DecisionEngine; using NzbDrone.Core.Parser.Model; namespace NzbDrone.Core.MediaFiles.EpisodeImport @@ -9,11 +8,11 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport public class ImportDecision { public LocalEpisode LocalEpisode { get; private set; } - public IEnumerable Rejections { get; private set; } + public IEnumerable Rejections { get; private set; } public bool Approved => Rejections.Empty(); - public ImportDecision(LocalEpisode localEpisode, params Rejection[] rejections) + public ImportDecision(LocalEpisode localEpisode, params ImportRejection[] rejections) { LocalEpisode = localEpisode; Rejections = rejections.ToList(); diff --git a/src/NzbDrone.Core/MediaFiles/EpisodeImport/ImportDecisionMaker.cs b/src/NzbDrone.Core/MediaFiles/EpisodeImport/ImportDecisionMaker.cs index 590b0f5d7..762a5a4f3 100644 --- a/src/NzbDrone.Core/MediaFiles/EpisodeImport/ImportDecisionMaker.cs +++ b/src/NzbDrone.Core/MediaFiles/EpisodeImport/ImportDecisionMaker.cs @@ -5,7 +5,6 @@ using NzbDrone.Common.Disk; using NzbDrone.Common.Extensions; using NzbDrone.Core.CustomFormats; -using NzbDrone.Core.DecisionEngine; using NzbDrone.Core.Download; using NzbDrone.Core.Download.TrackedDownloads; using NzbDrone.Core.MediaFiles.EpisodeImport.Aggregation; @@ -136,15 +135,15 @@ private ImportDecision GetDecision(LocalEpisode localEpisode, DownloadClientItem { if (IsPartialSeason(localEpisode)) { - decision = new ImportDecision(localEpisode, new Rejection("Partial season packs are not supported")); + decision = new ImportDecision(localEpisode, new ImportRejection(ImportRejectionReason.PartialSeason, "Partial season packs are not supported")); } else if (IsSeasonExtra(localEpisode)) { - decision = new ImportDecision(localEpisode, new Rejection("Extras are not supported")); + decision = new ImportDecision(localEpisode, new ImportRejection(ImportRejectionReason.SeasonExtra, "Extras are not supported")); } else { - decision = new ImportDecision(localEpisode, new Rejection("Invalid season or episode")); + decision = new ImportDecision(localEpisode, new ImportRejection(ImportRejectionReason.InvalidSeasonOrEpisode, "Invalid season or episode")); } } else @@ -167,13 +166,13 @@ private ImportDecision GetDecision(LocalEpisode localEpisode, DownloadClientItem } catch (AugmentingFailedException) { - decision = new ImportDecision(localEpisode, new Rejection("Unable to parse file")); + decision = new ImportDecision(localEpisode, new ImportRejection(ImportRejectionReason.UnableToParse, "Unable to parse file")); } catch (Exception ex) { _logger.Error(ex, "Couldn't import file. {0}", localEpisode.Path); - decision = new ImportDecision(localEpisode, new Rejection("Unexpected error processing file")); + decision = new ImportDecision(localEpisode, new ImportRejection(ImportRejectionReason.Error, "Unexpected error processing file")); } if (decision == null) @@ -192,7 +191,7 @@ private ImportDecision GetDecision(LocalEpisode localEpisode, DownloadClientItem return decision; } - private Rejection EvaluateSpec(IImportDecisionEngineSpecification spec, LocalEpisode localEpisode, DownloadClientItem downloadClientItem) + private ImportRejection EvaluateSpec(IImportDecisionEngineSpecification spec, LocalEpisode localEpisode, DownloadClientItem downloadClientItem) { try { @@ -200,7 +199,7 @@ private Rejection EvaluateSpec(IImportDecisionEngineSpecification spec, LocalEpi if (!result.Accepted) { - return new Rejection(result.Reason); + return new ImportRejection(result.Reason, result.Message); } } catch (Exception e) @@ -208,7 +207,7 @@ private Rejection EvaluateSpec(IImportDecisionEngineSpecification spec, LocalEpi // e.Data.Add("report", remoteEpisode.Report.ToJson()); // e.Data.Add("parsed", remoteEpisode.ParsedEpisodeInfo.ToJson()); _logger.Error(e, "Couldn't evaluate decision on {0}", localEpisode.Path); - return new Rejection($"{spec.GetType().Name}: {e.Message}"); + return new ImportRejection(ImportRejectionReason.DecisionError, $"{spec.GetType().Name}: {e.Message}"); } return null; diff --git a/src/NzbDrone.Core/MediaFiles/EpisodeImport/ImportRejection.cs b/src/NzbDrone.Core/MediaFiles/EpisodeImport/ImportRejection.cs new file mode 100644 index 000000000..0970873a3 --- /dev/null +++ b/src/NzbDrone.Core/MediaFiles/EpisodeImport/ImportRejection.cs @@ -0,0 +1,11 @@ +using NzbDrone.Core.DecisionEngine; + +namespace NzbDrone.Core.MediaFiles.EpisodeImport; + +public class ImportRejection : Rejection +{ + public ImportRejection(ImportRejectionReason reason, string message, RejectionType type = RejectionType.Permanent) + : base(reason, message, type) + { + } +} diff --git a/src/NzbDrone.Core/MediaFiles/EpisodeImport/ImportRejectionReason.cs b/src/NzbDrone.Core/MediaFiles/EpisodeImport/ImportRejectionReason.cs new file mode 100644 index 000000000..f13f5d2a1 --- /dev/null +++ b/src/NzbDrone.Core/MediaFiles/EpisodeImport/ImportRejectionReason.cs @@ -0,0 +1,38 @@ +namespace NzbDrone.Core.MediaFiles.EpisodeImport; + +public enum ImportRejectionReason +{ + Unknown, + FileLocked, + UnknownSeries, + ExecutableFile, + ArchiveFile, + SeriesFolder, + InvalidFilePath, + UnsupportedExtension, + PartialSeason, + SeasonExtra, + InvalidSeasonOrEpisode, + UnableToParse, + Error, + DecisionError, + NoEpisodes, + MissingAbsoluteEpisodeNumber, + EpisodeAlreadyImported, + TitleMissing, + TitleTba, + MinimumFreeSpace, + FullSeason, + NoAudio, + EpisodeUnexpected, + EpisodeNotFoundInRelease, + Sample, + SampleIndeterminate, + Unpacking, + ExistingFileHasMoreEpisodes, + SplitEpisode, + UnverifiedSceneMapping, + NotQualityUpgrade, + NotRevisionUpgrade, + NotCustomFormatUpgrade +} diff --git a/src/NzbDrone.Core/MediaFiles/EpisodeImport/ImportSpecDecision.cs b/src/NzbDrone.Core/MediaFiles/EpisodeImport/ImportSpecDecision.cs new file mode 100644 index 000000000..d4f5c5c2f --- /dev/null +++ b/src/NzbDrone.Core/MediaFiles/EpisodeImport/ImportSpecDecision.cs @@ -0,0 +1,34 @@ +namespace NzbDrone.Core.MediaFiles.EpisodeImport +{ + public class ImportSpecDecision + { + public bool Accepted { get; private set; } + public ImportRejectionReason Reason { get; set; } + public string Message { get; private set; } + + private static readonly ImportSpecDecision AcceptDecision = new () { Accepted = true }; + private ImportSpecDecision() + { + } + + public static ImportSpecDecision Accept() + { + return AcceptDecision; + } + + public static ImportSpecDecision Reject(ImportRejectionReason reason, string message, params object[] args) + { + return Reject(reason, string.Format(message, args)); + } + + public static ImportSpecDecision Reject(ImportRejectionReason reason, string message) + { + return new ImportSpecDecision + { + Accepted = false, + Reason = reason, + Message = message + }; + } + } +} diff --git a/src/NzbDrone.Core/MediaFiles/EpisodeImport/Manual/ManualImportItem.cs b/src/NzbDrone.Core/MediaFiles/EpisodeImport/Manual/ManualImportItem.cs index 9f690474b..4e4052b6c 100644 --- a/src/NzbDrone.Core/MediaFiles/EpisodeImport/Manual/ManualImportItem.cs +++ b/src/NzbDrone.Core/MediaFiles/EpisodeImport/Manual/ManualImportItem.cs @@ -1,6 +1,5 @@ using System.Collections.Generic; using NzbDrone.Core.CustomFormats; -using NzbDrone.Core.DecisionEngine; using NzbDrone.Core.Languages; using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Qualities; @@ -27,7 +26,7 @@ public class ManualImportItem public int CustomFormatScore { get; set; } public int IndexerFlags { get; set; } public ReleaseType ReleaseType { get; set; } - public IEnumerable Rejections { get; set; } + public IEnumerable Rejections { get; set; } public ManualImportItem() { diff --git a/src/NzbDrone.Core/MediaFiles/EpisodeImport/Manual/ManualImportService.cs b/src/NzbDrone.Core/MediaFiles/EpisodeImport/Manual/ManualImportService.cs index b3112bc24..aee6b97f4 100644 --- a/src/NzbDrone.Core/MediaFiles/EpisodeImport/Manual/ManualImportService.cs +++ b/src/NzbDrone.Core/MediaFiles/EpisodeImport/Manual/ManualImportService.cs @@ -7,7 +7,6 @@ using NzbDrone.Common.Extensions; using NzbDrone.Common.Instrumentation.Extensions; using NzbDrone.Core.CustomFormats; -using NzbDrone.Core.DecisionEngine; using NzbDrone.Core.Download; using NzbDrone.Core.Download.TrackedDownloads; using NzbDrone.Core.Languages; @@ -104,7 +103,7 @@ public List GetMediaFiles(int seriesId, int? seasonNumber) Quality = new QualityModel(Quality.Unknown), Languages = new List { Language.Unknown }, Size = _diskProvider.GetFileSize(file), - Rejections = Enumerable.Empty() + Rejections = Enumerable.Empty() })); } @@ -226,7 +225,7 @@ public ManualImportItem ReprocessItem(string path, string downloadId, int series ReleaseType = releaseType }; - return MapItem(new ImportDecision(localEpisode, new Rejection("Episodes not selected")), rootFolder, downloadId, null); + return MapItem(new ImportDecision(localEpisode, new ImportRejection(ImportRejectionReason.NoEpisodes, "Episodes not selected")), rootFolder, downloadId, null); } return ProcessFile(rootFolder, rootFolder, path, downloadId, series); @@ -338,7 +337,7 @@ private ManualImportItem ProcessFile(string rootFolder, string baseFolder, strin localEpisode.Size = _diskProvider.GetFileSize(file); return MapItem(new ImportDecision(localEpisode, - new Rejection("Unknown Series")), + new ImportRejection(ImportRejectionReason.UnknownSeries, "Unknown Series")), rootFolder, downloadId, null); @@ -367,7 +366,7 @@ private ManualImportItem ProcessFile(string rootFolder, string baseFolder, strin RelativePath = rootFolder.GetRelativePath(file), Name = Path.GetFileNameWithoutExtension(file), Size = _diskProvider.GetFileSize(file), - Rejections = new List() + Rejections = new List() }; } @@ -471,7 +470,7 @@ private ManualImportItem MapItem(EpisodeFile episodeFile, Series series, string item.IndexerFlags = (int)episodeFile.IndexerFlags; item.ReleaseType = episodeFile.ReleaseType; item.Size = _diskProvider.GetFileSize(item.Path); - item.Rejections = Enumerable.Empty(); + item.Rejections = Enumerable.Empty(); item.EpisodeFileId = episodeFile.Id; item.CustomFormats = _formatCalculator.ParseCustomFormat(episodeFile, series); diff --git a/src/NzbDrone.Core/MediaFiles/EpisodeImport/Specifications/AbsoluteEpisodeNumberSpecification.cs b/src/NzbDrone.Core/MediaFiles/EpisodeImport/Specifications/AbsoluteEpisodeNumberSpecification.cs index 9e361ccf9..134871fa5 100644 --- a/src/NzbDrone.Core/MediaFiles/EpisodeImport/Specifications/AbsoluteEpisodeNumberSpecification.cs +++ b/src/NzbDrone.Core/MediaFiles/EpisodeImport/Specifications/AbsoluteEpisodeNumberSpecification.cs @@ -1,7 +1,6 @@ using System; using NLog; using NzbDrone.Common.Extensions; -using NzbDrone.Core.DecisionEngine; using NzbDrone.Core.Download; using NzbDrone.Core.Organizer; using NzbDrone.Core.Parser.Model; @@ -20,18 +19,18 @@ public AbsoluteEpisodeNumberSpecification(IBuildFileNames buildFileNames, Logger _logger = logger; } - public Decision IsSatisfiedBy(LocalEpisode localEpisode, DownloadClientItem downloadClientItem) + public ImportSpecDecision IsSatisfiedBy(LocalEpisode localEpisode, DownloadClientItem downloadClientItem) { if (localEpisode.Series.SeriesType != SeriesTypes.Anime) { _logger.Debug("Series type is not Anime, skipping check"); - return Decision.Accept(); + return ImportSpecDecision.Accept(); } if (!_buildFileNames.RequiresAbsoluteEpisodeNumber()) { _logger.Debug("File name format does not require absolute episode number, skipping check"); - return Decision.Accept(); + return ImportSpecDecision.Accept(); } foreach (var episode in localEpisode.Episodes) @@ -49,11 +48,11 @@ public Decision IsSatisfiedBy(LocalEpisode localEpisode, DownloadClientItem down { _logger.Debug("Episode does not have an absolute episode number and recently aired"); - return Decision.Reject("Episode does not have an absolute episode number and recently aired"); + return ImportSpecDecision.Reject(ImportRejectionReason.MissingAbsoluteEpisodeNumber, "Episode does not have an absolute episode number and recently aired"); } } - return Decision.Accept(); + return ImportSpecDecision.Accept(); } } } diff --git a/src/NzbDrone.Core/MediaFiles/EpisodeImport/Specifications/AlreadyImportedSpecification.cs b/src/NzbDrone.Core/MediaFiles/EpisodeImport/Specifications/AlreadyImportedSpecification.cs index e7a62650b..30afe273b 100644 --- a/src/NzbDrone.Core/MediaFiles/EpisodeImport/Specifications/AlreadyImportedSpecification.cs +++ b/src/NzbDrone.Core/MediaFiles/EpisodeImport/Specifications/AlreadyImportedSpecification.cs @@ -22,12 +22,12 @@ public AlreadyImportedSpecification(IHistoryService historyService, public SpecificationPriority Priority => SpecificationPriority.Database; - public Decision IsSatisfiedBy(LocalEpisode localEpisode, DownloadClientItem downloadClientItem) + public ImportSpecDecision IsSatisfiedBy(LocalEpisode localEpisode, DownloadClientItem downloadClientItem) { if (downloadClientItem == null) { _logger.Debug("No download client information is available, skipping"); - return Decision.Accept(); + return ImportSpecDecision.Accept(); } foreach (var episode in localEpisode.Episodes) @@ -64,17 +64,17 @@ public Decision IsSatisfiedBy(LocalEpisode localEpisode, DownloadClientItem down 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()); + return ImportSpecDecision.Reject(ImportRejectionReason.EpisodeAlreadyImported, "Episode file already imported at {0}", lastImported.Date.ToLocalTime()); } } else { _logger.Debug("Episode file previously imported at {0}", lastImported.Date); - return Decision.Reject("Episode file already imported at {0}", lastImported.Date.ToLocalTime()); + return ImportSpecDecision.Reject(ImportRejectionReason.EpisodeAlreadyImported, "Episode file already imported at {0}", lastImported.Date.ToLocalTime()); } } - return Decision.Accept(); + return ImportSpecDecision.Accept(); } } } diff --git a/src/NzbDrone.Core/MediaFiles/EpisodeImport/Specifications/EpisodeTitleSpecification.cs b/src/NzbDrone.Core/MediaFiles/EpisodeImport/Specifications/EpisodeTitleSpecification.cs index 79a94e2f5..0860caf2e 100644 --- a/src/NzbDrone.Core/MediaFiles/EpisodeImport/Specifications/EpisodeTitleSpecification.cs +++ b/src/NzbDrone.Core/MediaFiles/EpisodeImport/Specifications/EpisodeTitleSpecification.cs @@ -3,7 +3,6 @@ using NLog; using NzbDrone.Common.Extensions; using NzbDrone.Core.Configuration; -using NzbDrone.Core.DecisionEngine; using NzbDrone.Core.Download; using NzbDrone.Core.Organizer; using NzbDrone.Core.Parser.Model; @@ -29,12 +28,12 @@ public EpisodeTitleSpecification(IConfigService configService, _logger = logger; } - public Decision IsSatisfiedBy(LocalEpisode localEpisode, DownloadClientItem downloadClientItem) + public ImportSpecDecision IsSatisfiedBy(LocalEpisode localEpisode, DownloadClientItem downloadClientItem) { if (localEpisode.ExistingFile) { _logger.Debug("{0} is in series folder, skipping check", localEpisode.Path); - return Decision.Accept(); + return ImportSpecDecision.Accept(); } var episodeTitleRequired = _configService.EpisodeTitleRequired; @@ -42,13 +41,13 @@ public Decision IsSatisfiedBy(LocalEpisode localEpisode, DownloadClientItem down if (episodeTitleRequired == EpisodeTitleRequiredType.Never) { _logger.Debug("Episode titles are never required, skipping check"); - return Decision.Accept(); + return ImportSpecDecision.Accept(); } if (!_buildFileNames.RequiresEpisodeTitle(localEpisode.Series, localEpisode.Episodes)) { _logger.Debug("File name format does not require episode title, skipping check"); - return Decision.Accept(); + return ImportSpecDecision.Accept(); } var episodes = localEpisode.Episodes; @@ -64,7 +63,7 @@ public Decision IsSatisfiedBy(LocalEpisode localEpisode, DownloadClientItem down e.AirDateUtc.Value == firstEpisode.AirDateUtc.Value) < 4) { _logger.Debug("Episode title only required for bulk season releases"); - return Decision.Accept(); + return ImportSpecDecision.Accept(); } foreach (var episode in episodes) @@ -82,18 +81,18 @@ public Decision IsSatisfiedBy(LocalEpisode localEpisode, DownloadClientItem down { _logger.Debug("Episode does not have a title and recently aired"); - return Decision.Reject("Episode does not have a title and recently aired"); + return ImportSpecDecision.Reject(ImportRejectionReason.TitleMissing, "Episode does not have a title and recently aired"); } if (title.Equals("TBA")) { _logger.Debug("Episode has a TBA title and recently aired"); - return Decision.Reject("Episode has a TBA title and recently aired"); + return ImportSpecDecision.Reject(ImportRejectionReason.TitleTba, "Episode has a TBA title and recently aired"); } } - return Decision.Accept(); + return ImportSpecDecision.Accept(); } } } diff --git a/src/NzbDrone.Core/MediaFiles/EpisodeImport/Specifications/FreeSpaceSpecification.cs b/src/NzbDrone.Core/MediaFiles/EpisodeImport/Specifications/FreeSpaceSpecification.cs index 29f74ca6b..1db0df0c6 100644 --- a/src/NzbDrone.Core/MediaFiles/EpisodeImport/Specifications/FreeSpaceSpecification.cs +++ b/src/NzbDrone.Core/MediaFiles/EpisodeImport/Specifications/FreeSpaceSpecification.cs @@ -4,7 +4,6 @@ using NzbDrone.Common.Disk; using NzbDrone.Common.Extensions; using NzbDrone.Core.Configuration; -using NzbDrone.Core.DecisionEngine; using NzbDrone.Core.Download; using NzbDrone.Core.Parser.Model; @@ -23,12 +22,12 @@ public FreeSpaceSpecification(IDiskProvider diskProvider, IConfigService configS _logger = logger; } - public Decision IsSatisfiedBy(LocalEpisode localEpisode, DownloadClientItem downloadClientItem) + public ImportSpecDecision IsSatisfiedBy(LocalEpisode localEpisode, DownloadClientItem downloadClientItem) { if (_configService.SkipFreeSpaceCheckWhenImporting) { _logger.Debug("Skipping free space check when importing"); - return Decision.Accept(); + return ImportSpecDecision.Accept(); } try @@ -36,7 +35,7 @@ public Decision IsSatisfiedBy(LocalEpisode localEpisode, DownloadClientItem down if (localEpisode.ExistingFile) { _logger.Debug("Skipping free space check for existing episode"); - return Decision.Accept(); + return ImportSpecDecision.Accept(); } var path = Directory.GetParent(localEpisode.Series.Path); @@ -45,13 +44,13 @@ public Decision IsSatisfiedBy(LocalEpisode localEpisode, DownloadClientItem down if (!freeSpace.HasValue) { _logger.Debug("Free space check returned an invalid result for: {0}", path); - return Decision.Accept(); + return ImportSpecDecision.Accept(); } if (freeSpace < localEpisode.Size + _configService.MinimumFreeSpaceWhenImporting.Megabytes()) { _logger.Warn("Not enough free space ({0}) to import: {1} ({2})", freeSpace, localEpisode, localEpisode.Size); - return Decision.Reject("Not enough free space"); + return ImportSpecDecision.Reject(ImportRejectionReason.MinimumFreeSpace, "Not enough free space"); } } catch (DirectoryNotFoundException ex) @@ -63,7 +62,7 @@ public Decision IsSatisfiedBy(LocalEpisode localEpisode, DownloadClientItem down _logger.Error(ex, "Unable to check free disk space while importing. {0}", localEpisode.Path); } - return Decision.Accept(); + return ImportSpecDecision.Accept(); } } } diff --git a/src/NzbDrone.Core/MediaFiles/EpisodeImport/Specifications/FullSeasonSpecification.cs b/src/NzbDrone.Core/MediaFiles/EpisodeImport/Specifications/FullSeasonSpecification.cs index 4d13eda6f..47116827b 100644 --- a/src/NzbDrone.Core/MediaFiles/EpisodeImport/Specifications/FullSeasonSpecification.cs +++ b/src/NzbDrone.Core/MediaFiles/EpisodeImport/Specifications/FullSeasonSpecification.cs @@ -1,5 +1,4 @@ using NLog; -using NzbDrone.Core.DecisionEngine; using NzbDrone.Core.Download; using NzbDrone.Core.Parser.Model; @@ -14,20 +13,20 @@ public FullSeasonSpecification(Logger logger) _logger = logger; } - public Decision IsSatisfiedBy(LocalEpisode localEpisode, DownloadClientItem downloadClientItem) + public ImportSpecDecision IsSatisfiedBy(LocalEpisode localEpisode, DownloadClientItem downloadClientItem) { if (localEpisode.FileEpisodeInfo == null) { - return Decision.Accept(); + return ImportSpecDecision.Accept(); } if (localEpisode.FileEpisodeInfo.FullSeason) { _logger.Debug("Single episode file detected as containing all episodes in the season due to no episode parsed from the file name."); - return Decision.Reject("Single episode file contains all episodes in seasons. Review file name or manually import"); + return ImportSpecDecision.Reject(ImportRejectionReason.FullSeason, "Single episode file contains all episodes in seasons. Review file name or manually import"); } - return Decision.Accept(); + return ImportSpecDecision.Accept(); } } } diff --git a/src/NzbDrone.Core/MediaFiles/EpisodeImport/Specifications/HasAudioTrackSpecification.cs b/src/NzbDrone.Core/MediaFiles/EpisodeImport/Specifications/HasAudioTrackSpecification.cs index 4a66eeea1..3e7680496 100644 --- a/src/NzbDrone.Core/MediaFiles/EpisodeImport/Specifications/HasAudioTrackSpecification.cs +++ b/src/NzbDrone.Core/MediaFiles/EpisodeImport/Specifications/HasAudioTrackSpecification.cs @@ -1,5 +1,4 @@ using NLog; -using NzbDrone.Core.DecisionEngine; using NzbDrone.Core.Download; using NzbDrone.Core.Parser.Model; @@ -14,22 +13,22 @@ public HasAudioTrackSpecification(Logger logger) _logger = logger; } - public Decision IsSatisfiedBy(LocalEpisode localEpisode, DownloadClientItem downloadClientItem) + public ImportSpecDecision IsSatisfiedBy(LocalEpisode localEpisode, DownloadClientItem downloadClientItem) { if (localEpisode.MediaInfo == null) { _logger.Debug("Failed to get media info from the file, make sure ffprobe is available, skipping check"); - return Decision.Accept(); + return ImportSpecDecision.Accept(); } if (localEpisode.MediaInfo.AudioStreamCount == 0) { _logger.Debug("No audio tracks found in file"); - return Decision.Reject("No audio tracks detected"); + return ImportSpecDecision.Reject(ImportRejectionReason.NoAudio, "No audio tracks detected"); } - return Decision.Accept(); + return ImportSpecDecision.Accept(); } } } diff --git a/src/NzbDrone.Core/MediaFiles/EpisodeImport/Specifications/MatchesFolderSpecification.cs b/src/NzbDrone.Core/MediaFiles/EpisodeImport/Specifications/MatchesFolderSpecification.cs index 230190cd5..22d3143d0 100644 --- a/src/NzbDrone.Core/MediaFiles/EpisodeImport/Specifications/MatchesFolderSpecification.cs +++ b/src/NzbDrone.Core/MediaFiles/EpisodeImport/Specifications/MatchesFolderSpecification.cs @@ -2,7 +2,6 @@ using System.Linq; using NLog; using NzbDrone.Common.Extensions; -using NzbDrone.Core.DecisionEngine; using NzbDrone.Core.Download; using NzbDrone.Core.Parser; using NzbDrone.Core.Parser.Model; @@ -21,11 +20,11 @@ public MatchesFolderSpecification(IParsingService parsingService, Logger logger) _parsingService = parsingService; } - public Decision IsSatisfiedBy(LocalEpisode localEpisode, DownloadClientItem downloadClientItem) + public ImportSpecDecision IsSatisfiedBy(LocalEpisode localEpisode, DownloadClientItem downloadClientItem) { if (localEpisode.ExistingFile) { - return Decision.Accept(); + return ImportSpecDecision.Accept(); } var fileInfo = localEpisode.FileEpisodeInfo; @@ -44,13 +43,13 @@ public Decision IsSatisfiedBy(LocalEpisode localEpisode, DownloadClientItem down if (folderInfo == null) { _logger.Debug("No folder ParsedEpisodeInfo, skipping check"); - return Decision.Accept(); + return ImportSpecDecision.Accept(); } if (fileInfo == null) { _logger.Debug("No file ParsedEpisodeInfo, skipping check"); - return Decision.Accept(); + return ImportSpecDecision.Accept(); } var folderEpisodes = _parsingService.GetEpisodes(folderInfo, localEpisode.Series, true); @@ -59,7 +58,7 @@ public Decision IsSatisfiedBy(LocalEpisode localEpisode, DownloadClientItem down if (folderEpisodes.Empty()) { _logger.Debug("No episode numbers in folder ParsedEpisodeInfo, skipping check"); - return Decision.Accept(); + return ImportSpecDecision.Accept(); } var unexpected = fileEpisodes.Where(e => folderEpisodes.All(o => o.Id != e.Id)).ToList(); @@ -70,13 +69,13 @@ public Decision IsSatisfiedBy(LocalEpisode localEpisode, DownloadClientItem down if (unexpected.Count == 1) { - return Decision.Reject("Episode {0} was unexpected considering the {1} folder name", FormatEpisode(unexpected), folderInfo.ReleaseTitle); + return ImportSpecDecision.Reject(ImportRejectionReason.EpisodeUnexpected, "Episode {0} was unexpected considering the {1} folder name", FormatEpisode(unexpected), folderInfo.ReleaseTitle); } - return Decision.Reject("Episodes {0} were unexpected considering the {1} folder name", FormatEpisode(unexpected), folderInfo.ReleaseTitle); + return ImportSpecDecision.Reject(ImportRejectionReason.EpisodeUnexpected, "Episodes {0} were unexpected considering the {1} folder name", FormatEpisode(unexpected), folderInfo.ReleaseTitle); } - return Decision.Accept(); + return ImportSpecDecision.Accept(); } private string FormatEpisode(List episodes) diff --git a/src/NzbDrone.Core/MediaFiles/EpisodeImport/Specifications/MatchesGrabSpecification.cs b/src/NzbDrone.Core/MediaFiles/EpisodeImport/Specifications/MatchesGrabSpecification.cs index a37762745..52c163bad 100644 --- a/src/NzbDrone.Core/MediaFiles/EpisodeImport/Specifications/MatchesGrabSpecification.cs +++ b/src/NzbDrone.Core/MediaFiles/EpisodeImport/Specifications/MatchesGrabSpecification.cs @@ -2,7 +2,6 @@ using System.Linq; using NLog; using NzbDrone.Common.Extensions; -using NzbDrone.Core.DecisionEngine; using NzbDrone.Core.Download; using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Tv; @@ -18,18 +17,18 @@ public MatchesGrabSpecification(Logger logger) _logger = logger; } - public Decision IsSatisfiedBy(LocalEpisode localEpisode, DownloadClientItem downloadClientItem) + public ImportSpecDecision IsSatisfiedBy(LocalEpisode localEpisode, DownloadClientItem downloadClientItem) { if (localEpisode.ExistingFile) { - return Decision.Accept(); + return ImportSpecDecision.Accept(); } var releaseInfo = localEpisode.Release; if (releaseInfo == null || releaseInfo.EpisodeIds.Empty()) { - return Decision.Accept(); + return ImportSpecDecision.Accept(); } var unexpected = localEpisode.Episodes.Where(e => releaseInfo.EpisodeIds.All(o => o != e.Id)).ToList(); @@ -40,13 +39,13 @@ public Decision IsSatisfiedBy(LocalEpisode localEpisode, DownloadClientItem down if (unexpected.Count == 1) { - return Decision.Reject("Episode {0} was not found in the grabbed release: {1}", FormatEpisode(unexpected), releaseInfo.Title); + return ImportSpecDecision.Reject(ImportRejectionReason.EpisodeNotFoundInRelease, "Episode {0} was not found in the grabbed release: {1}", FormatEpisode(unexpected), releaseInfo.Title); } - return Decision.Reject("Episodes {0} were not found in the grabbed release: {1}", FormatEpisode(unexpected), releaseInfo.Title); + return ImportSpecDecision.Reject(ImportRejectionReason.EpisodeNotFoundInRelease, "Episodes {0} were not found in the grabbed release: {1}", FormatEpisode(unexpected), releaseInfo.Title); } - return Decision.Accept(); + return ImportSpecDecision.Accept(); } private string FormatEpisode(List episodes) diff --git a/src/NzbDrone.Core/MediaFiles/EpisodeImport/Specifications/NotSampleSpecification.cs b/src/NzbDrone.Core/MediaFiles/EpisodeImport/Specifications/NotSampleSpecification.cs index 5d748f0f1..d042a7844 100644 --- a/src/NzbDrone.Core/MediaFiles/EpisodeImport/Specifications/NotSampleSpecification.cs +++ b/src/NzbDrone.Core/MediaFiles/EpisodeImport/Specifications/NotSampleSpecification.cs @@ -1,5 +1,4 @@ using NLog; -using NzbDrone.Core.DecisionEngine; using NzbDrone.Core.Download; using NzbDrone.Core.Parser; using NzbDrone.Core.Parser.Model; @@ -18,12 +17,12 @@ public NotSampleSpecification(IDetectSample detectSample, _logger = logger; } - public Decision IsSatisfiedBy(LocalEpisode localEpisode, DownloadClientItem downloadClientItem) + public ImportSpecDecision IsSatisfiedBy(LocalEpisode localEpisode, DownloadClientItem downloadClientItem) { if (localEpisode.ExistingFile) { _logger.Debug("Existing file, skipping sample check"); - return Decision.Accept(); + return ImportSpecDecision.Accept(); } try @@ -32,11 +31,11 @@ public Decision IsSatisfiedBy(LocalEpisode localEpisode, DownloadClientItem down if (sample == DetectSampleResult.Sample) { - return Decision.Reject("Sample"); + return ImportSpecDecision.Reject(ImportRejectionReason.Sample, "Sample"); } else if (sample == DetectSampleResult.Indeterminate) { - return Decision.Reject("Unable to determine if file is a sample"); + return ImportSpecDecision.Reject(ImportRejectionReason.SampleIndeterminate, "Unable to determine if file is a sample"); } } catch (InvalidSeasonException e) @@ -44,7 +43,7 @@ public Decision IsSatisfiedBy(LocalEpisode localEpisode, DownloadClientItem down _logger.Warn(e, "Invalid season detected during sample check"); } - return Decision.Accept(); + return ImportSpecDecision.Accept(); } } } diff --git a/src/NzbDrone.Core/MediaFiles/EpisodeImport/Specifications/NotUnpackingSpecification.cs b/src/NzbDrone.Core/MediaFiles/EpisodeImport/Specifications/NotUnpackingSpecification.cs index f8097737d..0db81d576 100644 --- a/src/NzbDrone.Core/MediaFiles/EpisodeImport/Specifications/NotUnpackingSpecification.cs +++ b/src/NzbDrone.Core/MediaFiles/EpisodeImport/Specifications/NotUnpackingSpecification.cs @@ -4,7 +4,6 @@ using NzbDrone.Common.Disk; using NzbDrone.Common.EnvironmentInfo; using NzbDrone.Core.Configuration; -using NzbDrone.Core.DecisionEngine; using NzbDrone.Core.Download; using NzbDrone.Core.Parser.Model; @@ -23,12 +22,12 @@ public NotUnpackingSpecification(IDiskProvider diskProvider, IConfigService conf _logger = logger; } - public Decision IsSatisfiedBy(LocalEpisode localEpisode, DownloadClientItem downloadClientItem) + public ImportSpecDecision IsSatisfiedBy(LocalEpisode localEpisode, DownloadClientItem downloadClientItem) { if (localEpisode.ExistingFile) { _logger.Debug("{0} is in series folder, skipping unpacking check", localEpisode.Path); - return Decision.Accept(); + return ImportSpecDecision.Accept(); } foreach (var workingFolder in _configService.DownloadClientWorkingFolders.Split('|')) @@ -41,13 +40,13 @@ public Decision IsSatisfiedBy(LocalEpisode localEpisode, DownloadClientItem down if (OsInfo.IsNotWindows) { _logger.Debug("{0} is still being unpacked", localEpisode.Path); - return Decision.Reject("File is still being unpacked"); + return ImportSpecDecision.Reject(ImportRejectionReason.Unpacking, "File is still being unpacked"); } if (_diskProvider.FileGetLastWrite(localEpisode.Path) > DateTime.UtcNow.AddMinutes(-5)) { _logger.Debug("{0} appears to be unpacking still", localEpisode.Path); - return Decision.Reject("File is still being unpacked"); + return ImportSpecDecision.Reject(ImportRejectionReason.Unpacking, "File is still being unpacked"); } } @@ -55,7 +54,7 @@ public Decision IsSatisfiedBy(LocalEpisode localEpisode, DownloadClientItem down } } - return Decision.Accept(); + return ImportSpecDecision.Accept(); } } } diff --git a/src/NzbDrone.Core/MediaFiles/EpisodeImport/Specifications/SameEpisodesImportSpecification.cs b/src/NzbDrone.Core/MediaFiles/EpisodeImport/Specifications/SameEpisodesImportSpecification.cs index b645f500c..93178226d 100644 --- a/src/NzbDrone.Core/MediaFiles/EpisodeImport/Specifications/SameEpisodesImportSpecification.cs +++ b/src/NzbDrone.Core/MediaFiles/EpisodeImport/Specifications/SameEpisodesImportSpecification.cs @@ -19,15 +19,15 @@ public SameEpisodesImportSpecification(SameEpisodesSpecification sameEpisodesSpe public RejectionType Type => RejectionType.Permanent; - public Decision IsSatisfiedBy(LocalEpisode localEpisode, DownloadClientItem downloadClientItem) + public ImportSpecDecision IsSatisfiedBy(LocalEpisode localEpisode, DownloadClientItem downloadClientItem) { if (_sameEpisodesSpecification.IsSatisfiedBy(localEpisode.Episodes)) { - return Decision.Accept(); + return ImportSpecDecision.Accept(); } _logger.Debug("Episode file on disk contains more episodes than this file contains"); - return Decision.Reject("Episode file on disk contains more episodes than this file contains"); + return ImportSpecDecision.Reject(ImportRejectionReason.ExistingFileHasMoreEpisodes, "Episode file on disk contains more episodes than this file contains"); } } } diff --git a/src/NzbDrone.Core/MediaFiles/EpisodeImport/Specifications/SplitEpisodeSpecification.cs b/src/NzbDrone.Core/MediaFiles/EpisodeImport/Specifications/SplitEpisodeSpecification.cs index 8a84b0b86..14ced57e2 100644 --- a/src/NzbDrone.Core/MediaFiles/EpisodeImport/Specifications/SplitEpisodeSpecification.cs +++ b/src/NzbDrone.Core/MediaFiles/EpisodeImport/Specifications/SplitEpisodeSpecification.cs @@ -1,5 +1,4 @@ using NLog; -using NzbDrone.Core.DecisionEngine; using NzbDrone.Core.Download; using NzbDrone.Core.Parser.Model; @@ -14,20 +13,20 @@ public SplitEpisodeSpecification(Logger logger) _logger = logger; } - public Decision IsSatisfiedBy(LocalEpisode localEpisode, DownloadClientItem downloadClientItem) + public ImportSpecDecision IsSatisfiedBy(LocalEpisode localEpisode, DownloadClientItem downloadClientItem) { if (localEpisode.FileEpisodeInfo == null) { - return Decision.Accept(); + return ImportSpecDecision.Accept(); } if (localEpisode.FileEpisodeInfo.IsSplitEpisode) { _logger.Debug("Single episode split into multiple files"); - return Decision.Reject("Single episode split into multiple files"); + return ImportSpecDecision.Reject(ImportRejectionReason.SplitEpisode, "Single episode split into multiple files"); } - return Decision.Accept(); + return ImportSpecDecision.Accept(); } } } diff --git a/src/NzbDrone.Core/MediaFiles/EpisodeImport/Specifications/UnverifiedSceneNumberingSpecification.cs b/src/NzbDrone.Core/MediaFiles/EpisodeImport/Specifications/UnverifiedSceneNumberingSpecification.cs index ede9cee58..a22df4411 100644 --- a/src/NzbDrone.Core/MediaFiles/EpisodeImport/Specifications/UnverifiedSceneNumberingSpecification.cs +++ b/src/NzbDrone.Core/MediaFiles/EpisodeImport/Specifications/UnverifiedSceneNumberingSpecification.cs @@ -1,6 +1,5 @@ using System.Linq; using NLog; -using NzbDrone.Core.DecisionEngine; using NzbDrone.Core.Download; using NzbDrone.Core.Parser.Model; namespace NzbDrone.Core.MediaFiles.EpisodeImport.Specifications @@ -14,21 +13,21 @@ public UnverifiedSceneNumberingSpecification(Logger logger) _logger = logger; } - public Decision IsSatisfiedBy(LocalEpisode localEpisode, DownloadClientItem downloadClientItem) + public ImportSpecDecision IsSatisfiedBy(LocalEpisode localEpisode, DownloadClientItem downloadClientItem) { if (localEpisode.ExistingFile) { _logger.Debug("Skipping scene numbering check for existing episode"); - return Decision.Accept(); + return ImportSpecDecision.Accept(); } if (localEpisode.Episodes.Any(v => v.UnverifiedSceneNumbering)) { _logger.Debug("This file uses unverified scene numbers, will not auto-import until numbering is confirmed on TheXEM. Skipping {0}", localEpisode.Path); - return Decision.Reject("This show has individual episode mappings on TheXEM but the mapping for this episode has not been confirmed yet by their administrators. TheXEM needs manual input."); + return ImportSpecDecision.Reject(ImportRejectionReason.UnverifiedSceneMapping, "This show has individual episode mappings on TheXEM but the mapping for this episode has not been confirmed yet by their administrators. TheXEM needs manual input."); } - return Decision.Accept(); + return ImportSpecDecision.Accept(); } } } diff --git a/src/NzbDrone.Core/MediaFiles/EpisodeImport/Specifications/UpgradeSpecification.cs b/src/NzbDrone.Core/MediaFiles/EpisodeImport/Specifications/UpgradeSpecification.cs index 0a6d5a6be..ae824ffb9 100644 --- a/src/NzbDrone.Core/MediaFiles/EpisodeImport/Specifications/UpgradeSpecification.cs +++ b/src/NzbDrone.Core/MediaFiles/EpisodeImport/Specifications/UpgradeSpecification.cs @@ -3,7 +3,6 @@ using NzbDrone.Common.Extensions; using NzbDrone.Core.Configuration; using NzbDrone.Core.CustomFormats; -using NzbDrone.Core.DecisionEngine; using NzbDrone.Core.Download; using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Qualities; @@ -25,7 +24,7 @@ public UpgradeSpecification(IConfigService configService, _logger = logger; } - public Decision IsSatisfiedBy(LocalEpisode localEpisode, DownloadClientItem downloadClientItem) + public ImportSpecDecision IsSatisfiedBy(LocalEpisode localEpisode, DownloadClientItem downloadClientItem) { var downloadPropersAndRepacks = _configService.DownloadPropersAndRepacks; var qualityProfile = localEpisode.Series.QualityProfile.Value; @@ -46,7 +45,7 @@ public Decision IsSatisfiedBy(LocalEpisode localEpisode, DownloadClientItem down if (qualityCompare < 0) { _logger.Debug("This file isn't a quality upgrade for all episodes. Existing quality: {0}. New Quality {1}. Skipping {2}", episodeFile.Quality.Quality, localEpisode.Quality.Quality, localEpisode.Path); - return Decision.Reject("Not an upgrade for existing episode file(s). Existing quality: {0}. New Quality {1}.", episodeFile.Quality.Quality, localEpisode.Quality.Quality); + return ImportSpecDecision.Reject(ImportRejectionReason.NotQualityUpgrade, "Not an upgrade for existing episode file(s). Existing quality: {0}. New Quality {1}.", episodeFile.Quality.Quality, localEpisode.Quality.Quality); } // Same quality, propers/repacks are preferred and it is not a revision update. Reject revision downgrade. @@ -56,7 +55,7 @@ public Decision IsSatisfiedBy(LocalEpisode localEpisode, DownloadClientItem down localEpisode.Quality.Revision.CompareTo(episodeFile.Quality.Revision) < 0) { _logger.Debug("This file isn't a quality revision upgrade for all episodes. Skipping {0}", localEpisode.Path); - return Decision.Reject("Not a quality revision upgrade for existing episode file(s)"); + return ImportSpecDecision.Reject(ImportRejectionReason.NotRevisionUpgrade, "Not a quality revision upgrade for existing episode file(s)"); } var currentFormats = _formatService.ParseCustomFormat(episodeFile); @@ -72,7 +71,8 @@ public Decision IsSatisfiedBy(LocalEpisode localEpisode, DownloadClientItem down currentFormats != null ? currentFormats.ConcatToString() : "", currentFormatScore); - return Decision.Reject("Not a Custom Format upgrade for existing episode file(s). New: [{0}] ({1}) do not improve on Existing: [{2}] ({3})", + return ImportSpecDecision.Reject(ImportRejectionReason.NotCustomFormatUpgrade, + "Not a Custom Format upgrade for existing episode file(s). New: [{0}] ({1}) do not improve on Existing: [{2}] ({3})", newFormats != null ? newFormats.ConcatToString() : "", newFormatScore, currentFormats != null ? currentFormats.ConcatToString() : "", @@ -86,7 +86,7 @@ public Decision IsSatisfiedBy(LocalEpisode localEpisode, DownloadClientItem down currentFormatScore); } - return Decision.Accept(); + return ImportSpecDecision.Accept(); } } } diff --git a/src/Sonarr.Api.V3/Indexers/ReleaseResource.cs b/src/Sonarr.Api.V3/Indexers/ReleaseResource.cs index 2b4ddb899..d9b12f633 100644 --- a/src/Sonarr.Api.V3/Indexers/ReleaseResource.cs +++ b/src/Sonarr.Api.V3/Indexers/ReleaseResource.cs @@ -138,7 +138,7 @@ public static ReleaseResource ToResource(this DownloadDecision model) TvdbId = releaseInfo.TvdbId, TvRageId = releaseInfo.TvRageId, ImdbId = releaseInfo.ImdbId, - Rejections = model.Rejections.Select(r => r.Reason).ToList(), + Rejections = model.Rejections.Select(r => r.Message).ToList(), PublishDate = releaseInfo.PublishDate, CommentUrl = releaseInfo.CommentUrl, DownloadUrl = releaseInfo.DownloadUrl, diff --git a/src/Sonarr.Api.V3/ManualImport/ManualImportController.cs b/src/Sonarr.Api.V3/ManualImport/ManualImportController.cs index 46ab91a95..7c727fd4c 100644 --- a/src/Sonarr.Api.V3/ManualImport/ManualImportController.cs +++ b/src/Sonarr.Api.V3/ManualImport/ManualImportController.cs @@ -45,7 +45,7 @@ public object ReprocessItems([FromBody] List item item.Episodes = processedItem.Episodes.ToResource(); item.ReleaseType = processedItem.ReleaseType; item.IndexerFlags = processedItem.IndexerFlags; - item.Rejections = processedItem.Rejections; + item.Rejections = processedItem.Rejections.Select(r => r.ToResource()); item.CustomFormats = processedItem.CustomFormats.ToResource(false); item.CustomFormatScore = processedItem.CustomFormatScore; diff --git a/src/Sonarr.Api.V3/ManualImport/ManualImportReprocessResource.cs b/src/Sonarr.Api.V3/ManualImport/ManualImportReprocessResource.cs index 4eb2bbe4b..182fefe22 100644 --- a/src/Sonarr.Api.V3/ManualImport/ManualImportReprocessResource.cs +++ b/src/Sonarr.Api.V3/ManualImport/ManualImportReprocessResource.cs @@ -1,5 +1,4 @@ using System.Collections.Generic; -using NzbDrone.Core.DecisionEngine; using NzbDrone.Core.Languages; using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Qualities; @@ -24,6 +23,6 @@ public class ManualImportReprocessResource : RestResource public int CustomFormatScore { get; set; } public int IndexerFlags { get; set; } public ReleaseType ReleaseType { get; set; } - public IEnumerable Rejections { get; set; } + public IEnumerable Rejections { get; set; } } } diff --git a/src/Sonarr.Api.V3/ManualImport/ManualImportResource.cs b/src/Sonarr.Api.V3/ManualImport/ManualImportResource.cs index a65e6bdf3..b65da7987 100644 --- a/src/Sonarr.Api.V3/ManualImport/ManualImportResource.cs +++ b/src/Sonarr.Api.V3/ManualImport/ManualImportResource.cs @@ -3,6 +3,7 @@ using NzbDrone.Common.Crypto; using NzbDrone.Core.DecisionEngine; using NzbDrone.Core.Languages; +using NzbDrone.Core.MediaFiles.EpisodeImport; using NzbDrone.Core.MediaFiles.EpisodeImport.Manual; using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Qualities; @@ -33,7 +34,7 @@ public class ManualImportResource : RestResource public int CustomFormatScore { get; set; } public int IndexerFlags { get; set; } public ReleaseType ReleaseType { get; set; } - public IEnumerable Rejections { get; set; } + public IEnumerable Rejections { get; set; } } public static class ManualImportResourceMapper @@ -70,7 +71,7 @@ public static ManualImportResource ToResource(this ManualImportItem model) DownloadId = model.DownloadId, IndexerFlags = model.IndexerFlags, ReleaseType = model.ReleaseType, - Rejections = model.Rejections + Rejections = model.Rejections.Select(r => r.ToResource()) }; } @@ -79,4 +80,27 @@ public static List ToResource(this IEnumerable