mirror of
https://github.com/Radarr/Radarr.git
synced 2024-09-11 12:02:35 +02:00
Decision Engine is now mostly working with movies :)
This commit is contained in:
parent
cd310626e9
commit
9aa8050627
@ -89,8 +89,8 @@ private IEnumerable<DownloadDecision> GetMovieDecisions(List<ReleaseInfo> report
|
||||
else
|
||||
{
|
||||
remoteEpisode.DownloadAllowed = true;
|
||||
//decision = GetDecisionForReport(remoteEpisode, searchCriteria); TODO: Rewrite this for movies!
|
||||
decision = new DownloadDecision(remoteEpisode);
|
||||
decision = GetDecisionForReport(remoteEpisode, searchCriteria);
|
||||
//decision = new DownloadDecision(remoteEpisode);
|
||||
}
|
||||
|
||||
}
|
||||
@ -205,6 +205,14 @@ private IEnumerable<DownloadDecision> GetDecisions(List<ReleaseInfo> reports, Se
|
||||
}
|
||||
}
|
||||
|
||||
private DownloadDecision GetDecisionForReport(RemoteMovie remoteEpisode, SearchCriteriaBase searchCriteria = null)
|
||||
{
|
||||
var reasons = _specifications.Select(c => EvaluateSpec(c, remoteEpisode, searchCriteria))
|
||||
.Where(c => c != null);
|
||||
|
||||
return new DownloadDecision(remoteEpisode, reasons.ToArray());
|
||||
}
|
||||
|
||||
private DownloadDecision GetDecisionForReport(RemoteEpisode remoteEpisode, SearchCriteriaBase searchCriteria = null)
|
||||
{
|
||||
var reasons = _specifications.Select(c => EvaluateSpec(c, remoteEpisode, searchCriteria))
|
||||
@ -235,5 +243,32 @@ private Rejection EvaluateSpec(IDecisionEngineSpecification spec, RemoteEpisode
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private Rejection EvaluateSpec(IDecisionEngineSpecification spec, RemoteMovie remoteEpisode, SearchCriteriaBase searchCriteriaBase = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
var result = spec.IsSatisfiedBy(remoteEpisode, searchCriteriaBase);
|
||||
|
||||
if (!result.Accepted)
|
||||
{
|
||||
return new Rejection(result.Reason, spec.Type);
|
||||
}
|
||||
}
|
||||
catch (NotImplementedException e)
|
||||
{
|
||||
_logger.Info("Spec " + spec.GetType().Name + " does not care about movies.");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
e.Data.Add("report", remoteEpisode.Release.ToJson());
|
||||
e.Data.Add("parsed", remoteEpisode.ParsedEpisodeInfo.ToJson());
|
||||
_logger.Error(e, "Couldn't evaluate decision on " + remoteEpisode.Release.Title + ", with spec: " + spec.GetType().Name);
|
||||
return new Rejection(string.Format("{0}: {1}", spec.GetType().Name, e.Message));//TODO UPDATE SPECS!
|
||||
return null;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -8,5 +8,7 @@ public interface IDecisionEngineSpecification
|
||||
RejectionType Type { get; }
|
||||
|
||||
Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria);
|
||||
|
||||
Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCriteria);
|
||||
}
|
||||
}
|
||||
|
@ -107,5 +107,58 @@ public Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCr
|
||||
_logger.Debug("Item: {0}, meets size constraints.", subject);
|
||||
return Decision.Accept();
|
||||
}
|
||||
|
||||
public Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCriteria)
|
||||
{
|
||||
_logger.Debug("Beginning size check for: {0}", subject);
|
||||
|
||||
var quality = subject.ParsedMovieInfo.Quality.Quality;
|
||||
|
||||
if (subject.Release.Size == 0)
|
||||
{
|
||||
_logger.Debug("Release has unknown size, skipping size check.");
|
||||
return Decision.Accept();
|
||||
}
|
||||
|
||||
var qualityDefinition = _qualityDefinitionService.Get(quality);
|
||||
if (qualityDefinition.MinSize.HasValue)
|
||||
{
|
||||
var minSize = qualityDefinition.MinSize.Value.Megabytes();
|
||||
|
||||
//Multiply maxSize by Series.Runtime
|
||||
minSize = minSize * subject.Movie.Runtime;
|
||||
|
||||
//If the parsed size is smaller than minSize we don't want it
|
||||
if (subject.Release.Size < minSize)
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
if (!qualityDefinition.MaxSize.HasValue || qualityDefinition.MaxSize.Value == 0)
|
||||
{
|
||||
_logger.Debug("Max size is unlimited - skipping check.");
|
||||
}
|
||||
else
|
||||
{
|
||||
var maxSize = qualityDefinition.MaxSize.Value.Megabytes();
|
||||
|
||||
//Multiply maxSize by Series.Runtime
|
||||
maxSize = maxSize * subject.Movie.Runtime;
|
||||
|
||||
//If the parsed size is greater than maxSize we don't want it
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
_logger.Debug("Item: {0}, meets size constraints.", subject);
|
||||
return Decision.Accept();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Extensions;
|
||||
@ -55,5 +56,10 @@ public virtual Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase
|
||||
|
||||
return Decision.Accept();
|
||||
}
|
||||
|
||||
public Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCriteria)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,7 @@
|
||||
using NzbDrone.Core.Blacklisting;
|
||||
using NzbDrone.Core.IndexerSearch.Definitions;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using System;
|
||||
|
||||
namespace NzbDrone.Core.DecisionEngine.Specifications
|
||||
{
|
||||
@ -28,5 +29,13 @@ public Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCr
|
||||
|
||||
return Decision.Accept();
|
||||
}
|
||||
|
||||
public Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCriteria)
|
||||
{
|
||||
|
||||
throw new NotImplementedException();
|
||||
|
||||
return Decision.Accept();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using NLog;
|
||||
using NzbDrone.Core.IndexerSearch.Definitions;
|
||||
@ -34,5 +35,18 @@ public virtual Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase
|
||||
|
||||
return Decision.Accept();
|
||||
}
|
||||
|
||||
public Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCriteria)
|
||||
{
|
||||
if (subject.Movie.MovieFile.Value != null)
|
||||
{
|
||||
if (!_qualityUpgradableSpecification.CutoffNotMet(subject.Movie.Profile, subject.Movie.MovieFile.Value.Quality, subject.ParsedMovieInfo.Quality))
|
||||
{
|
||||
return Decision.Reject("Existing file meets cutoff: {0}", subject.Movie.Profile.Value.Cutoff);
|
||||
}
|
||||
}
|
||||
|
||||
return Decision.Accept();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -36,5 +36,10 @@ public virtual Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase
|
||||
|
||||
return Decision.Accept();
|
||||
}
|
||||
|
||||
public Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCriteria)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -29,5 +29,20 @@ public virtual Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase
|
||||
|
||||
return Decision.Accept();
|
||||
}
|
||||
|
||||
public virtual Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCriteria)
|
||||
{
|
||||
var wantedLanguage = subject.Movie.Profile.Value.Language;
|
||||
|
||||
_logger.Debug("Checking if report meets language requirements. {0}", subject.ParsedMovieInfo.Language);
|
||||
|
||||
if (subject.ParsedMovieInfo.Language != wantedLanguage)
|
||||
{
|
||||
_logger.Debug("Report Language: {0} rejected because it is not wanted, wanted {1}", subject.ParsedMovieInfo.Language, wantedLanguage);
|
||||
return Decision.Reject("{0} is wanted, but found {1}", wantedLanguage, subject.ParsedMovieInfo.Language);
|
||||
}
|
||||
|
||||
return Decision.Accept();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -36,6 +36,37 @@ public virtual Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase
|
||||
}
|
||||
|
||||
|
||||
_logger.Debug("Checking if report meets minimum age requirements. {0}", age);
|
||||
|
||||
if (age < minimumAge)
|
||||
{
|
||||
_logger.Debug("Only {0} minutes old, minimum age is {1} minutes", age, minimumAge);
|
||||
return Decision.Reject("Only {0} minutes old, minimum age is {1} minutes", age, minimumAge);
|
||||
}
|
||||
|
||||
_logger.Debug("Release is {0} minutes old, greater than minimum age of {1} minutes", age, minimumAge);
|
||||
|
||||
return Decision.Accept();
|
||||
}
|
||||
|
||||
public virtual Decision 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();
|
||||
}
|
||||
|
||||
var age = subject.Release.AgeMinutes;
|
||||
var minimumAge = _configService.MinimumAge;
|
||||
|
||||
if (minimumAge == 0)
|
||||
{
|
||||
_logger.Debug("Minimum age is not set.");
|
||||
return Decision.Accept();
|
||||
}
|
||||
|
||||
|
||||
_logger.Debug("Checking if report meets minimum age requirements. {0}", age);
|
||||
|
||||
if (age < minimumAge)
|
||||
|
@ -1,4 +1,5 @@
|
||||
using NLog;
|
||||
using System;
|
||||
using NLog;
|
||||
using NzbDrone.Core.IndexerSearch.Definitions;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
|
||||
@ -25,5 +26,16 @@ public Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCr
|
||||
|
||||
return Decision.Accept();
|
||||
}
|
||||
|
||||
public Decision 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 Decision.Accept();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -38,5 +38,24 @@ public virtual Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase
|
||||
|
||||
return Decision.Accept();
|
||||
}
|
||||
|
||||
public virtual Decision 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 series", subject.Release.Title);
|
||||
return Decision.Reject("Usenet is not enabled for this series");
|
||||
}
|
||||
|
||||
if (subject.Release.DownloadProtocol == DownloadProtocol.Torrent && !delayProfile.EnableTorrent)
|
||||
{
|
||||
_logger.Debug("[{0}] Torrent is not enabled for this series", subject.Release.Title);
|
||||
return Decision.Reject("Torrent is not enabled for this series");
|
||||
}
|
||||
|
||||
return Decision.Accept();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -26,5 +26,17 @@ public virtual Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase
|
||||
|
||||
return Decision.Accept();
|
||||
}
|
||||
|
||||
public virtual Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCriteria)
|
||||
{
|
||||
_logger.Debug("Checking if report meets quality requirements. {0}", subject.ParsedMovieInfo.Quality);
|
||||
if (!subject.Movie.Profile.Value.Items.Exists(v => v.Allowed && v.Quality == subject.ParsedMovieInfo.Quality.Quality))
|
||||
{
|
||||
_logger.Debug("Quality {0} rejected by Series' quality profile", subject.ParsedMovieInfo.Quality);
|
||||
return Decision.Reject("{0} is not wanted in profile", subject.ParsedMovieInfo.Quality.Quality);
|
||||
}
|
||||
|
||||
return Decision.Accept();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -50,5 +50,32 @@ public Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCr
|
||||
|
||||
return Decision.Accept();
|
||||
}
|
||||
|
||||
public Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCriteria)
|
||||
{
|
||||
var queue = _queueService.GetQueue()
|
||||
.Select(q => q.RemoteMovie).ToList();
|
||||
|
||||
var matchingSeries = queue.Where(q => q.Movie.Id == subject.Movie.Id);
|
||||
|
||||
foreach (var remoteEpisode in matchingSeries)
|
||||
{
|
||||
_logger.Debug("Checking if existing release in queue meets cutoff. Queued quality is: {0}", remoteEpisode.ParsedEpisodeInfo.Quality);
|
||||
|
||||
if (!_qualityUpgradableSpecification.CutoffNotMet(subject.Movie.Profile, remoteEpisode.ParsedMovieInfo.Quality, subject.ParsedMovieInfo.Quality))
|
||||
{
|
||||
return Decision.Reject("Quality for release in queue already meets cutoff: {0}", remoteEpisode.ParsedEpisodeInfo.Quality);
|
||||
}
|
||||
|
||||
_logger.Debug("Checking if release is higher quality than queued release. Queued quality is: {0}", remoteEpisode.ParsedMovieInfo.Quality);
|
||||
|
||||
if (!_qualityUpgradableSpecification.IsUpgradable(subject.Movie.Profile, remoteEpisode.ParsedMovieInfo.Quality, subject.ParsedMovieInfo.Quality))
|
||||
{
|
||||
return Decision.Reject("Quality for release in queue is of equal or higher preference: {0}", remoteEpisode.ParsedMovieInfo.Quality);
|
||||
}
|
||||
}
|
||||
|
||||
return Decision.Accept();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -42,5 +42,27 @@ public virtual Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase
|
||||
|
||||
return Decision.Accept();
|
||||
}
|
||||
|
||||
public virtual Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCriteria)
|
||||
{
|
||||
if (subject.Release == null || subject.Release.Container.IsNullOrWhiteSpace())
|
||||
{
|
||||
return Decision.Accept();
|
||||
}
|
||||
|
||||
if (_dvdContainerTypes.Contains(subject.Release.Container.ToLower()))
|
||||
{
|
||||
_logger.Debug("Release contains raw DVD, rejecting.");
|
||||
return Decision.Reject("Raw DVD release");
|
||||
}
|
||||
|
||||
if (_blurayContainerTypes.Contains(subject.Release.Container.ToLower()))
|
||||
{
|
||||
_logger.Debug("Release contains raw Bluray, rejecting.");
|
||||
return Decision.Reject("Raw Bluray release");
|
||||
}
|
||||
|
||||
return Decision.Accept();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -62,6 +62,46 @@ public virtual Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase
|
||||
return Decision.Accept();
|
||||
}
|
||||
|
||||
public virtual Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCriteria)
|
||||
{
|
||||
_logger.Debug("Checking if release meets restrictions: {0}", subject);
|
||||
|
||||
var title = subject.Release.Title;
|
||||
var restrictions = _restrictionService.AllForTags(subject.Movie.Tags);
|
||||
|
||||
var required = restrictions.Where(r => r.Required.IsNotNullOrWhiteSpace());
|
||||
var ignored = restrictions.Where(r => r.Ignored.IsNotNullOrWhiteSpace());
|
||||
|
||||
foreach (var r in required)
|
||||
{
|
||||
var requiredTerms = r.Required.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).ToList();
|
||||
|
||||
var foundTerms = ContainsAny(requiredTerms, title);
|
||||
if (foundTerms.Empty())
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var r in ignored)
|
||||
{
|
||||
var ignoredTerms = r.Ignored.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).ToList();
|
||||
|
||||
var foundTerms = ContainsAny(ignoredTerms, title);
|
||||
if (foundTerms.Any())
|
||||
{
|
||||
var terms = string.Join(", ", foundTerms);
|
||||
_logger.Debug("[{0}] contains these ignored terms: {1}", title, terms);
|
||||
return Decision.Reject("Contains these ignored terms: {0}", terms);
|
||||
}
|
||||
}
|
||||
|
||||
_logger.Debug("[{0}] No restrictions apply, allowing", subject);
|
||||
return Decision.Accept();
|
||||
}
|
||||
|
||||
private static List<string> ContainsAny(List<string> terms, string title)
|
||||
{
|
||||
return terms.Where(t => title.ToLowerInvariant().Contains(t.ToLowerInvariant())).ToList();
|
||||
|
@ -38,5 +38,26 @@ public virtual Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase
|
||||
|
||||
return Decision.Accept();
|
||||
}
|
||||
|
||||
public virtual Decision 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();
|
||||
}
|
||||
|
||||
var age = subject.Release.Age;
|
||||
var retention = _configService.Retention;
|
||||
|
||||
_logger.Debug("Checking if report meets retention requirements. {0}", age);
|
||||
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 Decision.Accept();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -28,6 +28,71 @@ public DelaySpecification(IPendingReleaseService pendingReleaseService,
|
||||
|
||||
public RejectionType Type => RejectionType.Temporary;
|
||||
|
||||
public virtual Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCriteria)
|
||||
{
|
||||
if (searchCriteria != null && searchCriteria.UserInvokedSearch)
|
||||
{
|
||||
_logger.Debug("Ignoring delay for user invoked search");
|
||||
return Decision.Accept();
|
||||
}
|
||||
|
||||
var profile = subject.Movie.Profile.Value;
|
||||
var delayProfile = _delayProfileService.BestForTags(subject.Movie.Tags);
|
||||
var delay = delayProfile.GetProtocolDelay(subject.Release.DownloadProtocol);
|
||||
var isPreferredProtocol = subject.Release.DownloadProtocol == delayProfile.PreferredProtocol;
|
||||
|
||||
if (delay == 0)
|
||||
{
|
||||
_logger.Debug("Profile does not require a waiting period before download for {0}.", subject.Release.DownloadProtocol);
|
||||
return Decision.Accept();
|
||||
}
|
||||
|
||||
var comparer = new QualityModelComparer(profile);
|
||||
|
||||
if (isPreferredProtocol)
|
||||
{
|
||||
var upgradable = _qualityUpgradableSpecification.IsUpgradable(profile, subject.Movie.MovieFile.Value.Quality, subject.ParsedMovieInfo.Quality);
|
||||
|
||||
if (upgradable)
|
||||
{
|
||||
var revisionUpgrade = _qualityUpgradableSpecification.IsRevisionUpgrade(subject.Movie.MovieFile.Value.Quality, subject.ParsedMovieInfo.Quality);
|
||||
|
||||
if (revisionUpgrade)
|
||||
{
|
||||
_logger.Debug("New quality is a better revision for existing quality, skipping delay");
|
||||
return Decision.Accept();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// If quality meets or exceeds the best allowed quality in the profile accept it immediately
|
||||
var bestQualityInProfile = new QualityModel(profile.LastAllowedQuality());
|
||||
var isBestInProfile = comparer.Compare(subject.ParsedEpisodeInfo.Quality, bestQualityInProfile) >= 0;
|
||||
|
||||
if (isBestInProfile && isPreferredProtocol)
|
||||
{
|
||||
_logger.Debug("Quality is highest in profile for preferred protocol, will not delay");
|
||||
return Decision.Accept();
|
||||
}
|
||||
|
||||
/*
|
||||
var oldest = _pendingReleaseService.OldestPendingRelease(subject.Series.Id, episodeIds);
|
||||
|
||||
if (oldest != null && oldest.Release.AgeMinutes > delay)
|
||||
{
|
||||
return Decision.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");
|
||||
}*/ //TODO: Update for movies!
|
||||
|
||||
return Decision.Accept();
|
||||
}
|
||||
|
||||
public virtual Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria)
|
||||
{
|
||||
if (searchCriteria != null && searchCriteria.UserInvokedSearch)
|
||||
|
@ -28,6 +28,56 @@ public HistorySpecification(IHistoryService historyService,
|
||||
|
||||
public RejectionType Type => RejectionType.Permanent;
|
||||
|
||||
public virtual Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCriteria)
|
||||
{
|
||||
if (searchCriteria != null)
|
||||
{
|
||||
_logger.Debug("Skipping history check during search");
|
||||
return Decision.Accept();
|
||||
}
|
||||
|
||||
var cdhEnabled = _configService.EnableCompletedDownloadHandling;
|
||||
|
||||
_logger.Debug("Performing history status check on report");
|
||||
_logger.Debug("Checking current status of episode [{0}] in history", subject.Movie.Id);
|
||||
var mostRecent = _historyService.MostRecentForMovie(subject.Movie.Id);
|
||||
|
||||
if (mostRecent != null && mostRecent.EventType == HistoryEventType.Grabbed)
|
||||
{
|
||||
var recent = mostRecent.Date.After(DateTime.UtcNow.AddHours(-12));
|
||||
var cutoffUnmet = _qualityUpgradableSpecification.CutoffNotMet(subject.Movie.Profile, mostRecent.Quality, subject.ParsedMovieInfo.Quality);
|
||||
var upgradeable = _qualityUpgradableSpecification.IsUpgradable(subject.Movie.Profile, mostRecent.Quality, subject.ParsedMovieInfo.Quality);
|
||||
|
||||
if (!recent && cdhEnabled)
|
||||
{
|
||||
return Decision.Accept();
|
||||
}
|
||||
|
||||
if (!cutoffUnmet)
|
||||
{
|
||||
if (recent)
|
||||
{
|
||||
return Decision.Reject("Recent grab event in history already meets cutoff: {0}", mostRecent.Quality);
|
||||
}
|
||||
|
||||
return Decision.Reject("CDH is disabled and grab event in history already meets cutoff: {0}", mostRecent.Quality);
|
||||
}
|
||||
|
||||
if (!upgradeable)
|
||||
{
|
||||
if (recent)
|
||||
{
|
||||
return Decision.Reject("Recent grab event in history is of equal or higher quality: {0}", mostRecent.Quality);
|
||||
}
|
||||
|
||||
return Decision.Reject("CDH is disabled and grab event in history is of equal or higher quality: {0}", mostRecent.Quality);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return Decision.Accept();
|
||||
}
|
||||
|
||||
public virtual Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria)
|
||||
{
|
||||
if (searchCriteria != null)
|
||||
|
@ -1,3 +1,4 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using NLog;
|
||||
using NzbDrone.Core.IndexerSearch.Definitions;
|
||||
@ -16,6 +17,11 @@ public MonitoredEpisodeSpecification(Logger logger)
|
||||
|
||||
public RejectionType Type => RejectionType.Permanent;
|
||||
|
||||
public Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCriteria)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public virtual Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria)
|
||||
{
|
||||
if (searchCriteria != null)
|
||||
|
@ -47,6 +47,39 @@ public virtual Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase
|
||||
}
|
||||
}
|
||||
|
||||
return Decision.Accept();
|
||||
}
|
||||
|
||||
public virtual Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCriteria)
|
||||
{
|
||||
if (searchCriteria != null)
|
||||
{
|
||||
return Decision.Accept();
|
||||
}
|
||||
|
||||
if (subject.Movie.MovieFile.Value == null)
|
||||
{
|
||||
return Decision.Accept();
|
||||
}
|
||||
|
||||
var file = subject.Movie.MovieFile.Value;
|
||||
|
||||
if (_qualityUpgradableSpecification.IsRevisionUpgrade(file.Quality, subject.ParsedMovieInfo.Quality))
|
||||
{
|
||||
if (file.DateAdded < DateTime.Today.AddDays(-7))
|
||||
{
|
||||
_logger.Debug("Proper for old file, rejecting: {0}", subject);
|
||||
return Decision.Reject("Proper for old file");
|
||||
}
|
||||
|
||||
if (!_configService.AutoDownloadPropers)
|
||||
{
|
||||
_logger.Debug("Auto downloading of propers is disabled");
|
||||
return Decision.Reject("Proper downloading is disabled");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return Decision.Accept();
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
using System;
|
||||
using NLog;
|
||||
using NzbDrone.Core.IndexerSearch.Definitions;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
@ -27,5 +28,10 @@ public virtual Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase
|
||||
_logger.Debug("Episode file on disk contains more episodes than this release contains");
|
||||
return Decision.Reject("Episode file on disk contains more episodes than this release contains");
|
||||
}
|
||||
|
||||
public Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCriteria)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
using System;
|
||||
using NLog;
|
||||
using NzbDrone.Core.IndexerSearch.Definitions;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
@ -39,5 +40,10 @@ public Decision IsSatisfiedBy(RemoteEpisode remoteEpisode, SearchCriteriaBase se
|
||||
|
||||
return Decision.Accept();
|
||||
}
|
||||
|
||||
public Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCriteria)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
using System.Linq;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using NLog;
|
||||
using NzbDrone.Core.IndexerSearch.Definitions;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
@ -17,6 +18,11 @@ public EpisodeRequestedSpecification(Logger logger)
|
||||
|
||||
public RejectionType Type => RejectionType.Permanent;
|
||||
|
||||
public Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCriteria)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Decision IsSatisfiedBy(RemoteEpisode remoteEpisode, SearchCriteriaBase searchCriteria)
|
||||
{
|
||||
if (searchCriteria == null)
|
||||
|
@ -1,3 +1,4 @@
|
||||
using System;
|
||||
using NLog;
|
||||
using NzbDrone.Core.IndexerSearch.Definitions;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
@ -15,6 +16,11 @@ public SeasonMatchSpecification(Logger logger)
|
||||
|
||||
public RejectionType Type => RejectionType.Permanent;
|
||||
|
||||
public Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCriteria)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Decision IsSatisfiedBy(RemoteEpisode remoteEpisode, SearchCriteriaBase searchCriteria)
|
||||
{
|
||||
if (searchCriteria == null)
|
||||
|
@ -32,5 +32,23 @@ public Decision IsSatisfiedBy(RemoteEpisode remoteEpisode, SearchCriteriaBase se
|
||||
|
||||
return Decision.Accept();
|
||||
}
|
||||
|
||||
public Decision IsSatisfiedBy(RemoteMovie remoteEpisode, SearchCriteriaBase searchCriteria)
|
||||
{
|
||||
if (searchCriteria == null)
|
||||
{
|
||||
return Decision.Accept();
|
||||
}
|
||||
|
||||
_logger.Debug("Checking if movie matches searched movie");
|
||||
|
||||
if (remoteEpisode.Movie.Id != searchCriteria.Movie.Id)
|
||||
{
|
||||
_logger.Debug("Series {0} does not match {1}", remoteEpisode.Movie, searchCriteria.Series);
|
||||
return Decision.Reject("Wrong movie");
|
||||
}
|
||||
|
||||
return Decision.Accept();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,3 +1,4 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using NLog;
|
||||
using NzbDrone.Core.IndexerSearch.Definitions;
|
||||
@ -16,6 +17,11 @@ public SingleEpisodeSearchMatchSpecification(Logger logger)
|
||||
|
||||
public RejectionType Type => RejectionType.Permanent;
|
||||
|
||||
public Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCriteria)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Decision IsSatisfiedBy(RemoteEpisode remoteEpisode, SearchCriteriaBase searchCriteria)
|
||||
{
|
||||
if (searchCriteria == null)
|
||||
|
@ -33,5 +33,23 @@ public Decision IsSatisfiedBy(RemoteEpisode remoteEpisode, SearchCriteriaBase se
|
||||
|
||||
return Decision.Accept();
|
||||
}
|
||||
|
||||
public Decision IsSatisfiedBy(RemoteMovie remoteEpisode, SearchCriteriaBase searchCriteria)
|
||||
{
|
||||
var torrentInfo = remoteEpisode.Release as TorrentInfo;
|
||||
|
||||
if (torrentInfo == null)
|
||||
{
|
||||
return Decision.Accept();
|
||||
}
|
||||
|
||||
if (torrentInfo.Seeders != null && torrentInfo.Seeders < 1)
|
||||
{
|
||||
_logger.Debug("Not enough seeders. ({0})", torrentInfo.Seeders);
|
||||
return Decision.Reject("Not enough seeders. ({0})", torrentInfo.Seeders);
|
||||
}
|
||||
|
||||
return Decision.Accept();
|
||||
}
|
||||
}
|
||||
}
|
@ -30,6 +30,25 @@ public virtual Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase
|
||||
}
|
||||
}
|
||||
|
||||
return Decision.Accept();
|
||||
}
|
||||
|
||||
public virtual Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCriteria)
|
||||
{
|
||||
if (subject.Movie.MovieFile.Value == null)
|
||||
{
|
||||
return Decision.Accept();
|
||||
}
|
||||
|
||||
var file = subject.Movie.MovieFile.Value;
|
||||
_logger.Debug("Comparing file quality with report. Existing file is {0}", file.Quality);
|
||||
|
||||
if (!_qualityUpgradableSpecification.IsUpgradable(subject.Movie.Profile, file.Quality, subject.ParsedMovieInfo.Quality))
|
||||
{
|
||||
return Decision.Reject("Quality for existing file on disk is of equal or higher preference: {0}", file.Quality);
|
||||
}
|
||||
|
||||
|
||||
return Decision.Accept();
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user