more search cleanup.
This commit is contained in:
@@ -0,0 +1,73 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using NLog;
|
||||
using NzbDrone.Common.EnsureThat;
|
||||
using NzbDrone.Core.DecisionEngine;
|
||||
using NzbDrone.Core.Download;
|
||||
using NzbDrone.Core.Indexers;
|
||||
using NzbDrone.Core.Model;
|
||||
using NzbDrone.Core.Model.Notification;
|
||||
using NzbDrone.Core.ReferenceData;
|
||||
using NzbDrone.Core.Tv;
|
||||
|
||||
namespace NzbDrone.Core.IndexerSearch
|
||||
{
|
||||
public class DailyEpisodeSearch : SearchBase
|
||||
{
|
||||
private static readonly Logger logger = LogManager.GetCurrentClassLogger();
|
||||
|
||||
public DailyEpisodeSearch(IEpisodeService episodeService, DownloadProvider downloadProvider, IIndexerService indexerService,
|
||||
ISceneMappingService sceneMappingService, IDownloadDirector downloadDirector,
|
||||
ISeriesRepository seriesRepository)
|
||||
: base(seriesRepository, episodeService, downloadProvider, indexerService, sceneMappingService,
|
||||
downloadDirector)
|
||||
{
|
||||
}
|
||||
|
||||
public DailyEpisodeSearch()
|
||||
{
|
||||
}
|
||||
|
||||
public override List<EpisodeParseResult> PerformSearch(Series series, List<Episode> episodes, ProgressNotification notification)
|
||||
{
|
||||
var episode = episodes.Single();
|
||||
|
||||
notification.CurrentMessage = "Looking for " + episode;
|
||||
|
||||
var reports = new List<EpisodeParseResult>();
|
||||
var title = GetSearchTitle(series);
|
||||
|
||||
Parallel.ForEach(_indexerService.GetEnabledIndexers(), indexer =>
|
||||
{
|
||||
try
|
||||
{
|
||||
reports.AddRange(indexer.FetchDailyEpisode(title, episode.AirDate.Value));
|
||||
}
|
||||
|
||||
catch (Exception e)
|
||||
{
|
||||
logger.ErrorException(String.Format("An error has occurred while searching for {0} - {1:yyyy-MM-dd} from: {2}",
|
||||
series.Title, episode.AirDate, indexer.Name), e);
|
||||
}
|
||||
});
|
||||
|
||||
return reports;
|
||||
}
|
||||
|
||||
public override bool IsEpisodeMatch(Series series, dynamic options, EpisodeParseResult episodeParseResult)
|
||||
{
|
||||
Episode episode = options.Episode;
|
||||
|
||||
if (!episodeParseResult.AirDate.HasValue || episodeParseResult.AirDate.Value != episode.AirDate.Value)
|
||||
{
|
||||
logger.Trace("Episode AirDate does not match searched episode number, skipping.");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using NLog;
|
||||
using NzbDrone.Core.DecisionEngine;
|
||||
using NzbDrone.Core.Download;
|
||||
using NzbDrone.Core.Indexers;
|
||||
using NzbDrone.Core.Model;
|
||||
using NzbDrone.Core.Model.Notification;
|
||||
using NzbDrone.Core.ReferenceData;
|
||||
using NzbDrone.Core.Tv;
|
||||
|
||||
namespace NzbDrone.Core.IndexerSearch
|
||||
{
|
||||
public class EpisodeSearch : SearchBase
|
||||
{
|
||||
private static readonly Logger logger = LogManager.GetCurrentClassLogger();
|
||||
|
||||
public EpisodeSearch(IEpisodeService episodeService, DownloadProvider downloadProvider, IIndexerService indexerService,
|
||||
ISceneMappingService sceneMappingService, IDownloadDirector downloadDirector,
|
||||
ISeriesRepository seriesRepository)
|
||||
: base(seriesRepository, episodeService, downloadProvider, indexerService, sceneMappingService,
|
||||
downloadDirector)
|
||||
{
|
||||
}
|
||||
|
||||
public EpisodeSearch()
|
||||
{
|
||||
}
|
||||
|
||||
public override List<EpisodeParseResult> PerformSearch(Series series, List<Episode> episodes, ProgressNotification notification)
|
||||
{
|
||||
//Todo: Daily and Anime or separate them out?
|
||||
//Todo: Epsiodes that use scene numbering
|
||||
|
||||
var episode = episodes.Single();
|
||||
|
||||
|
||||
var reports = new List<EpisodeParseResult>();
|
||||
var title = GetSearchTitle(series);
|
||||
|
||||
var seasonNumber = episode.SeasonNumber;
|
||||
var episodeNumber = episode.EpisodeNumber;
|
||||
|
||||
if (series.UseSceneNumbering)
|
||||
{
|
||||
if (episode.SceneSeasonNumber > 0 && episode.SceneEpisodeNumber > 0)
|
||||
{
|
||||
logger.Trace("Using Scene Numbering for: {0}", episode);
|
||||
seasonNumber = episode.SceneSeasonNumber;
|
||||
episodeNumber = episode.SceneEpisodeNumber;
|
||||
}
|
||||
}
|
||||
|
||||
Parallel.ForEach(_indexerService.GetEnabledIndexers(), indexer =>
|
||||
{
|
||||
try
|
||||
{
|
||||
reports.AddRange(indexer.FetchEpisode(title, seasonNumber, episodeNumber));
|
||||
}
|
||||
|
||||
catch (Exception e)
|
||||
{
|
||||
logger.ErrorException(String.Format("An error has occurred while searching for {0}-S{1:00}E{2:00} from: {3}",
|
||||
series.Title, episode.SeasonNumber, episode.EpisodeNumber, indexer.Name), e);
|
||||
}
|
||||
});
|
||||
|
||||
return reports;
|
||||
}
|
||||
|
||||
public override bool IsEpisodeMatch(Series series, dynamic options, EpisodeParseResult episodeParseResult)
|
||||
{
|
||||
if (series.UseSceneNumbering && options.Episode.SeasonNumber > 0 && options.Episode.EpisodeNumber > 0)
|
||||
{
|
||||
if (options.Episode.SceneSeasonNumber != episodeParseResult.SeasonNumber)
|
||||
{
|
||||
logger.Trace("Season number does not match searched season number, skipping.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!episodeParseResult.EpisodeNumbers.Contains(options.Episode.SceneEpisodeNumber))
|
||||
{
|
||||
logger.Trace("Episode number does not match searched episode number, skipping.");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (options.Episode.SeasonNumber != episodeParseResult.SeasonNumber)
|
||||
{
|
||||
logger.Trace("Season number does not match searched season number, skipping.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!episodeParseResult.EpisodeNumbers.Contains(options.Episode.EpisodeNumber))
|
||||
{
|
||||
logger.Trace("Episode number does not match searched episode number, skipping.");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using NLog;
|
||||
using NzbDrone.Core.DecisionEngine;
|
||||
using NzbDrone.Core.Download;
|
||||
using NzbDrone.Core.Indexers;
|
||||
using NzbDrone.Core.Model;
|
||||
using NzbDrone.Core.Model.Notification;
|
||||
using NzbDrone.Core.ReferenceData;
|
||||
using NzbDrone.Core.Tv;
|
||||
|
||||
namespace NzbDrone.Core.IndexerSearch
|
||||
{
|
||||
public class PartialSeasonSearch : SearchBase
|
||||
{
|
||||
private static readonly Logger logger = LogManager.GetCurrentClassLogger();
|
||||
|
||||
public PartialSeasonSearch(IEpisodeService episodeService, DownloadProvider downloadProvider, IIndexerService indexerService,
|
||||
ISceneMappingService sceneMappingService, IDownloadDirector downloadDirector,
|
||||
ISeriesRepository seriesRepository)
|
||||
: base(seriesRepository, episodeService, downloadProvider, indexerService, sceneMappingService,
|
||||
downloadDirector)
|
||||
{
|
||||
}
|
||||
|
||||
public PartialSeasonSearch()
|
||||
{
|
||||
}
|
||||
|
||||
public override List<EpisodeParseResult> PerformSearch(Series series, List<Episode> episodes, ProgressNotification notification)
|
||||
{
|
||||
var seasons = episodes.Select(c => c.SeasonNumber).Distinct().ToList();
|
||||
|
||||
if (seasons.Count > 1)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("episodes", "episode list contains episodes from more than one season");
|
||||
}
|
||||
|
||||
var seasonNumber = seasons[0];
|
||||
notification.CurrentMessage = String.Format("Looking for {0} - Season {1}", series.Title, seasonNumber);
|
||||
|
||||
var reports = new List<EpisodeParseResult>();
|
||||
object reportsLock = new object();
|
||||
|
||||
var title = GetSearchTitle(series);
|
||||
var prefixes = GetEpisodeNumberPrefixes(episodes.Select(e => e.EpisodeNumber));
|
||||
|
||||
foreach (var p in prefixes)
|
||||
{
|
||||
var prefix = p;
|
||||
|
||||
Parallel.ForEach(_indexerService.GetEnabledIndexers(), indexer =>
|
||||
{
|
||||
try
|
||||
{
|
||||
lock (reportsLock)
|
||||
{
|
||||
reports.AddRange(indexer.FetchPartialSeason(title, seasonNumber, prefix));
|
||||
}
|
||||
}
|
||||
|
||||
catch (Exception e)
|
||||
{
|
||||
logger.ErrorException(
|
||||
String.Format(
|
||||
"An error has occurred while searching for {0} Season {1:00} Prefix: {2} from: {3}",
|
||||
series.Title, seasonNumber, prefix, indexer.Name),
|
||||
e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return reports;
|
||||
}
|
||||
|
||||
public override bool IsEpisodeMatch(Series series, dynamic options, EpisodeParseResult episodeParseResult)
|
||||
{
|
||||
if (options.SeasonNumber != episodeParseResult.SeasonNumber)
|
||||
{
|
||||
logger.Trace("Season number does not match searched season number, skipping.");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
private List<int> GetEpisodeNumberPrefixes(IEnumerable<int> episodeNumbers)
|
||||
{
|
||||
var results = new List<int>();
|
||||
|
||||
foreach (var i in episodeNumbers)
|
||||
{
|
||||
results.Add(i / 10);
|
||||
}
|
||||
|
||||
return results.Distinct().ToList();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using NLog;
|
||||
using NzbDrone.Core.DecisionEngine;
|
||||
using NzbDrone.Core.Download;
|
||||
using NzbDrone.Core.Indexers;
|
||||
using NzbDrone.Core.Model;
|
||||
using NzbDrone.Core.Model.Notification;
|
||||
using NzbDrone.Core.ReferenceData;
|
||||
using NzbDrone.Core.Tv;
|
||||
|
||||
namespace NzbDrone.Core.IndexerSearch
|
||||
{
|
||||
public abstract class SearchBase
|
||||
{
|
||||
private readonly ISeriesRepository _seriesRepository;
|
||||
protected readonly IEpisodeService _episodeService;
|
||||
protected readonly DownloadProvider _downloadProvider;
|
||||
protected readonly IIndexerService _indexerService;
|
||||
protected readonly ISceneMappingService _sceneMappingService;
|
||||
protected readonly IDownloadDirector DownloadDirector;
|
||||
|
||||
private static readonly Logger logger = LogManager.GetCurrentClassLogger();
|
||||
|
||||
protected SearchBase(ISeriesRepository seriesRepository, IEpisodeService episodeService, DownloadProvider downloadProvider,
|
||||
IIndexerService indexerService, ISceneMappingService sceneMappingService,
|
||||
IDownloadDirector downloadDirector)
|
||||
{
|
||||
_seriesRepository = seriesRepository;
|
||||
_episodeService = episodeService;
|
||||
_downloadProvider = downloadProvider;
|
||||
_indexerService = indexerService;
|
||||
_sceneMappingService = sceneMappingService;
|
||||
DownloadDirector = downloadDirector;
|
||||
}
|
||||
|
||||
protected SearchBase()
|
||||
{
|
||||
}
|
||||
|
||||
public abstract List<EpisodeParseResult> PerformSearch(Series series, List<Episode> episodes, ProgressNotification notification);
|
||||
public abstract bool IsEpisodeMatch(Series series, dynamic options, EpisodeParseResult episodeParseResult);
|
||||
|
||||
public virtual List<int> Search(Series series, dynamic options, ProgressNotification notification)
|
||||
{
|
||||
if (options == null)
|
||||
throw new ArgumentNullException(options);
|
||||
|
||||
|
||||
List<EpisodeParseResult> reports = PerformSearch(series, options, notification);
|
||||
|
||||
logger.Debug("Finished searching all indexers. Total {0}", reports.Count);
|
||||
notification.CurrentMessage = "Processing search results";
|
||||
|
||||
var result = ProcessReports(series, options, reports);
|
||||
|
||||
if (!result.Grabbed.Any())
|
||||
{
|
||||
logger.Warn("Unable to find {0} in any of indexers.", options.Episode);
|
||||
|
||||
notification.CurrentMessage = reports.Any() ? String.Format("Sorry, couldn't find {0}, that matches your preferences.", options.Episode)
|
||||
: String.Format("Sorry, couldn't find {0} in any of indexers.", options.Episode);
|
||||
}
|
||||
|
||||
return result.Grabbed;
|
||||
}
|
||||
|
||||
public void ProcessReports(Series series, dynamic options, List<EpisodeParseResult> episodeParseResults)
|
||||
{
|
||||
|
||||
var sortedResults = episodeParseResults.OrderByDescending(c => c.Quality)
|
||||
.ThenBy(c => c.EpisodeNumbers.MinOrDefault())
|
||||
.ThenBy(c => c.Age);
|
||||
|
||||
foreach (var episodeParseResult in sortedResults)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
||||
logger.Trace("Analyzing report " + episodeParseResult);
|
||||
episodeParseResult.Series = _seriesRepository.GetByTitle(episodeParseResult.CleanTitle);
|
||||
|
||||
if (episodeParseResult.Series == null || episodeParseResult.Series.Id != series.Id)
|
||||
{
|
||||
episodeParseResult.Decision = new DownloadDecision("Invalid Series");
|
||||
continue;
|
||||
}
|
||||
|
||||
episodeParseResult.Episodes = _episodeService.GetEpisodesByParseResult(episodeParseResult);
|
||||
|
||||
|
||||
if (!IsEpisodeMatch(series, options, episodeParseResult))
|
||||
{
|
||||
episodeParseResult.Decision = new DownloadDecision("Incorrect Episode/Season");
|
||||
}
|
||||
|
||||
var downloadDecision = DownloadDirector.GetDownloadDecision(episodeParseResult);
|
||||
|
||||
if (downloadDecision.Approved)
|
||||
{
|
||||
DownloadReport(episodeParseResult);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
logger.ErrorException("An error has occurred while processing parse result items from " + episodeParseResult, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public virtual Boolean DownloadReport(EpisodeParseResult episodeParseResult)
|
||||
{
|
||||
logger.Debug("Found '{0}'. Adding to download queue.", episodeParseResult);
|
||||
try
|
||||
{
|
||||
if (_downloadProvider.DownloadReport(episodeParseResult))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
logger.ErrorException("Unable to add report to download queue." + episodeParseResult, e);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public virtual string GetSearchTitle(Series series, int seasonNumber = -1)
|
||||
{
|
||||
var seasonTitle = _sceneMappingService.GetSceneName(series.Id, seasonNumber);
|
||||
|
||||
if (!String.IsNullOrWhiteSpace(seasonTitle))
|
||||
return seasonTitle;
|
||||
|
||||
var title = _sceneMappingService.GetSceneName(series.Id);
|
||||
|
||||
if (String.IsNullOrWhiteSpace(title))
|
||||
{
|
||||
title = series.Title;
|
||||
title = title.Replace("&", "and");
|
||||
title = Regex.Replace(title, @"[^\w\d\s\-]", "");
|
||||
}
|
||||
|
||||
return title;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user