diff --git a/src/NzbDrone.Core.Test/DecisionEngineTests/DownloadDecisionMakerFixture.cs b/src/NzbDrone.Core.Test/DecisionEngineTests/DownloadDecisionMakerFixture.cs index 5332589be0..0c3978f9f4 100644 --- a/src/NzbDrone.Core.Test/DecisionEngineTests/DownloadDecisionMakerFixture.cs +++ b/src/NzbDrone.Core.Test/DecisionEngineTests/DownloadDecisionMakerFixture.cs @@ -20,32 +20,32 @@ public class DownloadDecisionMakerFixture : CoreTest private List _reports; private RemoteMovie _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; [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(); - _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")); _reports = new List { new ReleaseInfo { Title = "Trolls.2016.720p.WEB-DL.DD5.1.H264-FGT" } }; _remoteEpisode = new RemoteMovie @@ -58,9 +58,9 @@ public void Setup() .Setup(c => c.Map(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())).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] diff --git a/src/NzbDrone.Core.Test/Download/CompletedDownloadServiceTests/ImportFixture.cs b/src/NzbDrone.Core.Test/Download/CompletedDownloadServiceTests/ImportFixture.cs index cd098da27e..2e9d1ff6c5 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; @@ -107,11 +106,11 @@ public void should_not_mark_as_imported_if_all_files_were_rejected() { new ImportResult( new ImportDecision( - new LocalMovie { Path = @"C:\TestPath\Droned.1998.mkv" }, new Rejection("Rejected!")), "Test Failure"), + new LocalMovie { Path = @"C:\TestPath\Droned.1998.mkv" }, new ImportRejection(ImportRejectionReason.Unknown, "Rejected!")), "Test Failure"), new ImportResult( new ImportDecision( - new LocalMovie { Path = @"C:\TestPath\Droned.1999.mkv" }, new Rejection("Rejected!")), "Test Failure") + new LocalMovie { Path = @"C:\TestPath\Droned.1999.mkv" }, new ImportRejection(ImportRejectionReason.Unknown, "Rejected!")), "Test Failure") }); Subject.Import(_trackedDownload); @@ -131,11 +130,11 @@ public void should_not_mark_as_imported_if_no_movies_were_parsed() { new ImportResult( new ImportDecision( - new LocalMovie { Path = @"C:\TestPath\Droned.1998.mkv" }, new Rejection("Rejected!")), "Test Failure"), + new LocalMovie { Path = @"C:\TestPath\Droned.1998.mkv" }, new ImportRejection(ImportRejectionReason.Unknown, "Rejected!")), "Test Failure"), new ImportResult( new ImportDecision( - new LocalMovie { Path = @"C:\TestPath\Droned.1998.mkv" }, new Rejection("Rejected!")), "Test Failure") + new LocalMovie { Path = @"C:\TestPath\Droned.1998.mkv" }, new ImportRejection(ImportRejectionReason.Unknown, "Rejected!")), "Test Failure") }); _trackedDownload.RemoteMovie.Movie = new Movie(); diff --git a/src/NzbDrone.Core.Test/Download/DownloadApprovedReportsTests/DownloadApprovedFixture.cs b/src/NzbDrone.Core.Test/Download/DownloadApprovedReportsTests/DownloadApprovedFixture.cs index 6d0ee4b540..7f6da7b73e 100644 --- a/src/NzbDrone.Core.Test/Download/DownloadApprovedReportsTests/DownloadApprovedFixture.cs +++ b/src/NzbDrone.Core.Test/Download/DownloadApprovedReportsTests/DownloadApprovedFixture.cs @@ -189,8 +189,8 @@ public void should_return_an_empty_list_when_none_are_appproved() { var decisions = new List(); RemoteMovie remoteMovie = null; - decisions.Add(new DownloadDecision(remoteMovie, new Rejection("Failure!"))); - decisions.Add(new DownloadDecision(remoteMovie, new Rejection("Failure!"))); + decisions.Add(new DownloadDecision(remoteMovie, new DownloadRejection(DownloadRejectionReason.Unknown, "Failure!"))); + decisions.Add(new DownloadDecision(remoteMovie, new DownloadRejection(DownloadRejectionReason.Unknown, "Failure!"))); Subject.GetQualifiedReports(decisions).Should().BeEmpty(); } @@ -201,7 +201,7 @@ public async Task should_not_grab_if_pending() var remoteMovie = GetRemoteMovie(new QualityModel(Quality.HDTV720p)); var decisions = new List(); - decisions.Add(new DownloadDecision(remoteMovie, new Rejection("Failure!", RejectionType.Temporary))); + decisions.Add(new DownloadDecision(remoteMovie, new DownloadRejection(DownloadRejectionReason.Unknown, "Failure!", RejectionType.Temporary))); await Subject.ProcessDecisions(decisions); Mocker.GetMock().Verify(v => v.DownloadReport(It.IsAny(), null), Times.Never()); @@ -214,7 +214,7 @@ public async Task should_not_add_to_pending_if_movie_was_grabbed() var decisions = new List(); decisions.Add(new DownloadDecision(removeMovie)); - decisions.Add(new DownloadDecision(removeMovie, new Rejection("Failure!", RejectionType.Temporary))); + decisions.Add(new DownloadDecision(removeMovie, 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 = GetRemoteMovie(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 bac4c9aa45..face5dfd8c 100644 --- a/src/NzbDrone.Core.Test/Download/Pending/PendingReleaseServiceTests/AddFixture.cs +++ b/src/NzbDrone.Core.Test/Download/Pending/PendingReleaseServiceTests/AddFixture.cs @@ -56,7 +56,7 @@ public void Setup() _remoteMovie.ParsedMovieInfo = _parsedMovieInfo; _remoteMovie.Release = _release; - _temporarilyRejected = new DownloadDecision(_remoteMovie, new Rejection("Temp Rejected", RejectionType.Temporary)); + _temporarilyRejected = new DownloadDecision(_remoteMovie, 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 3f86e1f232..2457fe8265 100644 --- a/src/NzbDrone.Core.Test/Download/Pending/PendingReleaseServiceTests/RemoveGrabbedFixture.cs +++ b/src/NzbDrone.Core.Test/Download/Pending/PendingReleaseServiceTests/RemoveGrabbedFixture.cs @@ -56,7 +56,7 @@ public void Setup() _remoteMovie.ParsedMovieInfo = _parsedMovieInfo; _remoteMovie.Release = _release; - _temporarilyRejected = new DownloadDecision(_remoteMovie, new Rejection("Temp Rejected", RejectionType.Temporary)); + _temporarilyRejected = new DownloadDecision(_remoteMovie, 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 9778581158..ade605a8c9 100644 --- a/src/NzbDrone.Core.Test/Download/Pending/PendingReleaseServiceTests/RemoveRejectedFixture.cs +++ b/src/NzbDrone.Core.Test/Download/Pending/PendingReleaseServiceTests/RemoveRejectedFixture.cs @@ -59,7 +59,7 @@ public void Setup() _remoteMovie.ParsedMovieInfo = _parsedMovieInfo; _remoteMovie.Release = _release; - _temporarilyRejected = new DownloadDecision(_remoteMovie, new Rejection("Temp Rejected", RejectionType.Temporary)); + _temporarilyRejected = new DownloadDecision(_remoteMovie, new DownloadRejection(DownloadRejectionReason.MinimumAgeDelay, "Temp Rejected", RejectionType.Temporary)); Mocker.GetMock() .Setup(s => s.All()) diff --git a/src/NzbDrone.Core.Test/MediaFiles/MovieImport/ImportApprovedMoviesFixture.cs b/src/NzbDrone.Core.Test/MediaFiles/MovieImport/ImportApprovedMoviesFixture.cs index 099b6580a9..6e23a21f45 100644 --- a/src/NzbDrone.Core.Test/MediaFiles/MovieImport/ImportApprovedMoviesFixture.cs +++ b/src/NzbDrone.Core.Test/MediaFiles/MovieImport/ImportApprovedMoviesFixture.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; @@ -46,9 +45,9 @@ public void Setup() .With(s => s.Path = @"C:\Test\TV\30 Rock".AsOsAgnostic()) .Build(); - _rejectedDecisions.Add(new ImportDecision(new LocalMovie(), new Rejection("Rejected!"))); - _rejectedDecisions.Add(new ImportDecision(new LocalMovie(), new Rejection("Rejected!"))); - _rejectedDecisions.Add(new ImportDecision(new LocalMovie(), new Rejection("Rejected!"))); + _rejectedDecisions.Add(new ImportDecision(new LocalMovie(), new ImportRejection(ImportRejectionReason.Unknown, "Rejected!"))); + _rejectedDecisions.Add(new ImportDecision(new LocalMovie(), new ImportRejection(ImportRejectionReason.Unknown, "Rejected!"))); + _rejectedDecisions.Add(new ImportDecision(new LocalMovie(), new ImportRejection(ImportRejectionReason.Unknown, "Rejected!"))); _approvedDecisions.Add(new ImportDecision( new LocalMovie diff --git a/src/NzbDrone.Core.Test/MediaFiles/MovieImport/ImportDecisionMakerFixture.cs b/src/NzbDrone.Core.Test/MediaFiles/MovieImport/ImportDecisionMakerFixture.cs index 8ef04bfcbc..493621bfa2 100644 --- a/src/NzbDrone.Core.Test/MediaFiles/MovieImport/ImportDecisionMakerFixture.cs +++ b/src/NzbDrone.Core.Test/MediaFiles/MovieImport/ImportDecisionMakerFixture.cs @@ -4,7 +4,6 @@ using FluentAssertions; using Moq; using NUnit.Framework; -using NzbDrone.Core.DecisionEngine; using NzbDrone.Core.Download; using NzbDrone.Core.MediaFiles; using NzbDrone.Core.MediaFiles.MovieImport; @@ -49,13 +48,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")); _movie = Builder.CreateNew() .With(e => e.Path = @"C:\Test\Movie".AsOsAgnostic()) diff --git a/src/NzbDrone.Core/DecisionEngine/Decision.cs b/src/NzbDrone.Core/DecisionEngine/Decision.cs deleted file mode 100644 index 160e2599d7..0000000000 --- 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 382c787bf8..fce86b2fea 100644 --- a/src/NzbDrone.Core/DecisionEngine/DownloadDecision.cs +++ b/src/NzbDrone.Core/DecisionEngine/DownloadDecision.cs @@ -8,7 +8,7 @@ public class DownloadDecision { public RemoteMovie RemoteMovie { get; private set; } - public IEnumerable Rejections { get; private set; } + public IEnumerable Rejections { get; private set; } public bool Approved => !Rejections.Any(); @@ -28,7 +28,7 @@ public bool Rejected } } - public DownloadDecision(RemoteMovie movie, params Rejection[] rejections) + public DownloadDecision(RemoteMovie movie, params DownloadRejection[] rejections) { RemoteMovie = movie; Rejections = rejections.ToList(); diff --git a/src/NzbDrone.Core/DecisionEngine/DownloadDecisionMaker.cs b/src/NzbDrone.Core/DecisionEngine/DownloadDecisionMaker.cs index b7d05f541e..e1d3f09063 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 IConfigService _configService; private readonly ICustomFormatCalculationService _formatCalculator; private readonly IRemoteMovieAggregationService _aggregationService; private readonly Logger _logger; - public DownloadDecisionMaker(IEnumerable specifications, + public DownloadDecisionMaker(IEnumerable specifications, IParsingService parsingService, IConfigService configService, ICustomFormatCalculationService formatCalculator, @@ -85,9 +85,7 @@ private IEnumerable GetDecisions(List reports, bo if (remoteMovie.Movie == null) { - var reason = "Unknown Movie"; - - decision = new DownloadDecision(remoteMovie, new Rejection(reason)); + decision = new DownloadDecision(remoteMovie, new DownloadRejection(DownloadRejectionReason.UnknownMovie, "Unknown Movie")); } else { @@ -123,7 +121,7 @@ private IEnumerable GetDecisions(List reports, bo Languages = parsedMovieInfo.Languages }; - decision = new DownloadDecision(remoteMovie, new Rejection("Unable to parse release")); + decision = new DownloadDecision(remoteMovie, new DownloadRejection(DownloadRejectionReason.UnableToParse, "Unable to parse release")); } } } @@ -132,7 +130,7 @@ private IEnumerable GetDecisions(List reports, bo _logger.Error(e, "Couldn't process release."); var remoteMovie = new RemoteMovie { Release = report }; - decision = new DownloadDecision(remoteMovie, new Rejection("Unexpected error processing release")); + decision = new DownloadDecision(remoteMovie, new DownloadRejection(DownloadRejectionReason.Error, "Unexpected error processing release")); } reportNumber++; @@ -175,7 +173,7 @@ private IEnumerable GetDecisions(List reports, bo private DownloadDecision GetDecisionForReport(RemoteMovie remoteMovie, SearchCriteriaBase searchCriteria = null) { - var reasons = Array.Empty(); + var reasons = Array.Empty(); foreach (var specifications in _specifications.GroupBy(v => v.Priority).OrderBy(v => v.Key)) { @@ -192,7 +190,7 @@ private DownloadDecision GetDecisionForReport(RemoteMovie remoteMovie, SearchCri return new DownloadDecision(remoteMovie, reasons.ToArray()); } - private Rejection EvaluateSpec(IDecisionEngineSpecification spec, RemoteMovie remoteMovie, SearchCriteriaBase searchCriteriaBase = null) + private DownloadRejection EvaluateSpec(IDownloadDecisionEngineSpecification spec, RemoteMovie remoteMovie, SearchCriteriaBase searchCriteriaBase = null) { try { @@ -200,7 +198,7 @@ private Rejection EvaluateSpec(IDecisionEngineSpecification spec, RemoteMovie re if (!result.Accepted) { - return new Rejection(result.Reason, spec.Type); + return new DownloadRejection(result.Reason, result.Message, spec.Type); } } catch (NotImplementedException) @@ -212,7 +210,7 @@ private Rejection EvaluateSpec(IDecisionEngineSpecification spec, RemoteMovie re e.Data.Add("report", remoteMovie.Release.ToJson()); e.Data.Add("parsed", remoteMovie.ParsedMovieInfo.ToJson()); _logger.Error(e, "Couldn't evaluate decision on {0}, with spec: {1}", remoteMovie.Release.Title, spec.GetType().Name); - 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 0000000000..fbaeccdf12 --- /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 0000000000..887acc1216 --- /dev/null +++ b/src/NzbDrone.Core/DecisionEngine/DownloadRejectionReason.cs @@ -0,0 +1,67 @@ +namespace NzbDrone.Core.DecisionEngine; + +public enum DownloadRejectionReason +{ + Unknown, + UnknownMovie, + UnableToParse, + Error, + DecisionError, + Availability, + MinimumAgeDelay, + MovieNotMonitored, + HistoryRecentCutoffMet, + HistoryCdhDisabledCutoffMet, + HistoryHigherPreference, + HistoryHigherRevision, + HistoryCutoffMet, + HistoryCustomFormatCutoffMet, + HistoryCustomFormatScore, + HistoryCustomFormatScoreIncrement, + HistoryUpgradesNotAllowed, + NoMatchingTag, + PropersDisabled, + ProperForOldFile, + WrongMovie, + UnknownRuntime, + BelowMinimumSize, + AboveMaximumSize, + AlreadyImportedSameHash, + AlreadyImportedSameName, + IndexerDisabled, + Blocklisted, + CustomFormatMinimumScore, + MinimumFreeSpace, + HardcodeSubtitles, + WantedLanguage, + MaximumSizeExceeded, + MinimumAge, + MaximumAge, + Sample, + ProtocolDisabled, + QualityNotWanted, + QualityUpgradesDisabled, + QueueHigherPreference, + QueueHigherRevision, + QueueCutoffMet, + QueueCustomFormatCutoffMet, + QueueCustomFormatScore, + QueueCustomFormatScoreIncrement, + QueueUpgradesNotAllowed, + QueuePropersDisabled, + Raw, + MustContainMissing, + MustNotContainPresent, + RepackDisabled, + RepackUnknownReleaseGroup, + RepackReleaseGroupDoesNotMatch, + RequiredFlags, + MinimumSeeders, + DiskHigherPreference, + DiskHigherRevision, + DiskCutoffMet, + DiskCustomFormatCutoffMet, + DiskCustomFormatScore, + DiskCustomFormatScoreIncrement, + DiskUpgradesNotAllowed +} diff --git a/src/NzbDrone.Core/DecisionEngine/DownloadSpecDecision.cs b/src/NzbDrone.Core/DecisionEngine/DownloadSpecDecision.cs new file mode 100644 index 0000000000..53653293d7 --- /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 723968f9b6..1f7057ea27 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 8d9f3cfd8c..7eff909836 100644 --- a/src/NzbDrone.Core/DecisionEngine/Specifications/AcceptableSizeSpecification.cs +++ b/src/NzbDrone.Core/DecisionEngine/Specifications/AcceptableSizeSpecification.cs @@ -6,7 +6,7 @@ namespace NzbDrone.Core.DecisionEngine.Specifications { - public class AcceptableSizeSpecification : IDecisionEngineSpecification + public class AcceptableSizeSpecification : IDownloadDecisionEngineSpecification { private readonly IQualityDefinitionService _qualityDefinitionService; private readonly Logger _logger; @@ -20,7 +20,7 @@ public AcceptableSizeSpecification(IQualityDefinitionService qualityDefinitionSe public SpecificationPriority Priority => SpecificationPriority.Default; public RejectionType Type => RejectionType.Permanent; - public Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCriteria) + public DownloadSpecDecision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCriteria) { _logger.Debug("Beginning size check for: {0}", subject); @@ -29,7 +29,7 @@ public Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCrit if (subject.Release.Size == 0) { _logger.Debug("Release has unknown size, skipping size check"); - return Decision.Accept(); + return DownloadSpecDecision.Accept(); } var qualityDefinition = _qualityDefinitionService.Get(quality); @@ -53,7 +53,7 @@ public Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCrit var runtimeMessage = subject.Movie.Title; _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); } } @@ -64,7 +64,7 @@ public Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCrit else if (subject.Movie.MovieMetadata.Value.Runtime == 0) { _logger.Debug("Movie runtime is 0, unable to validate size until it is available, rejecting"); - return Decision.Reject("Movie runtime is 0, unable to validate size until it is available"); + return DownloadSpecDecision.Reject(DownloadRejectionReason.UnknownRuntime, "Movie runtime is 0, unable to validate size until it is available"); } else { @@ -77,12 +77,12 @@ public Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCrit if (subject.Release.Size > maxSize) { _logger.Debug("Item: {0}, Size: {1} is greater than maximum allowed size ({2} for {3}), rejecting", subject, subject.Release.Size, maxSize, subject.Movie.Title); - return Decision.Reject("{0} is larger than maximum allowed {1} (for {2})", subject.Release.Size.SizeSuffix(), maxSize.SizeSuffix(), subject.Movie.Title); + return DownloadSpecDecision.Reject(DownloadRejectionReason.AboveMaximumSize, "{0} is larger than maximum allowed {1} (for {2})", subject.Release.Size.SizeSuffix(), maxSize.SizeSuffix(), subject.Movie.Title); } } _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 47bba7aafb..6d46a22dda 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(RemoteMovie subject, SearchCriteriaBase searchCriteria) + public DownloadSpecDecision IsSatisfiedBy(RemoteMovie 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(); } var movie = subject.Movie; @@ -45,7 +45,7 @@ public Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCrit if (!movie.HasFile) { _logger.Debug("Skipping already imported check for movie without file"); - return Decision.Accept(); + return DownloadSpecDecision.Accept(); } var historyForMovie = _historyService.GetByMovieId(movie.Id, null); @@ -53,7 +53,7 @@ public Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCrit if (lastGrabbed == null) { - return Decision.Accept(); + return DownloadSpecDecision.Accept(); } var imported = historyForMovie.FirstOrDefault(h => @@ -62,7 +62,7 @@ public Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCrit if (imported == null) { - return Decision.Accept(); + return DownloadSpecDecision.Accept(); } // This is really only a guard against redownloading the same release over @@ -70,7 +70,7 @@ public Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCrit // match skip this check. if (lastGrabbed.Quality.Equals(imported.Quality)) { - return Decision.Accept(); + return DownloadSpecDecision.Accept(); } var release = subject.Release; @@ -82,7 +82,7 @@ public Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCrit 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"); } } @@ -91,11 +91,11 @@ public Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCrit 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/BlockedIndexerSpecification.cs b/src/NzbDrone.Core/DecisionEngine/Specifications/BlockedIndexerSpecification.cs index de462311dc..cdbaecd0a2 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(RemoteMovie subject, SearchCriteriaBase searchCriteria) + public virtual DownloadSpecDecision IsSatisfiedBy(RemoteMovie 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 113f99a6c7..8d59181597 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(RemoteMovie subject, SearchCriteriaBase searchCriteria) + public DownloadSpecDecision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCriteria) { if (_blocklistService.Blocklisted(subject.Movie.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 891a4c26d2..81d0e110f4 100644 --- a/src/NzbDrone.Core/DecisionEngine/Specifications/CustomFormatAllowedByProfileSpecification.cs +++ b/src/NzbDrone.Core/DecisionEngine/Specifications/CustomFormatAllowedByProfileSpecification.cs @@ -5,7 +5,7 @@ namespace NzbDrone.Core.DecisionEngine.Specifications { - public class CustomFormatAllowedbyProfileSpecification : IDecisionEngineSpecification + public class CustomFormatAllowedbyProfileSpecification : IDownloadDecisionEngineSpecification { private readonly Logger _logger; @@ -17,19 +17,19 @@ public CustomFormatAllowedbyProfileSpecification(Logger logger) public SpecificationPriority Priority => SpecificationPriority.Default; public RejectionType Type => RejectionType.Permanent; - public virtual Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCriteria) + public virtual DownloadSpecDecision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCriteria) { var minScore = subject.Movie.QualityProfile.MinFormatScore; var score = subject.CustomFormatScore; if (score < minScore) { - return Decision.Reject("Custom Formats {0} have score {1} below Movie's profile minimum {2}", subject.CustomFormats.ConcatToString(), score, minScore); + return DownloadSpecDecision.Reject(DownloadRejectionReason.CustomFormatMinimumScore, "Custom Formats {0} have score {1} below Movie's profile minimum {2}", subject.CustomFormats.ConcatToString(), score, minScore); } _logger.Trace("Custom Format Score of {0} [{1}] above Movie's profile minimum {2}", score, subject.CustomFormats.ConcatToString(), 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 e35a609462..2067615d27 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(RemoteMovie subject, SearchCriteriaBase searchCriteria) + public DownloadSpecDecision IsSatisfiedBy(RemoteMovie 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(RemoteMovie subject, SearchCriteriaBase searchCrit { _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(RemoteMovie subject, SearchCriteriaBase searchCrit 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(RemoteMovie subject, SearchCriteriaBase searchCrit 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/HardcodeSubsSpecification.cs b/src/NzbDrone.Core/DecisionEngine/Specifications/HardcodeSubsSpecification.cs index 16fd5efc43..dadb6fac59 100644 --- a/src/NzbDrone.Core/DecisionEngine/Specifications/HardcodeSubsSpecification.cs +++ b/src/NzbDrone.Core/DecisionEngine/Specifications/HardcodeSubsSpecification.cs @@ -7,7 +7,7 @@ namespace NzbDrone.Core.DecisionEngine.Specifications { - public class HardcodeSubsSpecification : IDecisionEngineSpecification + public class HardcodeSubsSpecification : IDownloadDecisionEngineSpecification { private readonly IConfigService _configService; private readonly Logger _logger; @@ -21,13 +21,13 @@ public HardcodeSubsSpecification(IConfigService configService, Logger logger) public SpecificationPriority Priority => SpecificationPriority.Default; public RejectionType Type => RejectionType.Permanent; - public Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCriteria) + public DownloadSpecDecision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCriteria) { var hardcodeSubs = subject.ParsedMovieInfo.HardcodedSubs; if (_configService.AllowHardcodedSubs || hardcodeSubs.IsNullOrWhiteSpace()) { - return Decision.Accept(); + return DownloadSpecDecision.Accept(); } var whitelisted = _configService.WhitelistedHardcodedSubs.Split(','); @@ -35,12 +35,12 @@ public Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCrit if (whitelisted != null && whitelisted.Any(t => (hardcodeSubs.ToLower().Contains(t.ToLower()) && t.IsNotNullOrWhiteSpace()))) { _logger.Debug("Release hardcode subs ({0}) are in allowed values ({1})", hardcodeSubs, whitelisted); - return Decision.Accept(); + return DownloadSpecDecision.Accept(); } else { _logger.Debug("Hardcode subs found: {0}", hardcodeSubs); - return Decision.Reject("Hardcode subs found: {0}", hardcodeSubs); + return DownloadSpecDecision.Reject(DownloadRejectionReason.HardcodeSubtitles, "Hardcode subs found: {0}", hardcodeSubs); } } } diff --git a/src/NzbDrone.Core/DecisionEngine/Specifications/IDecisionEngineSpecification.cs b/src/NzbDrone.Core/DecisionEngine/Specifications/IDownloadDecisionEngineSpecification.cs similarity index 60% rename from src/NzbDrone.Core/DecisionEngine/Specifications/IDecisionEngineSpecification.cs rename to src/NzbDrone.Core/DecisionEngine/Specifications/IDownloadDecisionEngineSpecification.cs index 9d94e945f1..c4bb1a49cb 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(RemoteMovie subject, SearchCriteriaBase searchCriteria); + DownloadSpecDecision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCriteria); } } diff --git a/src/NzbDrone.Core/DecisionEngine/Specifications/LanguageSpecification.cs b/src/NzbDrone.Core/DecisionEngine/Specifications/LanguageSpecification.cs index eb557b170c..c480ea20dc 100644 --- a/src/NzbDrone.Core/DecisionEngine/Specifications/LanguageSpecification.cs +++ b/src/NzbDrone.Core/DecisionEngine/Specifications/LanguageSpecification.cs @@ -5,7 +5,7 @@ namespace NzbDrone.Core.DecisionEngine.Specifications { - public class LanguageSpecification : IDecisionEngineSpecification + public class LanguageSpecification : IDownloadDecisionEngineSpecification { private readonly Logger _logger; @@ -17,14 +17,14 @@ public LanguageSpecification(Logger logger) public SpecificationPriority Priority => SpecificationPriority.Default; public RejectionType Type => RejectionType.Permanent; - public virtual Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCriteria) + public virtual DownloadSpecDecision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCriteria) { var wantedLanguage = subject.Movie.QualityProfile.Language; if (wantedLanguage == Language.Any) { _logger.Debug("Profile allows any language, accepting release."); - return Decision.Accept(); + return DownloadSpecDecision.Accept(); } var originalLanguage = subject.Movie.MovieMetadata.Value.OriginalLanguage; @@ -34,10 +34,10 @@ public virtual Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase se if (!subject.Languages.Contains(originalLanguage)) { _logger.Debug("Original Language({0}) is wanted, but found {1}", originalLanguage, subject.Languages.ToExtendedString()); - return Decision.Reject("Original Language ({0}) is wanted, but found {1}", originalLanguage, subject.Languages.ToExtendedString()); + return DownloadSpecDecision.Reject(DownloadRejectionReason.WantedLanguage, "Original Language ({0}) is wanted, but found {1}", originalLanguage, subject.Languages.ToExtendedString()); } - return Decision.Accept(); + return DownloadSpecDecision.Accept(); } _logger.Debug("Checking if report meets language requirements. {0}", subject.ParsedMovieInfo.Languages.ToExtendedString()); @@ -45,10 +45,10 @@ public virtual Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase se if (!subject.Languages.Contains(wantedLanguage)) { _logger.Debug("Report Language: {0} rejected because it is not wanted, wanted {1}", subject.Languages.ToExtendedString(), wantedLanguage); - return Decision.Reject("{0} is wanted, but found {1}", wantedLanguage, subject.Languages.ToExtendedString()); + return DownloadSpecDecision.Reject(DownloadRejectionReason.WantedLanguage, "{0} is wanted, but found {1}", wantedLanguage, subject.Languages.ToExtendedString()); } - return Decision.Accept(); + return DownloadSpecDecision.Accept(); } } } diff --git a/src/NzbDrone.Core/DecisionEngine/Specifications/MaximumSizeSpecification.cs b/src/NzbDrone.Core/DecisionEngine/Specifications/MaximumSizeSpecification.cs index 65e42c5e20..731a62001b 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(RemoteMovie subject, SearchCriteriaBase searchCriteria) + public DownloadSpecDecision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCriteria) { var size = subject.Release.Size; var maximumSize = _configService.MaximumSize.Megabytes(); @@ -28,13 +28,13 @@ public Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCrit 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(RemoteMovie subject, SearchCriteriaBase searchCrit 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 53e228de03..d979fd5958 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(RemoteMovie subject, SearchCriteriaBase searchCriteria) + public virtual DownloadSpecDecision IsSatisfiedBy(RemoteMovie 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(RemoteMovie subject, SearchCriteriaBase se 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(RemoteMovie subject, SearchCriteriaBase se 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/NotSampleSpecification.cs b/src/NzbDrone.Core/DecisionEngine/Specifications/NotSampleSpecification.cs index 0c4a9cdec3..37c4347a64 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(RemoteMovie subject, SearchCriteriaBase searchCriteria) + public DownloadSpecDecision IsSatisfiedBy(RemoteMovie 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 abf8abc00f..96f14bcfde 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(RemoteMovie subject, SearchCriteriaBase searchCriteria) + public virtual DownloadSpecDecision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCriteria) { var delayProfile = _delayProfileService.BestForTags(subject.Movie.Tags); if (subject.Release.DownloadProtocol == DownloadProtocol.Usenet && !delayProfile.EnableUsenet) { _logger.Debug("[{0}] Usenet is not enabled for this movie", subject.Release.Title); - return Decision.Reject("Usenet is not enabled for this movie"); + return DownloadSpecDecision.Reject(DownloadRejectionReason.ProtocolDisabled, "Usenet is not enabled for this movie"); } if (subject.Release.DownloadProtocol == DownloadProtocol.Torrent && !delayProfile.EnableTorrent) { _logger.Debug("[{0}] Torrent is not enabled for this movie", subject.Release.Title); - return Decision.Reject("Torrent is not enabled for this movie"); + return DownloadSpecDecision.Reject(DownloadRejectionReason.ProtocolDisabled, "Torrent is not enabled for this movie"); } - 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 88a1432f88..d360f035e3 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(RemoteMovie subject, SearchCriteriaBase searchCriteria) + public virtual DownloadSpecDecision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCriteria) { _logger.Debug("Checking if report meets quality requirements. {0}", subject.ParsedMovieInfo.Quality); @@ -27,10 +27,10 @@ public virtual Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase se if (!qualityOrGroup.Allowed) { _logger.Debug("Quality {0} rejected by Movie's quality profile", subject.ParsedMovieInfo.Quality); - return Decision.Reject("{0} is not wanted in profile", subject.ParsedMovieInfo.Quality.Quality); + return DownloadSpecDecision.Reject(DownloadRejectionReason.QualityNotWanted, "{0} is not wanted in profile", subject.ParsedMovieInfo.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 f4d96eb763..8eefb2ada3 100644 --- a/src/NzbDrone.Core/DecisionEngine/Specifications/QueueSpecification.cs +++ b/src/NzbDrone.Core/DecisionEngine/Specifications/QueueSpecification.cs @@ -11,7 +11,7 @@ namespace NzbDrone.Core.DecisionEngine.Specifications { - public class QueueSpecification : IDecisionEngineSpecification + public class QueueSpecification : IDownloadDecisionEngineSpecification { private readonly IQueueService _queueService; private readonly UpgradableSpecification _upgradableSpecification; @@ -35,7 +35,7 @@ public QueueSpecification(IQueueService queueService, public SpecificationPriority Priority => SpecificationPriority.Default; public RejectionType Type => RejectionType.Permanent; - public Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCriteria) + public DownloadSpecDecision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCriteria) { var queue = _queueService.GetQueue(); var matchingMovies = queue.Where(q => q.RemoteMovie?.Movie != null && @@ -66,7 +66,7 @@ public Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCrit queuedItemCustomFormats, subject.ParsedMovieInfo.Quality)) { - return Decision.Reject("Quality for release in queue already meets cutoff: {0}", remoteMovie.ParsedMovieInfo.Quality); + return DownloadSpecDecision.Reject(DownloadRejectionReason.QueueCutoffMet, "Quality for release in queue already meets cutoff: {0}", remoteMovie.ParsedMovieInfo.Quality); } _logger.Debug("Checking if release is higher quality than queued release. Queued quality is: {0}", remoteMovie.ParsedMovieInfo.Quality); @@ -80,25 +80,25 @@ public Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCrit switch (upgradeableRejectReason) { case UpgradeableRejectReason.BetterQuality: - return Decision.Reject("Release in queue is of equal or higher preference: {0}", remoteMovie.ParsedMovieInfo.Quality); + return DownloadSpecDecision.Reject(DownloadRejectionReason.QueueHigherPreference, "Release in queue is of equal or higher preference: {0}", remoteMovie.ParsedMovieInfo.Quality); case UpgradeableRejectReason.BetterRevision: - return Decision.Reject("Release in queue is of equal or higher revision: {0}", remoteMovie.ParsedMovieInfo.Quality.Revision); + return DownloadSpecDecision.Reject(DownloadRejectionReason.QueueHigherRevision, "Release in queue is of equal or higher revision: {0}", remoteMovie.ParsedMovieInfo.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); case UpgradeableRejectReason.UpgradesNotAllowed: - return Decision.Reject("Release in queue and Quality Profile '{0}' does not allow upgrades", qualityProfile.Name); + return DownloadSpecDecision.Reject(DownloadRejectionReason.QueueUpgradesNotAllowed, "Release in queue and Quality Profile '{0}' does not allow upgrades", qualityProfile.Name); } if (_upgradableSpecification.IsRevisionUpgrade(remoteMovie.ParsedMovieInfo.Quality, subject.ParsedMovieInfo.Quality)) @@ -106,12 +106,12 @@ public Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCrit 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 bde4efcfc5..884fb6f9d1 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(RemoteMovie subject, SearchCriteriaBase searchCriteria) + public virtual DownloadSpecDecision IsSatisfiedBy(RemoteMovie 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(RemoteMovie subject, SearchCriteriaBase se 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 232b42d61e..3f0f360029 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(RemoteMovie subject, SearchCriteriaBase searchCriteria) + public virtual DownloadSpecDecision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCriteria) { _logger.Debug("Checking if release meets restrictions: {0}", subject); @@ -43,7 +43,7 @@ public virtual Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase se { 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(RemoteMovie subject, SearchCriteriaBase se { 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 f6d713281b..eb18c53130 100644 --- a/src/NzbDrone.Core/DecisionEngine/Specifications/RepackSpecification.cs +++ b/src/NzbDrone.Core/DecisionEngine/Specifications/RepackSpecification.cs @@ -8,7 +8,7 @@ namespace NzbDrone.Core.DecisionEngine.Specifications { - public class RepackSpecification : IDecisionEngineSpecification + public class RepackSpecification : IDownloadDecisionEngineSpecification { private readonly UpgradableSpecification _upgradableSpecification; private readonly IConfigService _configService; @@ -24,19 +24,19 @@ public RepackSpecification(UpgradableSpecification upgradableSpecification, ICon public SpecificationPriority Priority => SpecificationPriority.Database; public RejectionType Type => RejectionType.Permanent; - public Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCriteria) + public DownloadSpecDecision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCriteria) { var downloadPropersAndRepacks = _configService.DownloadPropersAndRepacks; if (!subject.ParsedMovieInfo.Quality.Revision.IsRepack) { - return Decision.Accept(); + return DownloadSpecDecision.Accept(); } if (downloadPropersAndRepacks == ProperDownloadTypes.DoNotPrefer) { _logger.Debug("Repacks are not preferred, skipping check"); - return Decision.Accept(); + return DownloadSpecDecision.Accept(); } if (subject.Movie.MovieFileId != 0) @@ -51,17 +51,17 @@ public Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCrit 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"); } 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)) @@ -70,7 +70,8 @@ public Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCrit "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); @@ -78,7 +79,7 @@ public Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCrit } } - return Decision.Accept(); + return DownloadSpecDecision.Accept(); } } } diff --git a/src/NzbDrone.Core/DecisionEngine/Specifications/RequiredIndexerFlagsSpecification.cs b/src/NzbDrone.Core/DecisionEngine/Specifications/RequiredIndexerFlagsSpecification.cs index 91a682fd6f..9dddd51ea6 100644 --- a/src/NzbDrone.Core/DecisionEngine/Specifications/RequiredIndexerFlagsSpecification.cs +++ b/src/NzbDrone.Core/DecisionEngine/Specifications/RequiredIndexerFlagsSpecification.cs @@ -7,7 +7,7 @@ namespace NzbDrone.Core.DecisionEngine.Specifications { - public class RequiredIndexerFlagsSpecification : IDecisionEngineSpecification + public class RequiredIndexerFlagsSpecification : IDownloadDecisionEngineSpecification { private readonly IIndexerFactory _indexerFactory; private readonly Logger _logger; @@ -21,7 +21,7 @@ public RequiredIndexerFlagsSpecification(IIndexerFactory indexerFactory, Logger public SpecificationPriority Priority => SpecificationPriority.Default; public RejectionType Type => RejectionType.Permanent; - public Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCriteria) + public DownloadSpecDecision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCriteria) { var torrentInfo = subject.Release; @@ -37,34 +37,35 @@ public Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCrit if (torrentInfo == null || indexerSettings == null) { - return Decision.Accept(); + return DownloadSpecDecision.Accept(); } if (indexerSettings is ITorrentIndexerSettings torrentIndexerSettings) { var requiredFlags = torrentIndexerSettings.RequiredFlags; - var requiredFlag = (IndexerFlags)0; if (requiredFlags == null || !requiredFlags.Any()) { - return Decision.Accept(); + return DownloadSpecDecision.Accept(); } + var requiredFlag = (IndexerFlags)0; + foreach (var flag in requiredFlags) { if (torrentInfo.IndexerFlags.HasFlag((IndexerFlags)flag)) { - return Decision.Accept(); + return DownloadSpecDecision.Accept(); } requiredFlag |= (IndexerFlags)flag; } _logger.Debug("None of the required indexer flags {0} where found. Found flags: {1}", requiredFlag, torrentInfo.IndexerFlags); - return Decision.Reject("None of the required indexer flags {0} where found. Found flags: {1}", requiredFlag, torrentInfo.IndexerFlags); + return DownloadSpecDecision.Reject(DownloadRejectionReason.RequiredFlags, "None of the required indexer flags {0} where found. Found flags: {1}", requiredFlag, torrentInfo.IndexerFlags); } - 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 f9860b6df2..8f34570ed6 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(RemoteMovie subject, SearchCriteriaBase searchCriteria) + public virtual DownloadSpecDecision IsSatisfiedBy(RemoteMovie 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(RemoteMovie subject, SearchCriteriaBase se 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/AvailabilitySpecification.cs b/src/NzbDrone.Core/DecisionEngine/Specifications/RssSync/AvailabilitySpecification.cs index 036be785bb..2a5d64ed42 100644 --- a/src/NzbDrone.Core/DecisionEngine/Specifications/RssSync/AvailabilitySpecification.cs +++ b/src/NzbDrone.Core/DecisionEngine/Specifications/RssSync/AvailabilitySpecification.cs @@ -5,7 +5,7 @@ namespace NzbDrone.Core.DecisionEngine.Specifications.RssSync { - public class AvailabilitySpecification : IDecisionEngineSpecification + public class AvailabilitySpecification : IDownloadDecisionEngineSpecification { private readonly IConfigService _configService; private readonly Logger _logger; @@ -19,22 +19,22 @@ public AvailabilitySpecification(IConfigService configService, Logger logger) public SpecificationPriority Priority => SpecificationPriority.Default; public RejectionType Type => RejectionType.Permanent; - public Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCriteria) + public DownloadSpecDecision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCriteria) { if (searchCriteria is { UserInvokedSearch: true }) { _logger.Debug("Skipping availability check during search"); - return Decision.Accept(); + return DownloadSpecDecision.Accept(); } var availabilityDelay = _configService.AvailabilityDelay; if (!subject.Movie.IsAvailable(availabilityDelay)) { - return Decision.Reject("Movie {0} will only be considered available {1} days after {2}", subject.Movie, availabilityDelay, subject.Movie.MinimumAvailability.ToString()); + return DownloadSpecDecision.Reject(DownloadRejectionReason.Availability, "Movie {0} will only be considered available {1} days after {2}", subject.Movie, availabilityDelay, subject.Movie.MinimumAvailability.ToString()); } - 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 d7640e25b6..19eb003a46 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 IUpgradableSpecification _qualityUpgradableSpecification; @@ -32,12 +32,12 @@ public DelaySpecification(IPendingReleaseService pendingReleaseService, public SpecificationPriority Priority => SpecificationPriority.Database; public RejectionType Type => RejectionType.Temporary; - public virtual Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCriteria) + public virtual DownloadSpecDecision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCriteria) { if (searchCriteria != null && searchCriteria.UserInvokedSearch) { _logger.Debug("Ignoring delay for user invoked search"); - return Decision.Accept(); + return DownloadSpecDecision.Accept(); } var profile = subject.Movie.QualityProfile; @@ -48,7 +48,7 @@ public virtual Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase se if (delay == 0) { _logger.Debug("Delay Profile does not require a waiting period before download for {0}.", subject.Release.DownloadProtocol); - return Decision.Accept(); + return DownloadSpecDecision.Accept(); } _logger.Debug("Delay Profile requires a waiting period of {0} minutes for {1}", delay, subject.Release.DownloadProtocol); @@ -73,7 +73,7 @@ public virtual Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase se if (revisionUpgrade) { _logger.Debug("New quality is a better revision for existing quality, skipping delay"); - return Decision.Accept(); + return DownloadSpecDecision.Accept(); } } } @@ -87,7 +87,7 @@ public virtual Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase se if (isBestInProfile && isPreferredProtocol) { _logger.Debug("Quality is highest in profile for preferred protocol, will not delay."); - return Decision.Accept(); + return DownloadSpecDecision.Accept(); } } @@ -100,7 +100,7 @@ public virtual Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase se 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(); } } @@ -109,16 +109,16 @@ public virtual Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase se if (oldest != null && oldest.Release.AgeMinutes > delay) { _logger.Debug("Oldest pending release {0} has been delayed for {1}, longer than the set delay of {2}. Release will be accepted", oldest.Release.Title, 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/HistorySpecification.cs b/src/NzbDrone.Core/DecisionEngine/Specifications/RssSync/HistorySpecification.cs index 48b0709723..1171c6ff8b 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(RemoteMovie subject, SearchCriteriaBase searchCriteria) + public virtual DownloadSpecDecision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCriteria) { if (searchCriteria != null) { _logger.Debug("Skipping history check during search"); - return Decision.Accept(); + return DownloadSpecDecision.Accept(); } var cdhEnabled = _configService.EnableCompletedDownloadHandling; @@ -55,7 +55,7 @@ public virtual Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase se if (!recent && cdhEnabled) { - return Decision.Accept(); + return DownloadSpecDecision.Accept(); } var customFormats = _formatService.ParseCustomFormat(mostRecent, subject.Movie); @@ -77,10 +77,10 @@ public virtual Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase se { 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"; @@ -88,32 +88,32 @@ public virtual Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase se switch (upgradeableRejectReason) { case UpgradeableRejectReason.None: - return Decision.Accept(); + return DownloadSpecDecision.Accept(); 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); case UpgradeableRejectReason.UpgradesNotAllowed: - return Decision.Reject("{0} grab event in history and Quality Profile '{1}' does not allow upgrades", rejectionSubject, qualityProfile.Name); + return DownloadSpecDecision.Reject(DownloadRejectionReason.HistoryUpgradesNotAllowed, "{0} grab event in history and Quality Profile '{1}' does not allow upgrades", rejectionSubject, qualityProfile.Name); } } - 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 4f02eb0656..ff391b96e9 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(RemoteMovie subject, SearchCriteriaBase searchCriteria) + public virtual DownloadSpecDecision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCriteria) { if (subject.Release == null || subject.Movie?.Tags == null || subject.Release.IndexerId == 0) { - return Decision.Accept(); + return DownloadSpecDecision.Accept(); } IndexerDefinition indexer; @@ -37,7 +37,7 @@ public virtual Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase se 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(RemoteMovie subject, SearchCriteriaBase se { _logger.Debug("Indexer {0} has tags. None of these are present on movie {1}. Rejecting", subject.Release.Indexer, subject.Movie); - return Decision.Reject("Movie tags do not match any of the indexer tags"); + return DownloadSpecDecision.Reject(DownloadRejectionReason.NoMatchingTag, "Movie tags do not match any of the indexer tags"); } - return Decision.Accept(); + return DownloadSpecDecision.Accept(); } } } diff --git a/src/NzbDrone.Core/DecisionEngine/Specifications/RssSync/MonitoredMovieSpecification.cs b/src/NzbDrone.Core/DecisionEngine/Specifications/RssSync/MonitoredMovieSpecification.cs index 0e032a03b2..de8be51e82 100644 --- a/src/NzbDrone.Core/DecisionEngine/Specifications/RssSync/MonitoredMovieSpecification.cs +++ b/src/NzbDrone.Core/DecisionEngine/Specifications/RssSync/MonitoredMovieSpecification.cs @@ -4,7 +4,7 @@ namespace NzbDrone.Core.DecisionEngine.Specifications.RssSync { - public class MonitoredMovieSpecification : IDecisionEngineSpecification + public class MonitoredMovieSpecification : IDownloadDecisionEngineSpecification { private readonly Logger _logger; @@ -16,23 +16,24 @@ public MonitoredMovieSpecification(Logger logger) public SpecificationPriority Priority => SpecificationPriority.Default; public RejectionType Type => RejectionType.Permanent; - public Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCriteria) + public DownloadSpecDecision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCriteria) { if (searchCriteria != null) { if (searchCriteria.UserInvokedSearch) { _logger.Debug("Skipping monitored check during search"); - return Decision.Accept(); + return DownloadSpecDecision.Accept(); } } if (!subject.Movie.Monitored) { - return Decision.Reject("Movie is not monitored"); + _logger.Debug("{0} is present in the DB but not tracked. Rejecting", subject.Movie); + return DownloadSpecDecision.Reject(DownloadRejectionReason.MovieNotMonitored, "Movie is not monitored"); } - return Decision.Accept(); + return DownloadSpecDecision.Accept(); } } } diff --git a/src/NzbDrone.Core/DecisionEngine/Specifications/RssSync/ProperSpecification.cs b/src/NzbDrone.Core/DecisionEngine/Specifications/RssSync/ProperSpecification.cs index 74d823849f..13b6218693 100644 --- a/src/NzbDrone.Core/DecisionEngine/Specifications/RssSync/ProperSpecification.cs +++ b/src/NzbDrone.Core/DecisionEngine/Specifications/RssSync/ProperSpecification.cs @@ -7,7 +7,7 @@ namespace NzbDrone.Core.DecisionEngine.Specifications.RssSync { - public class ProperSpecification : IDecisionEngineSpecification + public class ProperSpecification : IDownloadDecisionEngineSpecification { private readonly UpgradableSpecification _qualityUpgradableSpecification; private readonly IConfigService _configService; @@ -23,11 +23,11 @@ public ProperSpecification(UpgradableSpecification qualityUpgradableSpecificatio public SpecificationPriority Priority => SpecificationPriority.Default; public RejectionType Type => RejectionType.Permanent; - public virtual Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCriteria) + public virtual DownloadSpecDecision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCriteria) { if (searchCriteria != null) { - return Decision.Accept(); + return DownloadSpecDecision.Accept(); } var downloadPropersAndRepacks = _configService.DownloadPropersAndRepacks; @@ -35,12 +35,12 @@ public virtual Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase se if (downloadPropersAndRepacks == ProperDownloadTypes.DoNotPrefer) { _logger.Debug("Propers are not preferred, skipping check"); - return Decision.Accept(); + return DownloadSpecDecision.Accept(); } if (subject.Movie.MovieFile == null) { - return Decision.Accept(); + return DownloadSpecDecision.Accept(); } var file = subject.Movie.MovieFile; @@ -50,17 +50,17 @@ public virtual Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase se 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/Search/MovieSpecification.cs b/src/NzbDrone.Core/DecisionEngine/Specifications/Search/MovieSpecification.cs index 5715c8d388..f7be18f9ba 100644 --- a/src/NzbDrone.Core/DecisionEngine/Specifications/Search/MovieSpecification.cs +++ b/src/NzbDrone.Core/DecisionEngine/Specifications/Search/MovieSpecification.cs @@ -4,7 +4,7 @@ namespace NzbDrone.Core.DecisionEngine.Specifications.Search { - public class MovieSpecification : IDecisionEngineSpecification + public class MovieSpecification : IDownloadDecisionEngineSpecification { private readonly Logger _logger; @@ -16,11 +16,11 @@ public MovieSpecification(Logger logger) public SpecificationPriority Priority => SpecificationPriority.Default; public RejectionType Type => RejectionType.Permanent; - public Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCriteria) + public DownloadSpecDecision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCriteria) { if (searchCriteria == null) { - return Decision.Accept(); + return DownloadSpecDecision.Accept(); } _logger.Debug("Checking if movie matches searched movie"); @@ -28,10 +28,10 @@ public Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCrit if (subject.Movie.Id != searchCriteria.Movie.Id) { _logger.Debug("Movie {0} does not match {1}", subject.Movie, searchCriteria.Movie); - return Decision.Reject("Wrong movie"); + return DownloadSpecDecision.Reject(DownloadRejectionReason.WrongMovie, "Wrong movie"); } - 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 a7a6d53697..507f1f1a48 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(RemoteMovie subject, SearchCriteriaBase searchCriteria) + public DownloadSpecDecision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCriteria) { var torrentInfo = subject.Release as TorrentInfo; if (torrentInfo == null || torrentInfo.IndexerId == 0) { - return Decision.Accept(); + return DownloadSpecDecision.Accept(); } IndexerDefinition indexer; @@ -37,7 +37,7 @@ public Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCrit 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(RemoteMovie subject, SearchCriteriaBase searchCrit 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 728c6e3f45..44b0145157 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(RemoteMovie subject, SearchCriteriaBase searchCriteria) + public virtual DownloadSpecDecision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCriteria) { var qualityProfile = subject.Movie.QualityProfile; @@ -35,7 +35,7 @@ public virtual Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase se if (file == null) { _logger.Debug("File is no longer available, skipping this file."); - return Decision.Accept(); + return DownloadSpecDecision.Accept(); } file.Movie = subject.Movie; @@ -50,11 +50,11 @@ public virtual Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase se { _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 ac864b0b39..ed7503c6b5 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(RemoteMovie subject, SearchCriteriaBase searchCriteria) + public virtual DownloadSpecDecision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCriteria) { var qualityProfile = subject.Movie.QualityProfile; @@ -33,7 +33,7 @@ public virtual Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase se if (file == null) { _logger.Debug("File is no longer available, skipping this file."); - return Decision.Accept(); + return DownloadSpecDecision.Accept(); } file.Movie = subject.Movie; @@ -52,7 +52,7 @@ public virtual Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase se 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} [{1}]", qualityCutoff, customFormats.ConcatToString()); + return DownloadSpecDecision.Reject(DownloadRejectionReason.DiskCutoffMet, "Existing file meets cutoff: {0} [{1}]", qualityCutoff, customFormats.ConcatToString()); } var upgradeableRejectReason = _upgradableSpecification.IsUpgradable(qualityProfile, @@ -64,31 +64,31 @@ public virtual Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase se switch (upgradeableRejectReason) { case UpgradeableRejectReason.None: - return Decision.Accept(); + return DownloadSpecDecision.Accept(); 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); case UpgradeableRejectReason.UpgradesNotAllowed: - return Decision.Reject("Existing file on disk and Quality Profile '{0}' does not allow upgrades", qualityProfile.Name); + return DownloadSpecDecision.Reject(DownloadRejectionReason.DiskUpgradesNotAllowed, "Existing file on disk and Quality Profile '{0}' does not allow upgrades", qualityProfile.Name); } - return Decision.Accept(); + return DownloadSpecDecision.Accept(); } } } diff --git a/src/NzbDrone.Core/MediaFiles/DownloadedMovieImportService.cs b/src/NzbDrone.Core/MediaFiles/DownloadedMovieImportService.cs index 465ad0ab49..e8a0b715c0 100644 --- a/src/NzbDrone.Core/MediaFiles/DownloadedMovieImportService.cs +++ b/src/NzbDrone.Core/MediaFiles/DownloadedMovieImportService.cs @@ -7,7 +7,6 @@ using NzbDrone.Common.EnvironmentInfo; using NzbDrone.Common.Extensions; using NzbDrone.Core.Configuration; -using NzbDrone.Core.DecisionEngine; using NzbDrone.Core.Download; using NzbDrone.Core.History; using NzbDrone.Core.MediaFiles.MovieImport; @@ -186,7 +185,7 @@ private List ProcessFolder(DirectoryInfo directoryInfo, ImportMode _logger.Warn("Unable to process folder that is mapped to an existing movie"); return new List { - RejectionResult("Import path is mapped to a movie folder") + RejectionResult(ImportRejectionReason.MovieFolder, "Import path is mapped to a movie folder") }; } @@ -272,7 +271,7 @@ private List ProcessFile(FileInfo fileInfo, ImportMode importMode, return new List { - new ImportResult(new ImportDecision(new LocalMovie { Path = fileInfo.FullName }, new Rejection("Invalid video file, filename starts with '._'")), "Invalid video file, filename starts with '._'") + new ImportResult(new ImportDecision(new LocalMovie { Path = fileInfo.FullName }, new ImportRejection(ImportRejectionReason.InvalidFilePath, "Invalid video file, filename starts with '._'")), "Invalid video file, filename starts with '._'") }; } @@ -285,7 +284,7 @@ private List ProcessFile(FileInfo fileInfo, ImportMode importMode, return new List { new ImportResult(new ImportDecision(new LocalMovie { 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}'") }; } @@ -317,19 +316,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 LocalMovie { Path = videoFile }, new Rejection("Locked file, try again later")), "Locked file, try again later"); + return new ImportResult(new ImportDecision(new LocalMovie { Path = videoFile }, new ImportRejection(ImportRejectionReason.FileLocked, "Locked file, try again later")), "Locked file, try again later"); } private ImportResult UnknownMovieResult(string message, string videoFile = null) { var localMovie = videoFile == null ? null : new LocalMovie { Path = videoFile }; - return new ImportResult(new ImportDecision(localMovie, new Rejection("Unknown Movie")), message); + return new ImportResult(new ImportDecision(localMovie, new ImportRejection(ImportRejectionReason.UnknownMovie, "Unknown Movie")), 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) @@ -338,12 +337,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/MovieImport/IImportDecisionEngineSpecification.cs b/src/NzbDrone.Core/MediaFiles/MovieImport/IImportDecisionEngineSpecification.cs index 9d6fe6f0a0..20d16b3dfd 100644 --- a/src/NzbDrone.Core/MediaFiles/MovieImport/IImportDecisionEngineSpecification.cs +++ b/src/NzbDrone.Core/MediaFiles/MovieImport/IImportDecisionEngineSpecification.cs @@ -1,4 +1,3 @@ -using NzbDrone.Core.DecisionEngine; using NzbDrone.Core.Download; using NzbDrone.Core.Parser.Model; @@ -6,6 +5,6 @@ namespace NzbDrone.Core.MediaFiles.MovieImport { public interface IImportDecisionEngineSpecification { - Decision IsSatisfiedBy(LocalMovie localMovie, DownloadClientItem downloadClientItem); + ImportSpecDecision IsSatisfiedBy(LocalMovie localMovie, DownloadClientItem downloadClientItem); } } diff --git a/src/NzbDrone.Core/MediaFiles/MovieImport/ImportApprovedMovie.cs b/src/NzbDrone.Core/MediaFiles/MovieImport/ImportApprovedMovie.cs index 4d14c0620d..5bf3cad387 100644 --- a/src/NzbDrone.Core/MediaFiles/MovieImport/ImportApprovedMovie.cs +++ b/src/NzbDrone.Core/MediaFiles/MovieImport/ImportApprovedMovie.cs @@ -203,7 +203,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/MovieImport/ImportDecision.cs b/src/NzbDrone.Core/MediaFiles/MovieImport/ImportDecision.cs index a8574e64c0..7f33d08ff1 100644 --- a/src/NzbDrone.Core/MediaFiles/MovieImport/ImportDecision.cs +++ b/src/NzbDrone.Core/MediaFiles/MovieImport/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.MovieImport @@ -9,24 +8,14 @@ namespace NzbDrone.Core.MediaFiles.MovieImport public class ImportDecision { public LocalMovie LocalMovie { get; private set; } - public IEnumerable Rejections { get; private set; } + public IEnumerable Rejections { get; private set; } public bool Approved => Rejections.Empty(); - public ImportDecision(LocalMovie localMovie, params Rejection[] rejections) + public ImportDecision(LocalMovie localMovie, params ImportRejection[] rejections) { LocalMovie = localMovie; Rejections = rejections.ToList(); - - // LocalMovie = new LocalMovie - // { - // Quality = localMovie.Quality, - // ExistingFile = localMovie.ExistingFile, - // MediaInfo = localMovie.MediaInfo, - // ParsedMovieInfo = localMovie.ParsedMovieInfo, - // Path = localMovie.Path, - // Size = localMovie.Size - // }; } } } diff --git a/src/NzbDrone.Core/MediaFiles/MovieImport/ImportDecisionMaker.cs b/src/NzbDrone.Core/MediaFiles/MovieImport/ImportDecisionMaker.cs index 33028a76af..46c464fefd 100644 --- a/src/NzbDrone.Core/MediaFiles/MovieImport/ImportDecisionMaker.cs +++ b/src/NzbDrone.Core/MediaFiles/MovieImport/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.MovieImport.Aggregation; @@ -128,7 +127,7 @@ private ImportDecision GetDecision(LocalMovie localMovie, DownloadClientItem dow if (localMovie.Movie == null) { - decision = new ImportDecision(localMovie, new Rejection("Invalid movie")); + decision = new ImportDecision(localMovie, new ImportRejection(ImportRejectionReason.InvalidMovie, "Invalid movie")); } else { @@ -150,13 +149,13 @@ private ImportDecision GetDecision(LocalMovie localMovie, DownloadClientItem dow } catch (AugmentingFailedException) { - decision = new ImportDecision(localMovie, new Rejection("Unable to parse file")); + decision = new ImportDecision(localMovie, new ImportRejection(ImportRejectionReason.UnableToParse, "Unable to parse file")); } catch (Exception ex) { _logger.Error(ex, "Couldn't import file. {0}", localMovie.Path); - decision = new ImportDecision(localMovie, new Rejection("Unexpected error processing file")); + decision = new ImportDecision(localMovie, new ImportRejection(ImportRejectionReason.Error, "Unexpected error processing file")); } if (decision == null) @@ -175,7 +174,7 @@ private ImportDecision GetDecision(LocalMovie localMovie, DownloadClientItem dow return decision; } - private Rejection EvaluateSpec(IImportDecisionEngineSpecification spec, LocalMovie localMovie, DownloadClientItem downloadClientItem) + private ImportRejection EvaluateSpec(IImportDecisionEngineSpecification spec, LocalMovie localMovie, DownloadClientItem downloadClientItem) { try { @@ -183,7 +182,7 @@ private Rejection EvaluateSpec(IImportDecisionEngineSpecification spec, LocalMov if (!result.Accepted) { - return new Rejection(result.Reason); + return new ImportRejection(result.Reason, result.Message); } } catch (NotImplementedException e) @@ -194,7 +193,7 @@ private Rejection EvaluateSpec(IImportDecisionEngineSpecification spec, LocalMov catch (Exception ex) { _logger.Error(ex, "Couldn't evaluate decision on {0}", localMovie.Path); - return new Rejection($"{spec.GetType().Name}: {ex.Message}"); + return new ImportRejection(ImportRejectionReason.DecisionError, $"{spec.GetType().Name}: {ex.Message}"); } return null; diff --git a/src/NzbDrone.Core/MediaFiles/MovieImport/ImportRejection.cs b/src/NzbDrone.Core/MediaFiles/MovieImport/ImportRejection.cs new file mode 100644 index 0000000000..8a75de9e4e --- /dev/null +++ b/src/NzbDrone.Core/MediaFiles/MovieImport/ImportRejection.cs @@ -0,0 +1,11 @@ +using NzbDrone.Core.DecisionEngine; + +namespace NzbDrone.Core.MediaFiles.MovieImport; + +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/MovieImport/ImportRejectionReason.cs b/src/NzbDrone.Core/MediaFiles/MovieImport/ImportRejectionReason.cs new file mode 100644 index 0000000000..9240599fd7 --- /dev/null +++ b/src/NzbDrone.Core/MediaFiles/MovieImport/ImportRejectionReason.cs @@ -0,0 +1,28 @@ +namespace NzbDrone.Core.MediaFiles.MovieImport; + +public enum ImportRejectionReason +{ + Unknown, + FileLocked, + UnknownMovie, + ExecutableFile, + ArchiveFile, + MovieFolder, + InvalidFilePath, + UnsupportedExtension, + InvalidMovie, + UnableToParse, + Error, + DecisionError, + MovieAlreadyImported, + MinimumFreeSpace, + NoAudio, + MovieNotFoundInRelease, + Sample, + SampleIndeterminate, + Unpacking, + MultiPartMovie, + NotQualityUpgrade, + NotRevisionUpgrade, + NotCustomFormatUpgrade +} diff --git a/src/NzbDrone.Core/MediaFiles/MovieImport/ImportSpecDecision.cs b/src/NzbDrone.Core/MediaFiles/MovieImport/ImportSpecDecision.cs new file mode 100644 index 0000000000..d796073bcd --- /dev/null +++ b/src/NzbDrone.Core/MediaFiles/MovieImport/ImportSpecDecision.cs @@ -0,0 +1,34 @@ +namespace NzbDrone.Core.MediaFiles.MovieImport +{ + 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/MovieImport/Manual/ManualImportItem.cs b/src/NzbDrone.Core/MediaFiles/MovieImport/Manual/ManualImportItem.cs index a809868fb1..af6bf94d1d 100644 --- a/src/NzbDrone.Core/MediaFiles/MovieImport/Manual/ManualImportItem.cs +++ b/src/NzbDrone.Core/MediaFiles/MovieImport/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.Movies; using NzbDrone.Core.Qualities; @@ -21,7 +20,7 @@ public class ManualImportItem public List CustomFormats { get; set; } public int CustomFormatScore { get; set; } public int IndexerFlags { get; set; } - public IEnumerable Rejections { get; set; } + public IEnumerable Rejections { get; set; } public Movie Movie { get; set; } public ManualImportItem() diff --git a/src/NzbDrone.Core/MediaFiles/MovieImport/Manual/ManualImportService.cs b/src/NzbDrone.Core/MediaFiles/MovieImport/Manual/ManualImportService.cs index 4bc6fe16c5..cfb96548e5 100644 --- a/src/NzbDrone.Core/MediaFiles/MovieImport/Manual/ManualImportService.cs +++ b/src/NzbDrone.Core/MediaFiles/MovieImport/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; @@ -255,7 +254,11 @@ private ManualImportItem ProcessFile(string rootFolder, string baseFolder, strin localMovie.Languages = LanguageParser.ParseLanguages(file); localMovie.Size = _diskProvider.GetFileSize(file); - return MapItem(new ImportDecision(localMovie, new Rejection("Unknown Movie")), rootFolder, downloadId, null); + return MapItem(new ImportDecision(localMovie, + new ImportRejection(ImportRejectionReason.UnknownMovie, "Unknown Movie")), + rootFolder, + downloadId, + null); } var importDecisions = _importDecisionMaker.GetImportDecisions(new List { file }, movie, trackedDownload?.DownloadItem, null, SceneSource(movie, baseFolder)); @@ -277,7 +280,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() }; } diff --git a/src/NzbDrone.Core/MediaFiles/MovieImport/Specifications/AlreadyImportedSpecification.cs b/src/NzbDrone.Core/MediaFiles/MovieImport/Specifications/AlreadyImportedSpecification.cs index ea2bfbbdd4..98c09a89e3 100644 --- a/src/NzbDrone.Core/MediaFiles/MovieImport/Specifications/AlreadyImportedSpecification.cs +++ b/src/NzbDrone.Core/MediaFiles/MovieImport/Specifications/AlreadyImportedSpecification.cs @@ -22,12 +22,12 @@ public AlreadyImportedSpecification(IHistoryService historyService, public SpecificationPriority Priority => SpecificationPriority.Database; - public Decision IsSatisfiedBy(LocalMovie localMovie, DownloadClientItem downloadClientItem) + public ImportSpecDecision IsSatisfiedBy(LocalMovie localMovie, DownloadClientItem downloadClientItem) { if (downloadClientItem == null) { _logger.Debug("No download client information is available, skipping"); - return Decision.Accept(); + return ImportSpecDecision.Accept(); } var movie = localMovie.Movie; @@ -35,7 +35,7 @@ public Decision IsSatisfiedBy(LocalMovie localMovie, DownloadClientItem download if (!movie.HasFile) { _logger.Debug("Skipping already imported check for movie without file"); - return Decision.Accept(); + return ImportSpecDecision.Accept(); } var movieImportedHistory = _historyService.GetByMovieId(movie.Id, null); @@ -48,7 +48,7 @@ public Decision IsSatisfiedBy(LocalMovie localMovie, DownloadClientItem download if (lastImported == null) { _logger.Trace("Movie file has not been imported"); - return Decision.Accept(); + return ImportSpecDecision.Accept(); } if (lastGrabbed != null) @@ -57,23 +57,23 @@ public Decision IsSatisfiedBy(LocalMovie localMovie, DownloadClientItem download if (lastGrabbed.Date.After(lastImported.Date)) { _logger.Trace("Movie file was grabbed again after importing"); - return Decision.Accept(); + return ImportSpecDecision.Accept(); } // If the release was imported after the last grab reject it if (lastImported.Date.After(lastGrabbed.Date)) { _logger.Debug("Movie file previously imported at {0}", lastImported.Date); - return Decision.Reject("Movie file already imported at {0}", lastImported.Date.ToLocalTime()); + return ImportSpecDecision.Reject(ImportRejectionReason.MovieAlreadyImported, "Movie file already imported at {0}", lastImported.Date.ToLocalTime()); } } else { _logger.Debug("Movie file previously imported at {0}", lastImported.Date); - return Decision.Reject("Movie file already imported at {0}", lastImported.Date.ToLocalTime()); + return ImportSpecDecision.Reject(ImportRejectionReason.MovieAlreadyImported, "Movie file already imported at {0}", lastImported.Date.ToLocalTime()); } - return Decision.Accept(); + return ImportSpecDecision.Accept(); } } } diff --git a/src/NzbDrone.Core/MediaFiles/MovieImport/Specifications/FreeSpaceSpecification.cs b/src/NzbDrone.Core/MediaFiles/MovieImport/Specifications/FreeSpaceSpecification.cs index bdc17611f3..cc9330f6a8 100644 --- a/src/NzbDrone.Core/MediaFiles/MovieImport/Specifications/FreeSpaceSpecification.cs +++ b/src/NzbDrone.Core/MediaFiles/MovieImport/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(LocalMovie localMovie, DownloadClientItem downloadClientItem) + public ImportSpecDecision IsSatisfiedBy(LocalMovie localMovie, 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(LocalMovie localMovie, DownloadClientItem download if (localMovie.ExistingFile) { _logger.Debug("Skipping free space check for existing movie"); - return Decision.Accept(); + return ImportSpecDecision.Accept(); } var path = Directory.GetParent(localMovie.Movie.Path); @@ -45,13 +44,13 @@ public Decision IsSatisfiedBy(LocalMovie localMovie, DownloadClientItem download if (!freeSpace.HasValue) { _logger.Debug("Free space check returned an invalid result for: {0}", path); - return Decision.Accept(); + return ImportSpecDecision.Accept(); } if (freeSpace < localMovie.Size + _configService.MinimumFreeSpaceWhenImporting.Megabytes()) { _logger.Warn("Not enough free space ({0}) to import: {1} ({2})", freeSpace, localMovie, localMovie.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(LocalMovie localMovie, DownloadClientItem download _logger.Error(ex, "Unable to check free disk space while importing: {0}", localMovie.Path); } - return Decision.Accept(); + return ImportSpecDecision.Accept(); } } } diff --git a/src/NzbDrone.Core/MediaFiles/MovieImport/Specifications/GrabbedReleaseQualitySpecification.cs b/src/NzbDrone.Core/MediaFiles/MovieImport/Specifications/GrabbedReleaseQualitySpecification.cs index 0592f3b3e6..f96568378a 100644 --- a/src/NzbDrone.Core/MediaFiles/MovieImport/Specifications/GrabbedReleaseQualitySpecification.cs +++ b/src/NzbDrone.Core/MediaFiles/MovieImport/Specifications/GrabbedReleaseQualitySpecification.cs @@ -1,7 +1,6 @@ using System.Linq; using NLog; using NzbDrone.Common.Extensions; -using NzbDrone.Core.DecisionEngine; using NzbDrone.Core.Download; using NzbDrone.Core.History; using NzbDrone.Core.Parser.Model; @@ -21,11 +20,11 @@ public GrabbedReleaseQualitySpecification(Logger logger, _historyService = historyService; } - public Decision IsSatisfiedBy(LocalMovie localMovie, DownloadClientItem downloadClientItem) + public ImportSpecDecision IsSatisfiedBy(LocalMovie localMovie, DownloadClientItem downloadClientItem) { if (downloadClientItem == null) { - return Decision.Accept(); + return ImportSpecDecision.Accept(); } var grabbedHistory = _historyService.FindByDownloadId(downloadClientItem.DownloadId) @@ -35,7 +34,7 @@ public Decision IsSatisfiedBy(LocalMovie localMovie, DownloadClientItem download if (grabbedHistory.Empty()) { _logger.Debug("No grabbed history for this download client item"); - return Decision.Accept(); + return ImportSpecDecision.Accept(); } foreach (var item in grabbedHistory) @@ -47,7 +46,7 @@ public Decision IsSatisfiedBy(LocalMovie localMovie, DownloadClientItem download } } - return Decision.Accept(); + return ImportSpecDecision.Accept(); } } } diff --git a/src/NzbDrone.Core/MediaFiles/MovieImport/Specifications/HasAudioTrackSpecification.cs b/src/NzbDrone.Core/MediaFiles/MovieImport/Specifications/HasAudioTrackSpecification.cs index 2be157725f..743a0859e9 100644 --- a/src/NzbDrone.Core/MediaFiles/MovieImport/Specifications/HasAudioTrackSpecification.cs +++ b/src/NzbDrone.Core/MediaFiles/MovieImport/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(LocalMovie localMovie, DownloadClientItem downloadClientItem) + public ImportSpecDecision IsSatisfiedBy(LocalMovie localMovie, DownloadClientItem downloadClientItem) { if (localMovie.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 (localMovie.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/MovieImport/Specifications/MatchesFolderSpecification.cs b/src/NzbDrone.Core/MediaFiles/MovieImport/Specifications/MatchesFolderSpecification.cs index b957486d72..666ae2c2e0 100644 --- a/src/NzbDrone.Core/MediaFiles/MovieImport/Specifications/MatchesFolderSpecification.cs +++ b/src/NzbDrone.Core/MediaFiles/MovieImport/Specifications/MatchesFolderSpecification.cs @@ -1,6 +1,5 @@ using System.IO; using NLog; -using NzbDrone.Core.DecisionEngine; using NzbDrone.Core.Download; using NzbDrone.Core.Parser.Model; @@ -15,18 +14,18 @@ public MatchesFolderSpecification(Logger logger) _logger = logger; } - public Decision IsSatisfiedBy(LocalMovie localMovie, DownloadClientItem downloadClientItem) + public ImportSpecDecision IsSatisfiedBy(LocalMovie localMovie, DownloadClientItem downloadClientItem) { if (localMovie.ExistingFile) { - return Decision.Accept(); + return ImportSpecDecision.Accept(); } var dirInfo = new FileInfo(localMovie.Path).Directory; if (dirInfo == null) { - return Decision.Accept(); + return ImportSpecDecision.Accept(); } // TODO: Actually implement this!!!! @@ -37,7 +36,7 @@ public Decision IsSatisfiedBy(LocalMovie localMovie, DownloadClientItem download return Decision.Accept(); }*/ - return Decision.Accept(); + return ImportSpecDecision.Accept(); } } } diff --git a/src/NzbDrone.Core/MediaFiles/MovieImport/Specifications/MatchesGrabSpecification.cs b/src/NzbDrone.Core/MediaFiles/MovieImport/Specifications/MatchesGrabSpecification.cs index 0cabb7c3e2..c116a2feba 100644 --- a/src/NzbDrone.Core/MediaFiles/MovieImport/Specifications/MatchesGrabSpecification.cs +++ b/src/NzbDrone.Core/MediaFiles/MovieImport/Specifications/MatchesGrabSpecification.cs @@ -1,7 +1,6 @@ using System.Linq; using NLog; using NzbDrone.Common.Extensions; -using NzbDrone.Core.DecisionEngine; using NzbDrone.Core.Download; using NzbDrone.Core.Parser.Model; @@ -16,28 +15,28 @@ public MatchesGrabSpecification(Logger logger) _logger = logger; } - public Decision IsSatisfiedBy(LocalMovie localMovie, DownloadClientItem downloadClientItem) + public ImportSpecDecision IsSatisfiedBy(LocalMovie localMovie, DownloadClientItem downloadClientItem) { if (localMovie.ExistingFile) { - return Decision.Accept(); + return ImportSpecDecision.Accept(); } var releaseInfo = localMovie.Release; if (releaseInfo == null || releaseInfo.MovieIds.Empty()) { - return Decision.Accept(); + return ImportSpecDecision.Accept(); } if (releaseInfo.MovieIds.All(o => o != localMovie.Movie.Id)) { _logger.Debug("Unexpected movie(s) in file: {0}", localMovie.Movie.ToString()); - return Decision.Reject("Movie {0} was not found in the grabbed release: {1}", localMovie.Movie.ToString(), releaseInfo.Title); + return ImportSpecDecision.Reject(ImportRejectionReason.MovieNotFoundInRelease, "Movie {0} was not found in the grabbed release: {1}", localMovie.Movie.ToString(), releaseInfo.Title); } - return Decision.Accept(); + return ImportSpecDecision.Accept(); } } } diff --git a/src/NzbDrone.Core/MediaFiles/MovieImport/Specifications/NotMultiPartSpecification.cs b/src/NzbDrone.Core/MediaFiles/MovieImport/Specifications/NotMultiPartSpecification.cs index 1f1bc9e6c3..a6694a8bca 100644 --- a/src/NzbDrone.Core/MediaFiles/MovieImport/Specifications/NotMultiPartSpecification.cs +++ b/src/NzbDrone.Core/MediaFiles/MovieImport/Specifications/NotMultiPartSpecification.cs @@ -3,7 +3,6 @@ using NLog; using NzbDrone.Common.Disk; using NzbDrone.Common.Extensions; -using NzbDrone.Core.DecisionEngine; using NzbDrone.Core.Download; using NzbDrone.Core.Parser.Model; @@ -11,8 +10,14 @@ namespace NzbDrone.Core.MediaFiles.MovieImport.Specifications { public class NotMultiPartSpecification : IImportDecisionEngineSpecification { - private readonly Logger _logger; + private static readonly Regex[] MovieMultiPartRegex = new[] + { + new Regex(@"(?[ _.-]*(?:cd|dvd|p(?:ar)?t|dis[ck])[ _.-]*[0-9]+)", RegexOptions.Compiled | RegexOptions.IgnoreCase), + new Regex(@"(?[ _.-]*(?:cd|dvd|p(?:ar)?t|dis[ck])[ _.-]*[a-d]+)", RegexOptions.Compiled | RegexOptions.IgnoreCase), + }; + private readonly IDiskProvider _diskProvider; + private readonly Logger _logger; public NotMultiPartSpecification(IDiskProvider diskProvider, Logger logger) { @@ -20,33 +25,24 @@ public NotMultiPartSpecification(IDiskProvider diskProvider, Logger logger) _logger = logger; } - private static readonly Regex[] MovieMultiPartRegex = new Regex[] + public ImportSpecDecision IsSatisfiedBy(LocalMovie localMovie, DownloadClientItem downloadClientItem) { - new Regex(@"(?[ _.-]*(?:cd|dvd|p(?:ar)?t|dis[ck])[ _.-]*[0-9]+)", RegexOptions.Compiled | RegexOptions.IgnoreCase), - new Regex(@"(?[ _.-]*(?:cd|dvd|p(?:ar)?t|dis[ck])[ _.-]*[a-d]+)", RegexOptions.Compiled | RegexOptions.IgnoreCase), - }; - - public Decision IsSatisfiedBy(LocalMovie localMovie, DownloadClientItem downloadClientItem) - { - var regexReplace = MovieMultiPartRegex.First().Replace(localMovie.Path, ""); - if (MovieMultiPartRegex.Any(v => v.IsMatch(localMovie.Path))) { - var parentPath = localMovie.Path.GetParentPath(); - var filesInDirectory = _diskProvider.GetFiles(localMovie.Path.GetParentPath(), false); + var filesInDirectory = _diskProvider.GetFiles(localMovie.Path.GetParentPath(), false).ToList(); foreach (var regex in MovieMultiPartRegex) { - if (filesInDirectory.Where(file => regex.Replace(file, "") == regex.Replace(localMovie.Path, "")).Count() > 1) + if (filesInDirectory.Count(file => regex.Replace(file, "") == regex.Replace(localMovie.Path, "")) > 1) { _logger.Debug("Rejected Multi-Part File: {0}", localMovie.Path); - return Decision.Reject("File is suspected multi-part file, Radarr doesn't support this"); + return ImportSpecDecision.Reject(ImportRejectionReason.MultiPartMovie, "File is suspected multi-part file, Radarr doesn't support this"); } } } - return Decision.Accept(); + return ImportSpecDecision.Accept(); } } } diff --git a/src/NzbDrone.Core/MediaFiles/MovieImport/Specifications/NotSampleSpecification.cs b/src/NzbDrone.Core/MediaFiles/MovieImport/Specifications/NotSampleSpecification.cs index 73c19e53b4..75d9f72cb4 100644 --- a/src/NzbDrone.Core/MediaFiles/MovieImport/Specifications/NotSampleSpecification.cs +++ b/src/NzbDrone.Core/MediaFiles/MovieImport/Specifications/NotSampleSpecification.cs @@ -1,5 +1,4 @@ using NLog; -using NzbDrone.Core.DecisionEngine; using NzbDrone.Core.Download; using NzbDrone.Core.Parser.Model; @@ -17,26 +16,26 @@ public NotSampleSpecification(IDetectSample detectSample, _logger = logger; } - public Decision IsSatisfiedBy(LocalMovie localMovie, DownloadClientItem downloadClientItem) + public ImportSpecDecision IsSatisfiedBy(LocalMovie localMovie, DownloadClientItem downloadClientItem) { if (localMovie.ExistingFile) { _logger.Debug("Existing file, skipping sample check"); - return Decision.Accept(); + return ImportSpecDecision.Accept(); } var sample = _detectSample.IsSample(localMovie.Movie.MovieMetadata, localMovie.Path); 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"); } - return Decision.Accept(); + return ImportSpecDecision.Accept(); } } } diff --git a/src/NzbDrone.Core/MediaFiles/MovieImport/Specifications/NotUnpackingSpecification.cs b/src/NzbDrone.Core/MediaFiles/MovieImport/Specifications/NotUnpackingSpecification.cs index 54aff4a638..05773fac83 100644 --- a/src/NzbDrone.Core/MediaFiles/MovieImport/Specifications/NotUnpackingSpecification.cs +++ b/src/NzbDrone.Core/MediaFiles/MovieImport/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(LocalMovie localMovie, DownloadClientItem downloadClientItem) + public ImportSpecDecision IsSatisfiedBy(LocalMovie localMovie, DownloadClientItem downloadClientItem) { if (localMovie.ExistingFile) { _logger.Debug("{0} is in movie folder, skipping unpacking check", localMovie.Path); - return Decision.Accept(); + return ImportSpecDecision.Accept(); } foreach (var workingFolder in _configService.DownloadClientWorkingFolders.Split('|')) @@ -41,13 +40,13 @@ public Decision IsSatisfiedBy(LocalMovie localMovie, DownloadClientItem download if (OsInfo.IsNotWindows) { _logger.Debug("{0} is still being unpacked", localMovie.Path); - return Decision.Reject("File is still being unpacked"); + return ImportSpecDecision.Reject(ImportRejectionReason.Unpacking, "File is still being unpacked"); } if (_diskProvider.FileGetLastWrite(localMovie.Path) > DateTime.UtcNow.AddMinutes(-5)) { _logger.Debug("{0} appears to be unpacking still", localMovie.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(LocalMovie localMovie, DownloadClientItem download } } - return Decision.Accept(); + return ImportSpecDecision.Accept(); } } } diff --git a/src/NzbDrone.Core/MediaFiles/MovieImport/Specifications/UnverifiedSceneNumberingSpecification.cs b/src/NzbDrone.Core/MediaFiles/MovieImport/Specifications/UnverifiedSceneNumberingSpecification.cs deleted file mode 100644 index 8ea6634a24..0000000000 --- a/src/NzbDrone.Core/MediaFiles/MovieImport/Specifications/UnverifiedSceneNumberingSpecification.cs +++ /dev/null @@ -1,21 +0,0 @@ -using NLog; -using NzbDrone.Core.DecisionEngine; -using NzbDrone.Core.Download; -using NzbDrone.Core.Parser.Model; -namespace NzbDrone.Core.MediaFiles.MovieImport.Specifications -{ - public class UnverifiedSceneNumberingSpecification : IImportDecisionEngineSpecification - { - private readonly Logger _logger; - - public UnverifiedSceneNumberingSpecification(Logger logger) - { - _logger = logger; - } - - public Decision IsSatisfiedBy(LocalMovie localMovie, DownloadClientItem downloadClientItem) - { - return Decision.Accept(); - } - } -} diff --git a/src/NzbDrone.Core/MediaFiles/MovieImport/Specifications/UpgradeSpecification.cs b/src/NzbDrone.Core/MediaFiles/MovieImport/Specifications/UpgradeSpecification.cs index fcd7ffc934..aac399bc43 100644 --- a/src/NzbDrone.Core/MediaFiles/MovieImport/Specifications/UpgradeSpecification.cs +++ b/src/NzbDrone.Core/MediaFiles/MovieImport/Specifications/UpgradeSpecification.cs @@ -2,7 +2,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; @@ -24,7 +23,7 @@ public UpgradeSpecification(IConfigService configService, _logger = logger; } - public Decision IsSatisfiedBy(LocalMovie localMovie, DownloadClientItem downloadClientItem) + public ImportSpecDecision IsSatisfiedBy(LocalMovie localMovie, DownloadClientItem downloadClientItem) { var downloadPropersAndRepacks = _configService.DownloadPropersAndRepacks; var qualityProfile = localMovie.Movie.QualityProfile; @@ -38,7 +37,7 @@ public Decision IsSatisfiedBy(LocalMovie localMovie, DownloadClientItem download { _logger.Trace("Unable to get movie file details from the DB. MovieId: {0} MovieFileId: {1}", localMovie.Movie.Id, localMovie.Movie.MovieFileId); - return Decision.Accept(); + return ImportSpecDecision.Accept(); } var qualityCompare = qualityComparer.Compare(localMovie.Quality.Quality, movieFile.Quality.Quality); @@ -46,7 +45,7 @@ public Decision IsSatisfiedBy(LocalMovie localMovie, DownloadClientItem download if (qualityCompare < 0) { _logger.Debug("This file isn't a quality upgrade for movie. Existing quality: {0}. New Quality {1}. Skipping {2}", movieFile.Quality.Quality, localMovie.Quality.Quality, localMovie.Path); - return Decision.Reject("Not an upgrade for existing movie file. Existing quality: {0}. New Quality {1}.", movieFile.Quality.Quality, localMovie.Quality.Quality); + return ImportSpecDecision.Reject(ImportRejectionReason.NotQualityUpgrade, "Not an upgrade for existing movie file. Existing quality: {0}. New Quality {1}.", movieFile.Quality.Quality, localMovie.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(LocalMovie localMovie, DownloadClientItem download localMovie.Quality.Revision.CompareTo(movieFile.Quality.Revision) < 0) { _logger.Debug("This file isn't a quality revision upgrade for movie. Skipping {0}", localMovie.Path); - return Decision.Reject("Not a quality revision upgrade for existing movie file(s)"); + return ImportSpecDecision.Reject(ImportRejectionReason.NotRevisionUpgrade, "Not a quality revision upgrade for existing movie file(s)"); } movieFile.Movie = localMovie.Movie; @@ -73,7 +72,8 @@ public Decision IsSatisfiedBy(LocalMovie localMovie, DownloadClientItem download currentCustomFormats != null ? currentCustomFormats.ConcatToString() : "", currentFormatScore); - return Decision.Reject("Not a Custom Format upgrade for existing movie file(s). New: [{0}] ({1}) do not improve on Existing: [{2}] ({3})", + return ImportSpecDecision.Reject(ImportRejectionReason.NotCustomFormatUpgrade, + "Not a Custom Format upgrade for existing movie file(s). New: [{0}] ({1}) do not improve on Existing: [{2}] ({3})", newCustomFormats != null ? newCustomFormats.ConcatToString() : "", newFormatScore, currentCustomFormats != null ? currentCustomFormats.ConcatToString() : "", @@ -87,7 +87,7 @@ public Decision IsSatisfiedBy(LocalMovie localMovie, DownloadClientItem download currentFormatScore); } - return Decision.Accept(); + return ImportSpecDecision.Accept(); } } } diff --git a/src/Radarr.Api.V3/Indexers/ReleaseResource.cs b/src/Radarr.Api.V3/Indexers/ReleaseResource.cs index 22b24cbb45..d77897a6f5 100644 --- a/src/Radarr.Api.V3/Indexers/ReleaseResource.cs +++ b/src/Radarr.Api.V3/Indexers/ReleaseResource.cs @@ -105,7 +105,7 @@ public static ReleaseResource ToResource(this DownloadDecision model) Rejected = model.Rejected, TmdbId = releaseInfo.TmdbId, 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/Radarr.Api.V3/ManualImport/ManualImportController.cs b/src/Radarr.Api.V3/ManualImport/ManualImportController.cs index 40f877d7f8..8c1f3ba38a 100644 --- a/src/Radarr.Api.V3/ManualImport/ManualImportController.cs +++ b/src/Radarr.Api.V3/ManualImport/ManualImportController.cs @@ -38,7 +38,7 @@ public object ReprocessItems([FromBody] List item item.Movie = processedItem.Movie.ToResource(0); 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/Radarr.Api.V3/ManualImport/ManualImportReprocessResource.cs b/src/Radarr.Api.V3/ManualImport/ManualImportReprocessResource.cs index 659fea56f1..3ec7152c34 100644 --- a/src/Radarr.Api.V3/ManualImport/ManualImportReprocessResource.cs +++ b/src/Radarr.Api.V3/ManualImport/ManualImportReprocessResource.cs @@ -1,5 +1,4 @@ using System.Collections.Generic; -using NzbDrone.Core.DecisionEngine; using NzbDrone.Core.Languages; using NzbDrone.Core.Qualities; using Radarr.Api.V3.CustomFormats; @@ -20,6 +19,6 @@ public class ManualImportReprocessResource : RestResource public List CustomFormats { get; set; } public int CustomFormatScore { get; set; } public int IndexerFlags { get; set; } - public IEnumerable Rejections { get; set; } + public IEnumerable Rejections { get; set; } } } diff --git a/src/Radarr.Api.V3/ManualImport/ManualImportResource.cs b/src/Radarr.Api.V3/ManualImport/ManualImportResource.cs index b895614039..deae5dad71 100644 --- a/src/Radarr.Api.V3/ManualImport/ManualImportResource.cs +++ b/src/Radarr.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.MovieImport; using NzbDrone.Core.MediaFiles.MovieImport.Manual; using NzbDrone.Core.Qualities; using Radarr.Api.V3.CustomFormats; @@ -27,7 +28,7 @@ public class ManualImportResource : RestResource public List CustomFormats { get; set; } public int CustomFormatScore { get; set; } public int IndexerFlags { get; set; } - public IEnumerable Rejections { get; set; } + public IEnumerable Rejections { get; set; } } public static class ManualImportResourceMapper @@ -60,7 +61,7 @@ public static ManualImportResource ToResource(this ManualImportItem model) // QualityWeight DownloadId = model.DownloadId, IndexerFlags = model.IndexerFlags, - Rejections = model.Rejections + Rejections = model.Rejections.Select(r => r.ToResource()) }; } @@ -69,4 +70,27 @@ public static List ToResource(this IEnumerable