Radarr/src/NzbDrone.Core/DecisionEngine/DownloadDecisionMaker.cs

294 lines
11 KiB
C#
Raw Normal View History

using System;
2013-04-07 07:30:37 +00:00
using System.Collections.Generic;
using System.Linq;
using NLog;
using NzbDrone.Common.Extensions;
2014-07-23 23:43:54 +00:00
using NzbDrone.Common.Instrumentation.Extensions;
using NzbDrone.Common.Serializer;
using NzbDrone.Core.Configuration;
2013-04-07 07:30:37 +00:00
using NzbDrone.Core.IndexerSearch.Definitions;
using NzbDrone.Core.Parser;
using NzbDrone.Core.Parser.Model;
2013-04-07 07:30:37 +00:00
namespace NzbDrone.Core.DecisionEngine
{
public interface IMakeDownloadDecision
{
2013-09-13 23:25:30 +00:00
List<DownloadDecision> GetRssDecision(List<ReleaseInfo> reports);
List<DownloadDecision> GetSearchDecision(List<ReleaseInfo> reports, SearchCriteriaBase searchCriteriaBase);
2013-04-07 07:30:37 +00:00
}
public class DownloadDecisionMaker : IMakeDownloadDecision
{
private readonly IEnumerable<IDecisionEngineSpecification> _specifications;
private readonly IParsingService _parsingService;
private readonly IConfigService _configService;
private readonly Logger _logger;
2013-04-07 07:30:37 +00:00
public DownloadDecisionMaker(IEnumerable<IDecisionEngineSpecification> specifications, IParsingService parsingService, IConfigService configService, Logger logger)
2013-04-07 07:30:37 +00:00
{
_specifications = specifications;
_parsingService = parsingService;
_configService = configService;
_logger = logger;
2013-04-07 07:30:37 +00:00
}
2013-09-13 23:25:30 +00:00
public List<DownloadDecision> GetRssDecision(List<ReleaseInfo> reports)
2013-04-07 07:30:37 +00:00
{
2017-01-17 20:22:51 +00:00
return GetMovieDecisions(reports).ToList();
2013-04-07 07:30:37 +00:00
}
2013-09-13 23:25:30 +00:00
public List<DownloadDecision> GetSearchDecision(List<ReleaseInfo> reports, SearchCriteriaBase searchCriteriaBase)
2013-04-07 07:30:37 +00:00
{
if (searchCriteriaBase.Movie != null)
{
return GetMovieDecisions(reports, searchCriteriaBase).ToList();
}
return GetDecisions(reports, searchCriteriaBase).ToList();
2013-04-07 07:30:37 +00:00
}
private IEnumerable<DownloadDecision> GetMovieDecisions(List<ReleaseInfo> reports, SearchCriteriaBase searchCriteria = null)
{
if (reports.Any())
{
_logger.ProgressInfo("Processing {0} releases", reports.Count);
}
else
{
_logger.ProgressInfo("No results found");
}
var reportNumber = 1;
foreach (var report in reports)
{
DownloadDecision decision = null;
_logger.ProgressTrace("Processing release {0}/{1}", reportNumber, reports.Count);
try
{
var parsedMovieInfo = Parser.Parser.ParseMovieTitle(report.Title, _configService.ParsingLeniency > 0);
2017-03-27 15:07:23 +00:00
if (parsedMovieInfo != null && !parsedMovieInfo.MovieTitle.IsNullOrWhiteSpace())
{
RemoteMovie remoteMovie = _parsingService.Map(parsedMovieInfo, report.ImdbId.ToString(), searchCriteria);
remoteMovie.Release = report;
if (remoteMovie.Movie == null)
{
decision = new DownloadDecision(remoteMovie, new Rejection("Unknown movie. Movie found does not match wanted movie."));
}
else
{
if (parsedMovieInfo.Quality.HardcodedSubs.IsNotNullOrWhiteSpace())
{
remoteMovie.DownloadAllowed = true;
if (_configService.AllowHardcodedSubs)
{
decision = GetDecisionForReport(remoteMovie, searchCriteria);
}
else
{
var whitelisted = _configService.WhitelistedHardcodedSubs.Split(',');
_logger.Debug("Testing: {0}", whitelisted);
if (whitelisted != null && whitelisted.Any(t => (parsedMovieInfo.Quality.HardcodedSubs.ToLower().Contains(t.ToLower()) && t.IsNotNullOrWhiteSpace())))
{
decision = GetDecisionForReport(remoteMovie, searchCriteria);
}
else
{
decision = new DownloadDecision(remoteMovie, new Rejection("Hardcoded subs found: " + parsedMovieInfo.Quality.HardcodedSubs));
}
}
2017-03-27 15:07:23 +00:00
}
else
{
remoteMovie.DownloadAllowed = true;
decision = GetDecisionForReport(remoteMovie, searchCriteria);
}
}
}
else
{
_logger.Trace("{0} could not be parsed :(.", report.Title);
}
}
catch (Exception e)
{
_logger.Error(e, "Couldn't process release.");
var remoteMovie = new RemoteMovie { Release = report };
decision = new DownloadDecision(remoteMovie, new Rejection("Unexpected error processing release"));
}
reportNumber++;
if (decision != null)
{
if (decision.Rejections.Any())
{
_logger.Debug("Release rejected for the following reasons: {0}", string.Join(", ", decision.Rejections));
}
else
{
_logger.Debug("Release accepted");
}
yield return decision;
}
}
}
2013-09-13 23:25:30 +00:00
private IEnumerable<DownloadDecision> GetDecisions(List<ReleaseInfo> reports, SearchCriteriaBase searchCriteria = null)
{
if (reports.Any())
{
_logger.ProgressInfo("Processing {0} releases", reports.Count);
}
else
{
_logger.ProgressInfo("No results found");
}
var reportNumber = 1;
foreach (var report in reports)
{
DownloadDecision decision = null;
_logger.ProgressTrace("Processing release {0}/{1}", reportNumber, reports.Count);
try
{
var parsedEpisodeInfo = Parser.Parser.ParseTitle(report.Title);
if (parsedEpisodeInfo == null || parsedEpisodeInfo.IsPossibleSpecialEpisode)
{
var specialEpisodeInfo = _parsingService.ParseSpecialEpisodeTitle(report.Title, report.TvdbId, report.TvRageId, searchCriteria);
if (specialEpisodeInfo != null)
{
parsedEpisodeInfo = specialEpisodeInfo;
}
}
if (parsedEpisodeInfo != null && !parsedEpisodeInfo.SeriesTitle.IsNullOrWhiteSpace())
{
var remoteEpisode = _parsingService.Map(parsedEpisodeInfo, report.TvdbId, report.TvRageId, searchCriteria);
2013-09-13 23:17:58 +00:00
remoteEpisode.Release = report;
if (remoteEpisode.Series == null)
{
//remoteEpisode.DownloadAllowed = true; //Fuck you :)
//decision = GetDecisionForReport(remoteEpisode, searchCriteria);
decision = new DownloadDecision(remoteEpisode, new Rejection("Unknown release. Series not Found."));
}
else if (remoteEpisode.Episodes.Empty())
{
decision = new DownloadDecision(remoteEpisode, new Rejection("Unable to parse episodes from release name"));
}
else
{
remoteEpisode.DownloadAllowed = remoteEpisode.Episodes.Any();
decision = GetDecisionForReport(remoteEpisode, searchCriteria);
}
}
}
catch (Exception e)
{
2016-02-11 21:13:42 +00:00
_logger.Error(e, "Couldn't process release.");
var remoteEpisode = new RemoteEpisode { Release = report };
decision = new DownloadDecision(remoteEpisode, new Rejection("Unexpected error processing release"));
}
reportNumber++;
if (decision != null)
{
if (decision.Rejections.Any())
{
_logger.Debug("Release rejected for the following reasons: {0}", string.Join(", ", decision.Rejections));
}
2015-10-17 04:54:36 +00:00
else
{
_logger.Debug("Release accepted");
}
yield return decision;
}
}
}
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());
}
2014-10-03 23:29:52 +00:00
private DownloadDecision GetDecisionForReport(RemoteEpisode remoteEpisode, SearchCriteriaBase searchCriteria = null)
2013-04-07 07:30:37 +00:00
{
var reasons = _specifications.Select(c => EvaluateSpec(c, remoteEpisode, searchCriteria))
.Where(c => c != null);
return new DownloadDecision(remoteEpisode, reasons.ToArray());
}
private Rejection EvaluateSpec(IDecisionEngineSpecification spec, RemoteEpisode remoteEpisode, SearchCriteriaBase searchCriteriaBase = null)
{
try
{
var result = spec.IsSatisfiedBy(remoteEpisode, searchCriteriaBase);
if (!result.Accepted)
{
return new Rejection(result.Reason, spec.Type);
}
}
catch (Exception e)
{
2013-09-13 23:17:58 +00:00
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;
2013-04-07 07:30:37 +00:00
}
private Rejection EvaluateSpec(IDecisionEngineSpecification spec, RemoteMovie remoteMovie, SearchCriteriaBase searchCriteriaBase = null)
{
try
{
var result = spec.IsSatisfiedBy(remoteMovie, searchCriteriaBase);
if (!result.Accepted)
{
return new Rejection(result.Reason, spec.Type);
}
}
catch (NotImplementedException e)
{
2017-01-10 20:25:36 +00:00
_logger.Trace("Spec " + spec.GetType().Name + " does not care about movies.");
}
catch (Exception e)
{
e.Data.Add("report", remoteMovie.Release.ToJson());
e.Data.Add("parsed", remoteMovie.ParsedMovieInfo.ToJson());
_logger.Error(e, "Couldn't evaluate decision on " + remoteMovie.Release.Title + ", with spec: " + spec.GetType().Name);
return new Rejection(string.Format("{0}: {1}", spec.GetType().Name, e.Message));//TODO UPDATE SPECS!
}
return null;
}
2013-04-07 07:30:37 +00:00
}
2013-09-13 23:17:58 +00:00
}