1
0
mirror of https://github.com/Radarr/Radarr.git synced 2026-04-26 22:46:53 -04:00

Added: A Huge Cleanup of old Series Code. (Let's pray nothing breaks :P) (#2589)

This commit is contained in:
Qstick
2018-03-14 16:41:36 -04:00
committed by Leonardo Galli
parent 8a6d67a6d7
commit 25e10e26e3
551 changed files with 2835 additions and 18867 deletions
@@ -1,10 +1,10 @@
using System.Linq;
using System.Linq;
using NLog;
using NzbDrone.Common.Extensions;
using NzbDrone.Core.IndexerSearch.Definitions;
using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Qualities;
using NzbDrone.Core.Tv;
using NzbDrone.Core.Movies;
using System.Collections.Generic;
namespace NzbDrone.Core.DecisionEngine.Specifications
@@ -12,102 +12,16 @@ namespace NzbDrone.Core.DecisionEngine.Specifications
public class AcceptableSizeSpecification : IDecisionEngineSpecification
{
private readonly IQualityDefinitionService _qualityDefinitionService;
private readonly IEpisodeService _episodeService;
private readonly Logger _logger;
public AcceptableSizeSpecification(IQualityDefinitionService qualityDefinitionService, IEpisodeService episodeService, Logger logger)
public AcceptableSizeSpecification(IQualityDefinitionService qualityDefinitionService, Logger logger)
{
_qualityDefinitionService = qualityDefinitionService;
_episodeService = episodeService;
_logger = logger;
}
public RejectionType Type => RejectionType.Permanent;
public Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria)
{
_logger.Debug("Beginning size check for: {0}", subject);
var quality = subject.ParsedEpisodeInfo.Quality.Quality;
if (subject.ParsedEpisodeInfo.Special)
{
_logger.Debug("Special release found, skipping size check.");
return Decision.Accept();
}
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.Series.Runtime * subject.Episodes.Count;
//If the parsed size is smaller than minSize we don't want it
if (subject.Release.Size < minSize)
{
var runtimeMessage = subject.Episodes.Count == 1 ? $"{subject.Series.Runtime}min" : $"{subject.Episodes.Count}x {subject.Series.Runtime}min";
_logger.Debug("Item: {0}, Size: {1} is smaller than minimum allowed size ({2} bytes for {3}), rejecting.", subject, subject.Release.Size, minSize, runtimeMessage);
return Decision.Reject("{0} is smaller than minimum allowed {1} (for {2})", subject.Release.Size.SizeSuffix(), minSize.SizeSuffix(), runtimeMessage);
}
}
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.Series.Runtime * subject.Episodes.Count;
if (subject.Episodes.Count == 1)
{
Episode episode = subject.Episodes.First();
List<Episode> seasonEpisodes;
var seasonSearchCriteria = searchCriteria as SeasonSearchCriteria;
if (seasonSearchCriteria != null && !seasonSearchCriteria.Series.UseSceneNumbering && seasonSearchCriteria.Episodes.Any(v => v.Id == episode.Id))
{
seasonEpisodes = (searchCriteria as SeasonSearchCriteria).Episodes;
}
else
{
seasonEpisodes = _episodeService.GetEpisodesBySeason(episode.SeriesId, episode.SeasonNumber);
}
//Ensure that this is either the first episode
//or is the last episode in a season that has 10 or more episodes
if (seasonEpisodes.First().Id == episode.Id || (seasonEpisodes.Count() >= 10 && seasonEpisodes.Last().Id == episode.Id))
{
_logger.Debug("Possible double episode, doubling allowed size.");
maxSize = maxSize * 2;
}
}
//If the parsed size is greater than maxSize we don't want it
if (subject.Release.Size > maxSize)
{
var runtimeMessage = subject.Episodes.Count == 1 ? $"{subject.Series.Runtime}min" : $"{subject.Episodes.Count}x {subject.Series.Runtime}min";
_logger.Debug("Item: {0}, Size: {1} is greater than maximum allowed size ({2} for {3}), rejecting.", subject, subject.Release.Size, maxSize, runtimeMessage);
return Decision.Reject("{0} is larger than maximum allowed {1} (for {2})", subject.Release.Size.SizeSuffix(), maxSize.SizeSuffix(), runtimeMessage);
}
}
_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);
@@ -4,7 +4,7 @@ using NLog;
using NzbDrone.Common.Extensions;
using NzbDrone.Core.IndexerSearch.Definitions;
using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Tv;
using NzbDrone.Core.Movies;
namespace NzbDrone.Core.DecisionEngine.Specifications
{
@@ -21,42 +21,6 @@ namespace NzbDrone.Core.DecisionEngine.Specifications
public RejectionType Type => RejectionType.Permanent;
public virtual Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria)
{
var releaseGroup = subject.ParsedEpisodeInfo.ReleaseGroup;
if (subject.Series.SeriesType != SeriesTypes.Anime)
{
return Decision.Accept();
}
foreach (var file in subject.Episodes.Where(c => c.EpisodeFileId != 0).Select(c => c.EpisodeFile.Value))
{
if (_qualityUpgradableSpecification.IsRevisionUpgrade(file.Quality, subject.ParsedEpisodeInfo.Quality))
{
if (file.ReleaseGroup.IsNullOrWhiteSpace())
{
_logger.Debug("Unable to compare release group, existing file's release group is unknown");
return Decision.Reject("Existing release group is unknown");
}
if (releaseGroup.IsNullOrWhiteSpace())
{
_logger.Debug("Unable to compare release group, release's release group is unknown");
return Decision.Reject("Release group is unknown");
}
if (file.ReleaseGroup != releaseGroup)
{
_logger.Debug("Existing Release group is: {0} - release's release group is: {1}", file.ReleaseGroup, releaseGroup);
return Decision.Reject("{0} does not match existing release group {1}", releaseGroup, file.ReleaseGroup);
}
}
}
return Decision.Accept();
}
public Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCriteria)
{
throw new NotImplementedException();
@@ -19,17 +19,6 @@ namespace NzbDrone.Core.DecisionEngine.Specifications
public RejectionType Type => RejectionType.Permanent;
public Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria)
{
if (_blacklistService.Blacklisted(subject.Series.Id, subject.Release))
{
_logger.Debug("{0} is blacklisted, rejecting.", subject.Release.Title);
return Decision.Reject("Release is blacklisted");
}
return Decision.Accept();
}
public Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCriteria)
{
@@ -19,23 +19,6 @@ namespace NzbDrone.Core.DecisionEngine.Specifications
public RejectionType Type => RejectionType.Permanent;
public virtual Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria)
{
foreach (var file in subject.Episodes.Where(c => c.EpisodeFileId != 0).Select(c => c.EpisodeFile.Value))
{
_logger.Debug("Comparing file quality with report. Existing file is {0}", file.Quality);
if (!_qualityUpgradableSpecification.CutoffNotMet(subject.Series.Profile, file.Quality, subject.ParsedEpisodeInfo.Quality))
{
_logger.Debug("Cutoff already met, rejecting.");
return Decision.Reject("Existing file meets cutoff: {0}", subject.Series.Profile.Value.Cutoff);
}
}
return Decision.Accept();
}
public virtual Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCriteria)
{
if (subject.Movie.MovieFile != null)
@@ -1,45 +0,0 @@
using System;
using NLog;
using NzbDrone.Core.IndexerSearch.Definitions;
using NzbDrone.Core.Parser.Model;
using NzbDrone.Common.Extensions;
using System.Linq;
using NzbDrone.Core.Tv;
namespace NzbDrone.Core.DecisionEngine.Specifications
{
public class FullSeasonSpecification : IDecisionEngineSpecification
{
private readonly Logger _logger;
private readonly IEpisodeService _episodeService;
public FullSeasonSpecification(Logger logger, IEpisodeService episodeService)
{
_logger = logger;
_episodeService = episodeService;
}
public RejectionType Type => RejectionType.Permanent;
public virtual Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria)
{
if (subject.ParsedEpisodeInfo.FullSeason)
{
_logger.Debug("Checking if all episodes in full season release have aired. {0}", subject.Release.Title);
if (subject.Episodes.Any(e => !e.AirDateUtc.HasValue || e.AirDateUtc.Value.After(DateTime.UtcNow)))
{
_logger.Debug("Full season release {0} rejected. All episodes haven't aired yet.", subject.Release.Title);
//return Decision.Reject("Full season release rejected. All episodes haven't aired yet.");
}
}
return Decision.Accept();
}
public Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCriteria)
{
throw new NotImplementedException();
}
}
}
@@ -15,21 +15,6 @@ namespace NzbDrone.Core.DecisionEngine.Specifications
public RejectionType Type => RejectionType.Permanent;
public virtual Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria)
{
var wantedLanguage = subject.Series.Profile.Value.Language;
_logger.Debug("Checking if report meets language requirements. {0}", subject.ParsedEpisodeInfo.Language);
if (subject.ParsedEpisodeInfo.Language != wantedLanguage)
{
_logger.Debug("Report Language: {0} rejected because it is not wanted, wanted {1}", subject.ParsedEpisodeInfo.Language, wantedLanguage);
return Decision.Reject("{0} is wanted, but found {1}", wantedLanguage, subject.ParsedEpisodeInfo.Language);
}
return Decision.Accept();
}
public virtual Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCriteria)
{
var wantedLanguage = subject.Movie.Profile.Value.Language;
@@ -1,4 +1,4 @@
using NLog;
using NLog;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.IndexerSearch.Definitions;
using NzbDrone.Core.Parser.Model;
@@ -18,37 +18,6 @@ namespace NzbDrone.Core.DecisionEngine.Specifications
public RejectionType Type => RejectionType.Temporary;
public virtual Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria)
{
if (subject.Release.DownloadProtocol != Indexers.DownloadProtocol.Usenet)
{
_logger.Debug("Not checking minimum age requirement for non-usenet report");
return Decision.Accept();
}
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)
{
_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)
@@ -1,4 +1,4 @@
using System;
using System;
using NLog;
using NzbDrone.Core.IndexerSearch.Definitions;
using NzbDrone.Core.Parser.Model;
@@ -16,17 +16,6 @@ namespace NzbDrone.Core.DecisionEngine.Specifications
_logger = logger;
}
public Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria)
{
if (subject.Release.Title.ToLower().Contains("sample") && subject.Release.Size < 70.Megabytes())
{
_logger.Debug("Sample release, rejecting.");
return Decision.Reject("Sample");
}
return Decision.Accept();
}
public Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCriteria)
{
if (subject.Release.Title.ToLower().Contains("sample") && subject.Release.Size < 70.Megabytes())
@@ -20,39 +20,20 @@ namespace NzbDrone.Core.DecisionEngine.Specifications
public RejectionType Type => RejectionType.Permanent;
public virtual Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria)
{
var delayProfile = _delayProfileService.BestForTags(subject.Series.Tags);
if (subject.Release.DownloadProtocol == DownloadProtocol.Usenet && !delayProfile.EnableUsenet)
{
_logger.Debug("[{0}] Usenet is not enabled for this series", subject.Release.Title);
return Decision.Reject("Usenet is not enabled for this series");
}
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();
}
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");
_logger.Debug("[{0}] Usenet is not enabled for this movie", subject.Release.Title);
return Decision.Reject("Usenet is not enabled for this movie");
}
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");
_logger.Debug("[{0}] Torrent is not enabled for this movie", subject.Release.Title);
return Decision.Reject("Torrent is not enabled for this movie");
}
return Decision.Accept();
@@ -15,18 +15,6 @@ namespace NzbDrone.Core.DecisionEngine.Specifications
public RejectionType Type => RejectionType.Permanent;
public virtual Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria)
{
_logger.Debug("Checking if report meets quality requirements. {0}", subject.ParsedEpisodeInfo.Quality);
if (!subject.Series.Profile.Value.Items.Exists(v => v.Allowed && v.Quality == subject.ParsedEpisodeInfo.Quality.Quality))
{
_logger.Debug("Quality {0} rejected by Series' quality profile", subject.ParsedEpisodeInfo.Quality);
return Decision.Reject("{0} is not wanted in profile", subject.ParsedEpisodeInfo.Quality.Quality);
}
return Decision.Accept();
}
public virtual Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCriteria)
{
_logger.Debug("Checking if report meets quality requirements. {0}", subject.ParsedMovieInfo.Quality);
@@ -23,34 +23,6 @@ namespace NzbDrone.Core.DecisionEngine.Specifications
public RejectionType Type => RejectionType.Permanent;
public Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria)
{
var queue = _queueService.GetQueue()
.Select(q => q.RemoteEpisode).ToList();
var matchingSeries = queue.Where(q => q.Series.Id == subject.Series.Id);
var matchingEpisode = matchingSeries.Where(q => q.Episodes.Select(e => e.Id).Intersect(subject.Episodes.Select(e => e.Id)).Any());
foreach (var remoteEpisode in matchingEpisode)
{
_logger.Debug("Checking if existing release in queue meets cutoff. Queued quality is: {0}", remoteEpisode.ParsedEpisodeInfo.Quality);
if (!_qualityUpgradableSpecification.CutoffNotMet(subject.Series.Profile, remoteEpisode.ParsedEpisodeInfo.Quality, subject.ParsedEpisodeInfo.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.ParsedEpisodeInfo.Quality);
if (!_qualityUpgradableSpecification.IsUpgradable(subject.Series.Profile, remoteEpisode.ParsedEpisodeInfo.Quality, subject.ParsedEpisodeInfo.Quality))
{
return Decision.Reject("Quality for release in queue is of equal or higher preference: {0}", remoteEpisode.ParsedEpisodeInfo.Quality);
}
}
return Decision.Accept();
}
public Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCriteria)
{
var queue = _queueService.GetQueue()
@@ -21,28 +21,6 @@ namespace NzbDrone.Core.DecisionEngine.Specifications
public RejectionType Type => RejectionType.Permanent;
public virtual Decision IsSatisfiedBy(RemoteEpisode 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();
}
public virtual Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCriteria)
{
if (subject.Release == null || subject.Release.Container.IsNullOrWhiteSpace())
@@ -22,46 +22,6 @@ namespace NzbDrone.Core.DecisionEngine.Specifications
public RejectionType Type => RejectionType.Permanent;
public virtual Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria)
{
_logger.Debug("Checking if release meets restrictions: {0}", subject);
var title = subject.Release.Title;
var restrictions = _restrictionService.AllForTags(subject.Series.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();
}
public virtual Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCriteria)
{
_logger.Debug("Checking if release meets restrictions: {0}", subject);
@@ -1,4 +1,4 @@
using System.Linq;
using System.Linq;
using NLog;
using NzbDrone.Core.Datastore;
using NzbDrone.Core.Indexers;
@@ -21,47 +21,9 @@ namespace NzbDrone.Core.DecisionEngine.Specifications.Search
//public SpecificationPriority Priority => SpecificationPriority.Default;
public RejectionType Type => RejectionType.Permanent;
public Decision IsSatisfiedBy(RemoteEpisode remoteEpisode, SearchCriteriaBase searchCriteria)
public Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCriteria)
{
var torrentInfo = remoteEpisode.Release as TorrentInfo;
if (torrentInfo == null || torrentInfo.IndexerId == 0)
{
return Decision.Accept();
}
IndexerDefinition indexer;
try
{
indexer = _indexerFactory.Get(torrentInfo.IndexerId);
}
catch (ModelNotFoundException)
{
_logger.Debug("Indexer with id {0} does not exist, skipping seeders check", torrentInfo.IndexerId);
return Decision.Accept();
}
var torrentIndexerSettings = indexer.Settings as ITorrentIndexerSettings;
if (torrentIndexerSettings != null)
{
var minimumSeeders = torrentIndexerSettings.MinimumSeeders;
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 Decision.Accept();
}
public Decision IsSatisfiedBy(RemoteMovie remoteEpisode, SearchCriteriaBase searchCriteria)
{
var torrentInfo = remoteEpisode.Release;
var torrentInfo = subject.Release;
if (torrentInfo == null || torrentInfo.IndexerId == 0)
{
@@ -1,4 +1,4 @@
using NLog;
using NLog;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.IndexerSearch.Definitions;
using NzbDrone.Core.Parser.Model;
@@ -18,27 +18,6 @@ namespace NzbDrone.Core.DecisionEngine.Specifications
public RejectionType Type => RejectionType.Permanent;
public virtual Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria)
{
if (subject.Release.DownloadProtocol != Indexers.DownloadProtocol.Usenet)
{
_logger.Debug("Not checking retention requirement for non-usenet report");
return Decision.Accept();
}
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();
}
public virtual Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCriteria)
{
if (subject.Release.DownloadProtocol != Indexers.DownloadProtocol.Usenet)
@@ -3,7 +3,7 @@ using System.Linq;
using NLog;
using NzbDrone.Core.IndexerSearch.Definitions;
using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Tv;
using NzbDrone.Core.Movies;
using NzbDrone.Core.Configuration;
namespace NzbDrone.Core.DecisionEngine.Specifications.RssSync
@@ -38,32 +38,5 @@ namespace NzbDrone.Core.DecisionEngine.Specifications.RssSync
return Decision.Accept();
}
public virtual Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria)
{
if (searchCriteria != null)
{
if (!searchCriteria.MonitoredEpisodesOnly)
{
_logger.Debug("Skipping availability check during search");
return Decision.Accept();
}
}
/*if (subject.Series.Status != MovieStatusType.Released)
{
_logger.Debug("{0} is present in the DB but not yet available. skipping.", subject.Series);
return Decision.Reject("Series is not yet available");
}
/*var monitoredCount = subject.Episodes.Count(episode => episode.Monitored);
if (monitoredCount == subject.Episodes.Count)
{
return Decision.Accept();
}
_logger.Debug("Only {0}/{1} episodes are monitored. skipping.", monitoredCount, subject.Episodes.Count);*/
return Decision.Reject("Episode is not yet available");
}
}
}
@@ -1,4 +1,4 @@
using System.Linq;
using System.Linq;
using NLog;
using NzbDrone.Core.Download.Pending;
using NzbDrone.Core.IndexerSearch.Definitions;
@@ -107,73 +107,5 @@ namespace NzbDrone.Core.DecisionEngine.Specifications.RssSync
return Decision.Accept();
}
public virtual Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria)
{
if (searchCriteria != null && searchCriteria.UserInvokedSearch)
{
_logger.Debug("Ignoring delay for user invoked search");
return Decision.Accept();
}
var profile = subject.Series.Profile.Value;
var delayProfile = _delayProfileService.BestForTags(subject.Series.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)
{
foreach (var file in subject.Episodes.Where(c => c.EpisodeFileId != 0).Select(c => c.EpisodeFile.Value))
{
var upgradable = _qualityUpgradableSpecification.IsUpgradable(profile, file.Quality, subject.ParsedEpisodeInfo.Quality);
if (upgradable)
{
var revisionUpgrade = _qualityUpgradableSpecification.IsRevisionUpgrade(file.Quality, subject.ParsedEpisodeInfo.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 episodeIds = subject.Episodes.Select(e => e.Id);
//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");
}
return Decision.Accept();
}
}
}
@@ -39,7 +39,7 @@ namespace NzbDrone.Core.DecisionEngine.Specifications.RssSync
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);
_logger.Debug("Checking current status of movie [{0}] in history", subject.Movie.Id);
var mostRecent = _historyService.MostRecentForMovie(subject.Movie.Id);
if (mostRecent != null && mostRecent.EventType == HistoryEventType.Grabbed)
@@ -77,10 +77,5 @@ namespace NzbDrone.Core.DecisionEngine.Specifications.RssSync
return Decision.Accept();
}
public virtual Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria)
{
throw new NotImplementedException();
}
}
}
@@ -1,66 +0,0 @@
using System;
using System.Linq;
using NLog;
using NzbDrone.Core.IndexerSearch.Definitions;
using NzbDrone.Core.Parser.Model;
namespace NzbDrone.Core.DecisionEngine.Specifications.RssSync
{
public class MonitoredEpisodeSpecification : IDecisionEngineSpecification
{
private readonly Logger _logger;
public MonitoredEpisodeSpecification(Logger logger)
{
_logger = logger;
}
public RejectionType Type => RejectionType.Permanent;
public Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCriteria)
{
if (searchCriteria != null)
{
if (searchCriteria.UserInvokedSearch)
{
_logger.Debug("Skipping monitored check during search");
return Decision.Accept();
}
}
if (!subject.Movie.Monitored)
{
return Decision.Reject("Movie is not monitored");
}
return Decision.Accept();
}
public virtual Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria)
{
if (searchCriteria != null)
{
if (!searchCriteria.MonitoredEpisodesOnly)
{
_logger.Debug("Skipping monitored check during search");
return Decision.Accept();
}
}
if (!subject.Series.Monitored)
{
_logger.Debug("{0} is present in the DB but not tracked. skipping.", subject.Series);
return Decision.Reject("Series is not monitored");
}
var monitoredCount = subject.Episodes.Count(episode => episode.Monitored);
if (monitoredCount == subject.Episodes.Count)
{
return Decision.Accept();
}
_logger.Debug("Only {0}/{1} episodes are monitored. skipping.", monitoredCount, subject.Episodes.Count);
return Decision.Reject("Episode is not monitored");
}
}
}
@@ -0,0 +1,39 @@
using System;
using System.Linq;
using NLog;
using NzbDrone.Core.IndexerSearch.Definitions;
using NzbDrone.Core.Parser.Model;
namespace NzbDrone.Core.DecisionEngine.Specifications.RssSync
{
public class MonitoredMovieSpecification : IDecisionEngineSpecification
{
private readonly Logger _logger;
public MonitoredMovieSpecification(Logger logger)
{
_logger = logger;
}
public RejectionType Type => RejectionType.Permanent;
public Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCriteria)
{
if (searchCriteria != null)
{
if (searchCriteria.UserInvokedSearch)
{
_logger.Debug("Skipping monitored check during search");
return Decision.Accept();
}
}
if (!subject.Movie.Monitored)
{
return Decision.Reject("Movie is not monitored");
}
return Decision.Accept();
}
}
}
@@ -22,34 +22,6 @@ namespace NzbDrone.Core.DecisionEngine.Specifications.RssSync
public RejectionType Type => RejectionType.Permanent;
public virtual Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria)
{
if (searchCriteria != null)
{
return Decision.Accept();
}
foreach (var file in subject.Episodes.Where(c => c.EpisodeFileId != 0).Select(c => c.EpisodeFile.Value))
{
if (_qualityUpgradableSpecification.IsRevisionUpgrade(file.Quality, subject.ParsedEpisodeInfo.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();
}
public virtual Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCriteria)
{
if (searchCriteria != null)
@@ -1,37 +0,0 @@
using System;
using NLog;
using NzbDrone.Core.IndexerSearch.Definitions;
using NzbDrone.Core.Parser.Model;
namespace NzbDrone.Core.DecisionEngine.Specifications
{
public class SameEpisodesGrabSpecification : IDecisionEngineSpecification
{
private readonly SameEpisodesSpecification _sameEpisodesSpecification;
private readonly Logger _logger;
public SameEpisodesGrabSpecification(SameEpisodesSpecification sameEpisodesSpecification, Logger logger)
{
_sameEpisodesSpecification = sameEpisodesSpecification;
_logger = logger;
}
public RejectionType Type => RejectionType.Permanent;
public virtual Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria)
{
if (_sameEpisodesSpecification.IsSatisfiedBy(subject.Episodes))
{
return Decision.Accept();
}
_logger.Debug("Episode file on disk contains more episodes than this release contains");
return Decision.Reject("Episode file on disk contains more episodes than this release contains");
}
public Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCriteria)
{
throw new NotImplementedException();
}
}
}
@@ -2,48 +2,24 @@ using System;
using NLog;
using NzbDrone.Core.IndexerSearch.Definitions;
using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Tv;
using NzbDrone.Core.Movies;
namespace NzbDrone.Core.DecisionEngine.Specifications.Search
{
public class DailyEpisodeMatchSpecification : IDecisionEngineSpecification
{
private readonly Logger _logger;
private readonly IEpisodeService _episodeService;
public DailyEpisodeMatchSpecification(Logger logger, IEpisodeService episodeService)
public DailyEpisodeMatchSpecification(Logger logger)
{
_logger = logger;
_episodeService = episodeService;
}
public RejectionType Type => RejectionType.Permanent;
public Decision IsSatisfiedBy(RemoteEpisode remoteEpisode, SearchCriteriaBase searchCriteria)
{
if (searchCriteria == null)
{
return Decision.Accept();
}
var dailySearchSpec = searchCriteria as DailyEpisodeSearchCriteria;
if (dailySearchSpec == null) return Decision.Accept();
var episode = _episodeService.GetEpisode(dailySearchSpec.Series.Id, dailySearchSpec.AirDate.ToString(Episode.AIR_DATE_FORMAT));
if (!remoteEpisode.ParsedEpisodeInfo.IsDaily || remoteEpisode.ParsedEpisodeInfo.AirDate != episode.AirDate)
{
_logger.Debug("Episode AirDate does not match searched episode number, skipping.");
return Decision.Reject("Episode does not match");
}
return Decision.Accept();
}
public Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCriteria)
{
throw new NotImplementedException();
}
}
}
}
@@ -1,45 +0,0 @@
using System;
using System.Linq;
using NLog;
using NzbDrone.Core.IndexerSearch.Definitions;
using NzbDrone.Core.Parser.Model;
namespace NzbDrone.Core.DecisionEngine.Specifications.Search
{
public class EpisodeRequestedSpecification : IDecisionEngineSpecification
{
private readonly Logger _logger;
public EpisodeRequestedSpecification(Logger logger)
{
_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)
{
return Decision.Accept();
}
var criteriaEpisodes = searchCriteria.Episodes.Select(v => v.Id).ToList();
var remoteEpisodes = remoteEpisode.Episodes.Select(v => v.Id).ToList();
if (!criteriaEpisodes.Intersect(remoteEpisodes).Any())
{
_logger.Debug("Release rejected since the episode wasn't requested: {0}", remoteEpisode.ParsedEpisodeInfo);
return Decision.Reject("Episode wasn't requested");
}
return Decision.Accept();
}
}
}
@@ -0,0 +1,36 @@
using NLog;
using NzbDrone.Core.IndexerSearch.Definitions;
using NzbDrone.Core.Parser.Model;
namespace NzbDrone.Core.DecisionEngine.Specifications.Search
{
public class MovieSpecification : IDecisionEngineSpecification
{
private readonly Logger _logger;
public MovieSpecification(Logger logger)
{
_logger = logger;
}
public RejectionType Type => RejectionType.Permanent;
public Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCriteria)
{
if (searchCriteria == null)
{
return Decision.Accept();
}
_logger.Debug("Checking if movie matches searched movie");
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 Decision.Accept();
}
}
}
@@ -1,44 +0,0 @@
using System;
using NLog;
using NzbDrone.Core.IndexerSearch.Definitions;
using NzbDrone.Core.Parser.Model;
namespace NzbDrone.Core.DecisionEngine.Specifications.Search
{
public class SeasonMatchSpecification : IDecisionEngineSpecification
{
private readonly Logger _logger;
public SeasonMatchSpecification(Logger logger)
{
_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)
{
return Decision.Accept();
}
var singleEpisodeSpec = searchCriteria as SeasonSearchCriteria;
if (singleEpisodeSpec == null) return Decision.Accept();
if (singleEpisodeSpec.SeasonNumber != remoteEpisode.ParsedEpisodeInfo.SeasonNumber)
{
_logger.Debug("Season number does not match searched season number, skipping.");
//return Decision.Reject("Wrong season");
//Unnecessary for Movies
}
return Decision.Accept();
}
}
}
@@ -1,54 +0,0 @@
using NLog;
using NzbDrone.Core.IndexerSearch.Definitions;
using NzbDrone.Core.Parser.Model;
namespace NzbDrone.Core.DecisionEngine.Specifications.Search
{
public class SeriesSpecification : IDecisionEngineSpecification
{
private readonly Logger _logger;
public SeriesSpecification(Logger logger)
{
_logger = logger;
}
public RejectionType Type => RejectionType.Permanent;
public Decision IsSatisfiedBy(RemoteEpisode remoteEpisode, SearchCriteriaBase searchCriteria)
{
if (searchCriteria == null)
{
return Decision.Accept();
}
_logger.Debug("Checking if series matches searched series");
if (remoteEpisode.Series.Id != searchCriteria.Series.Id)
{
_logger.Debug("Series {0} does not match {1}", remoteEpisode.Series, searchCriteria.Series);
return Decision.Reject("Wrong series");
}
return 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,45 +0,0 @@
using System.Linq;
using NLog;
using NzbDrone.Core.IndexerSearch.Definitions;
using NzbDrone.Core.Parser.Model;
namespace NzbDrone.Core.DecisionEngine.Specifications.Search
{
public class SingleEpisodeMatchSpecification : IDecisionEngineSpecification
{
private readonly Logger _logger;
public SingleEpisodeMatchSpecification(Logger logger)
{
_logger = logger;
}
public string RejectionReason
{
get
{
return "Episode doesn't match";
}
}
public bool IsSatisfiedBy(RemoteEpisode remoteEpisode, SearchDefinitionBase searchDefinitionBase)
{
var singleEpisodeSpec = searchDefinitionBase as SingleEpisodeSearchDefinition;
if (singleEpisodeSpec == null) return true;
if (singleEpisodeSpec.SeasonNumber != remoteEpisode.ParsedEpisodeInfo.SeasonNumber)
{
_logger.Trace("Season number does not match searched season number, skipping.");
return false;
}
if (!remoteEpisode.Episodes.Select(c => c.EpisodeNumber).Contains(singleEpisodeSpec.EpisodeNumber))
{
_logger.Trace("Episode number does not match searched episode number, skipping.");
return false;
}
return true;
}
}
}
@@ -1,56 +0,0 @@
using System;
using System.Linq;
using NLog;
using NzbDrone.Core.IndexerSearch.Definitions;
using NzbDrone.Core.Parser.Model;
namespace NzbDrone.Core.DecisionEngine.Specifications.Search
{
public class SingleEpisodeSearchMatchSpecification : IDecisionEngineSpecification
{
private readonly Logger _logger;
public SingleEpisodeSearchMatchSpecification(Logger logger)
{
_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)
{
return Decision.Accept();
}
var singleEpisodeSpec = searchCriteria as SingleEpisodeSearchCriteria;
if (singleEpisodeSpec == null) return Decision.Accept();
if (singleEpisodeSpec.SeasonNumber != remoteEpisode.ParsedEpisodeInfo.SeasonNumber)
{
_logger.Debug("Season number does not match searched season number, skipping.");
//return Decision.Reject("Wrong season");
}
if (!remoteEpisode.ParsedEpisodeInfo.EpisodeNumbers.Any())
{
_logger.Debug("Full season result during single episode search, skipping.");
//return Decision.Reject("Full season pack");
}
if (!remoteEpisode.ParsedEpisodeInfo.EpisodeNumbers.Contains(singleEpisodeSpec.EpisodeNumber))
{
_logger.Debug("Episode number does not match searched episode number, skipping.");
//return Decision.Reject("Wrong episode");
}
return Decision.Accept();
}
}
}
@@ -1,4 +1,4 @@
using NLog;
using NLog;
using NzbDrone.Core.Datastore;
using NzbDrone.Core.Indexers;
using NzbDrone.Core.IndexerSearch.Definitions;
@@ -19,47 +19,10 @@ namespace NzbDrone.Core.DecisionEngine.Specifications.Search
//public SpecificationPriority Priority => SpecificationPriority.Default;
public RejectionType Type => RejectionType.Permanent;
public Decision IsSatisfiedBy(RemoteEpisode remoteEpisode, SearchCriteriaBase searchCriteria)
public Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCriteria)
{
var torrentInfo = remoteEpisode.Release as TorrentInfo;
if (torrentInfo == null || torrentInfo.IndexerId == 0)
{
return Decision.Accept();
}
IndexerDefinition indexer;
try
{
indexer = _indexerFactory.Get(torrentInfo.IndexerId);
}
catch (ModelNotFoundException)
{
_logger.Debug("Indexer with id {0} does not exist, skipping seeders check", torrentInfo.IndexerId);
return Decision.Accept();
}
var torrentIndexerSettings = indexer.Settings as ITorrentIndexerSettings;
if (torrentIndexerSettings != null)
{
var minimumSeeders = torrentIndexerSettings.MinimumSeeders;
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 Decision.Accept();
}
public Decision IsSatisfiedBy(RemoteMovie remoteEpisode, SearchCriteriaBase searchCriteria)
{
var torrentInfo = remoteEpisode.Release as TorrentInfo;
var torrentInfo = subject.Release as TorrentInfo;
if (torrentInfo == null || torrentInfo.IndexerId == 0)
{
@@ -18,21 +18,6 @@ namespace NzbDrone.Core.DecisionEngine.Specifications
public RejectionType Type => RejectionType.Permanent;
public virtual Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria)
{
foreach (var file in subject.Episodes.Where(c => c.EpisodeFileId != 0).Select(c => c.EpisodeFile.Value))
{
_logger.Debug("Comparing file quality with report. Existing file is {0}", file.Quality);
if (!_qualityUpgradableSpecification.IsUpgradable(subject.Series.Profile, file.Quality, subject.ParsedEpisodeInfo.Quality))
{
return Decision.Reject("Quality for existing file on disk is of equal or higher preference: {0}", file.Quality);
}
}
return Decision.Accept();
}
public virtual Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCriteria)
{
if (subject.Movie.MovieFile == null)