1
0
mirror of https://github.com/Radarr/Radarr.git synced 2024-11-04 10:02:40 +01:00

Decision Engine is now mostly working with movies :)

This commit is contained in:
Leonardo Galli 2017-01-06 15:05:30 +01:00
parent cd310626e9
commit 9aa8050627
28 changed files with 566 additions and 4 deletions

View File

@ -89,8 +89,8 @@ private IEnumerable<DownloadDecision> GetMovieDecisions(List<ReleaseInfo> report
else else
{ {
remoteEpisode.DownloadAllowed = true; remoteEpisode.DownloadAllowed = true;
//decision = GetDecisionForReport(remoteEpisode, searchCriteria); TODO: Rewrite this for movies! decision = GetDecisionForReport(remoteEpisode, searchCriteria);
decision = new DownloadDecision(remoteEpisode); //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) private DownloadDecision GetDecisionForReport(RemoteEpisode remoteEpisode, SearchCriteriaBase searchCriteria = null)
{ {
var reasons = _specifications.Select(c => EvaluateSpec(c, remoteEpisode, searchCriteria)) var reasons = _specifications.Select(c => EvaluateSpec(c, remoteEpisode, searchCriteria))
@ -235,5 +243,32 @@ private Rejection EvaluateSpec(IDecisionEngineSpecification spec, RemoteEpisode
return null; 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;
}
} }
} }

View File

@ -8,5 +8,7 @@ public interface IDecisionEngineSpecification
RejectionType Type { get; } RejectionType Type { get; }
Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria); Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria);
Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCriteria);
} }
} }

View File

@ -107,5 +107,58 @@ public Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCr
_logger.Debug("Item: {0}, meets size constraints.", subject); _logger.Debug("Item: {0}, meets size constraints.", subject);
return Decision.Accept(); 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();
}
} }
} }

View File

@ -1,3 +1,4 @@
using System;
using System.Linq; using System.Linq;
using NLog; using NLog;
using NzbDrone.Common.Extensions; using NzbDrone.Common.Extensions;
@ -55,5 +56,10 @@ public virtual Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase
return Decision.Accept(); return Decision.Accept();
} }
public Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCriteria)
{
throw new NotImplementedException();
}
} }
} }

View File

@ -2,6 +2,7 @@
using NzbDrone.Core.Blacklisting; using NzbDrone.Core.Blacklisting;
using NzbDrone.Core.IndexerSearch.Definitions; using NzbDrone.Core.IndexerSearch.Definitions;
using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Parser.Model;
using System;
namespace NzbDrone.Core.DecisionEngine.Specifications namespace NzbDrone.Core.DecisionEngine.Specifications
{ {
@ -28,5 +29,13 @@ public Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCr
return Decision.Accept(); return Decision.Accept();
} }
public Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCriteria)
{
throw new NotImplementedException();
return Decision.Accept();
}
} }
} }

View File

@ -1,3 +1,4 @@
using System;
using System.Linq; using System.Linq;
using NLog; using NLog;
using NzbDrone.Core.IndexerSearch.Definitions; using NzbDrone.Core.IndexerSearch.Definitions;
@ -34,5 +35,18 @@ public virtual Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase
return Decision.Accept(); 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();
}
} }
} }

View File

@ -36,5 +36,10 @@ public virtual Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase
return Decision.Accept(); return Decision.Accept();
} }
public Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCriteria)
{
throw new NotImplementedException();
}
} }
} }

View File

@ -29,5 +29,20 @@ public virtual Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase
return Decision.Accept(); 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();
}
} }
} }

View File

@ -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); _logger.Debug("Checking if report meets minimum age requirements. {0}", age);
if (age < minimumAge) if (age < minimumAge)

View File

@ -1,4 +1,5 @@
using NLog; using System;
using NLog;
using NzbDrone.Core.IndexerSearch.Definitions; using NzbDrone.Core.IndexerSearch.Definitions;
using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Parser.Model;
@ -25,5 +26,16 @@ public Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCr
return Decision.Accept(); 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();
}
} }
} }

View File

@ -38,5 +38,24 @@ public virtual Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase
return Decision.Accept(); 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();
}
} }
} }

View File

@ -26,5 +26,17 @@ public virtual Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase
return Decision.Accept(); 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();
}
} }
} }

View File

@ -50,5 +50,32 @@ public Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCr
return Decision.Accept(); 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();
}
} }
} }

View File

@ -42,5 +42,27 @@ public virtual Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase
return Decision.Accept(); 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();
}
} }
} }

View File

@ -62,6 +62,46 @@ public virtual Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase
return Decision.Accept(); 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) private static List<string> ContainsAny(List<string> terms, string title)
{ {
return terms.Where(t => title.ToLowerInvariant().Contains(t.ToLowerInvariant())).ToList(); return terms.Where(t => title.ToLowerInvariant().Contains(t.ToLowerInvariant())).ToList();

View File

@ -38,5 +38,26 @@ public virtual Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase
return Decision.Accept(); 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();
}
} }
} }

View File

@ -28,6 +28,71 @@ public DelaySpecification(IPendingReleaseService pendingReleaseService,
public RejectionType Type => RejectionType.Temporary; 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) public virtual Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria)
{ {
if (searchCriteria != null && searchCriteria.UserInvokedSearch) if (searchCriteria != null && searchCriteria.UserInvokedSearch)

View File

@ -28,6 +28,56 @@ public HistorySpecification(IHistoryService historyService,
public RejectionType Type => RejectionType.Permanent; 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) public virtual Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria)
{ {
if (searchCriteria != null) if (searchCriteria != null)

View File

@ -1,3 +1,4 @@
using System;
using System.Linq; using System.Linq;
using NLog; using NLog;
using NzbDrone.Core.IndexerSearch.Definitions; using NzbDrone.Core.IndexerSearch.Definitions;
@ -16,6 +17,11 @@ public MonitoredEpisodeSpecification(Logger logger)
public RejectionType Type => RejectionType.Permanent; public RejectionType Type => RejectionType.Permanent;
public Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCriteria)
{
throw new NotImplementedException();
}
public virtual Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria) public virtual Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria)
{ {
if (searchCriteria != null) if (searchCriteria != null)

View File

@ -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(); return Decision.Accept();
} }
} }

View File

@ -1,3 +1,4 @@
using System;
using NLog; using NLog;
using NzbDrone.Core.IndexerSearch.Definitions; using NzbDrone.Core.IndexerSearch.Definitions;
using NzbDrone.Core.Parser.Model; 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"); _logger.Debug("Episode file on disk contains more episodes than this release contains");
return Decision.Reject("Episode file on disk contains more episodes than this release contains"); return Decision.Reject("Episode file on disk contains more episodes than this release contains");
} }
public Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCriteria)
{
throw new NotImplementedException();
}
} }
} }

View File

@ -1,3 +1,4 @@
using System;
using NLog; using NLog;
using NzbDrone.Core.IndexerSearch.Definitions; using NzbDrone.Core.IndexerSearch.Definitions;
using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Parser.Model;
@ -39,5 +40,10 @@ public Decision IsSatisfiedBy(RemoteEpisode remoteEpisode, SearchCriteriaBase se
return Decision.Accept(); return Decision.Accept();
} }
public Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCriteria)
{
throw new NotImplementedException();
}
} }
} }

View File

@ -1,4 +1,5 @@
using System.Linq; using System;
using System.Linq;
using NLog; using NLog;
using NzbDrone.Core.IndexerSearch.Definitions; using NzbDrone.Core.IndexerSearch.Definitions;
using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Parser.Model;
@ -17,6 +18,11 @@ public EpisodeRequestedSpecification(Logger logger)
public RejectionType Type => RejectionType.Permanent; public RejectionType Type => RejectionType.Permanent;
public Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCriteria)
{
throw new NotImplementedException();
}
public Decision IsSatisfiedBy(RemoteEpisode remoteEpisode, SearchCriteriaBase searchCriteria) public Decision IsSatisfiedBy(RemoteEpisode remoteEpisode, SearchCriteriaBase searchCriteria)
{ {
if (searchCriteria == null) if (searchCriteria == null)

View File

@ -1,3 +1,4 @@
using System;
using NLog; using NLog;
using NzbDrone.Core.IndexerSearch.Definitions; using NzbDrone.Core.IndexerSearch.Definitions;
using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Parser.Model;
@ -15,6 +16,11 @@ public SeasonMatchSpecification(Logger logger)
public RejectionType Type => RejectionType.Permanent; public RejectionType Type => RejectionType.Permanent;
public Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCriteria)
{
throw new NotImplementedException();
}
public Decision IsSatisfiedBy(RemoteEpisode remoteEpisode, SearchCriteriaBase searchCriteria) public Decision IsSatisfiedBy(RemoteEpisode remoteEpisode, SearchCriteriaBase searchCriteria)
{ {
if (searchCriteria == null) if (searchCriteria == null)

View File

@ -32,5 +32,23 @@ public Decision IsSatisfiedBy(RemoteEpisode remoteEpisode, SearchCriteriaBase se
return Decision.Accept(); 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();
}
} }
} }

View File

@ -1,3 +1,4 @@
using System;
using System.Linq; using System.Linq;
using NLog; using NLog;
using NzbDrone.Core.IndexerSearch.Definitions; using NzbDrone.Core.IndexerSearch.Definitions;
@ -16,6 +17,11 @@ public SingleEpisodeSearchMatchSpecification(Logger logger)
public RejectionType Type => RejectionType.Permanent; public RejectionType Type => RejectionType.Permanent;
public Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCriteria)
{
throw new NotImplementedException();
}
public Decision IsSatisfiedBy(RemoteEpisode remoteEpisode, SearchCriteriaBase searchCriteria) public Decision IsSatisfiedBy(RemoteEpisode remoteEpisode, SearchCriteriaBase searchCriteria)
{ {
if (searchCriteria == null) if (searchCriteria == null)

View File

@ -33,5 +33,23 @@ public Decision IsSatisfiedBy(RemoteEpisode remoteEpisode, SearchCriteriaBase se
return Decision.Accept(); 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();
}
} }
} }

View File

@ -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(); return Decision.Accept();
} }
} }