Use episodes attached to search criteria instead of fetching from DB

pull/4/head
Mark McDowall 11 years ago
parent 372ac79fe6
commit 5b5ddf67f8

@ -4,6 +4,7 @@ using FluentAssertions;
using Moq; using Moq;
using NUnit.Framework; using NUnit.Framework;
using NzbDrone.Core.DecisionEngine; using NzbDrone.Core.DecisionEngine;
using NzbDrone.Core.IndexerSearch.Definitions;
using NzbDrone.Core.Parser; using NzbDrone.Core.Parser;
using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Test.Framework;
@ -59,7 +60,8 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
_reports = new List<ReleaseInfo> { new ReleaseInfo { Title = "The.Office.S03E115.DVDRip.XviD-OSiTV" } }; _reports = new List<ReleaseInfo> { new ReleaseInfo { Title = "The.Office.S03E115.DVDRip.XviD-OSiTV" } };
_remoteEpisode = new RemoteEpisode { Series = new Series() }; _remoteEpisode = new RemoteEpisode { Series = new Series() };
Mocker.GetMock<IParsingService>().Setup(c => c.Map(It.IsAny<ParsedEpisodeInfo>(), It.IsAny<int>())) Mocker.GetMock<IParsingService>()
.Setup(c => c.Map(It.IsAny<ParsedEpisodeInfo>(), It.IsAny<int>(), It.IsAny<SearchCriteriaBase>()))
.Returns(_remoteEpisode); .Returns(_remoteEpisode);
} }
@ -130,7 +132,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
var results = Subject.GetRssDecision(_reports).ToList(); var results = Subject.GetRssDecision(_reports).ToList();
Mocker.GetMock<IParsingService>().Verify(c => c.Map(It.IsAny<ParsedEpisodeInfo>(), It.IsAny<int>()), Times.Never()); Mocker.GetMock<IParsingService>().Verify(c => c.Map(It.IsAny<ParsedEpisodeInfo>(), It.IsAny<int>(), It.IsAny<SearchCriteriaBase>()), Times.Never());
_pass1.Verify(c => c.IsSatisfiedBy(It.IsAny<RemoteEpisode>(), null), Times.Never()); _pass1.Verify(c => c.IsSatisfiedBy(It.IsAny<RemoteEpisode>(), null), Times.Never());
_pass2.Verify(c => c.IsSatisfiedBy(It.IsAny<RemoteEpisode>(), null), Times.Never()); _pass2.Verify(c => c.IsSatisfiedBy(It.IsAny<RemoteEpisode>(), null), Times.Never());
@ -146,7 +148,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
var results = Subject.GetRssDecision(_reports).ToList(); var results = Subject.GetRssDecision(_reports).ToList();
Mocker.GetMock<IParsingService>().Verify(c => c.Map(It.IsAny<ParsedEpisodeInfo>(), It.IsAny<int>()), Times.Never()); Mocker.GetMock<IParsingService>().Verify(c => c.Map(It.IsAny<ParsedEpisodeInfo>(), It.IsAny<int>(), It.IsAny<SearchCriteriaBase>()), Times.Never());
_pass1.Verify(c => c.IsSatisfiedBy(It.IsAny<RemoteEpisode>(), null), Times.Never()); _pass1.Verify(c => c.IsSatisfiedBy(It.IsAny<RemoteEpisode>(), null), Times.Never());
_pass2.Verify(c => c.IsSatisfiedBy(It.IsAny<RemoteEpisode>(), null), Times.Never()); _pass2.Verify(c => c.IsSatisfiedBy(It.IsAny<RemoteEpisode>(), null), Times.Never());
@ -174,7 +176,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
{ {
GivenSpecifications(_pass1); GivenSpecifications(_pass1);
Mocker.GetMock<IParsingService>().Setup(c => c.Map(It.IsAny<ParsedEpisodeInfo>(), It.IsAny<int>())) Mocker.GetMock<IParsingService>().Setup(c => c.Map(It.IsAny<ParsedEpisodeInfo>(), It.IsAny<int>(), It.IsAny<SearchCriteriaBase>()))
.Throws<TestException>(); .Throws<TestException>();
_reports = new List<ReleaseInfo> _reports = new List<ReleaseInfo>
@ -186,7 +188,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
Subject.GetRssDecision(_reports); Subject.GetRssDecision(_reports);
Mocker.GetMock<IParsingService>().Verify(c => c.Map(It.IsAny<ParsedEpisodeInfo>(), It.IsAny<int>()), Times.Exactly(_reports.Count)); Mocker.GetMock<IParsingService>().Verify(c => c.Map(It.IsAny<ParsedEpisodeInfo>(), It.IsAny<int>(), It.IsAny<SearchCriteriaBase>()), Times.Exactly(_reports.Count));
ExceptionVerification.ExpectedErrors(3); ExceptionVerification.ExpectedErrors(3);
} }

@ -65,7 +65,7 @@ namespace NzbDrone.Core.DecisionEngine
if (parsedEpisodeInfo != null && !string.IsNullOrWhiteSpace(parsedEpisodeInfo.SeriesTitle)) if (parsedEpisodeInfo != null && !string.IsNullOrWhiteSpace(parsedEpisodeInfo.SeriesTitle))
{ {
var remoteEpisode = _parsingService.Map(parsedEpisodeInfo, report.TvRageId); var remoteEpisode = _parsingService.Map(parsedEpisodeInfo, report.TvRageId, searchCriteria);
remoteEpisode.Release = report; remoteEpisode.Release = report;
if (remoteEpisode.Series != null) if (remoteEpisode.Series != null)

@ -34,7 +34,7 @@ namespace NzbDrone.Core.DecisionEngine.Specifications.Search
if (dailySearchSpec == null) return true; if (dailySearchSpec == null) return true;
var episode = _episodeService.GetEpisode(dailySearchSpec.SeriesId, dailySearchSpec.Airtime); var episode = _episodeService.GetEpisode(dailySearchSpec.Series.Id, dailySearchSpec.Airtime);
if (!remoteEpisode.ParsedEpisodeInfo.AirDate.HasValue || remoteEpisode.ParsedEpisodeInfo.AirDate.Value.ToString(Episode.AIR_DATE_FORMAT) != episode.AirDate) if (!remoteEpisode.ParsedEpisodeInfo.AirDate.HasValue || remoteEpisode.ParsedEpisodeInfo.AirDate.Value.ToString(Episode.AIR_DATE_FORMAT) != episode.AirDate)
{ {

@ -1,17 +1,19 @@
using System; using System;
using System.Collections.Generic;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using NzbDrone.Common.EnsureThat; using NzbDrone.Common.EnsureThat;
using NzbDrone.Core.Tv;
namespace NzbDrone.Core.IndexerSearch.Definitions namespace NzbDrone.Core.IndexerSearch.Definitions
{ {
public abstract class SearchCriteriaBase public abstract class SearchCriteriaBase
{ {
private static readonly Regex NoneWord = new Regex(@"[\W]", RegexOptions.IgnoreCase | RegexOptions.Compiled); private static readonly Regex NonWord = new Regex(@"[\W]", RegexOptions.IgnoreCase | RegexOptions.Compiled);
private static readonly Regex BeginningThe = new Regex(@"^the\s", RegexOptions.IgnoreCase | RegexOptions.Compiled); private static readonly Regex BeginningThe = new Regex(@"^the\s", RegexOptions.IgnoreCase | RegexOptions.Compiled);
public int SeriesId { get; set; } public Series Series { get; set; }
public int SeriesTvRageId { get; set; }
public string SceneTitle { get; set; } public string SceneTitle { get; set; }
public List<Episode> Episodes { get; set; }
public string QueryTitle public string QueryTitle
{ {
@ -32,7 +34,7 @@ namespace NzbDrone.Core.IndexerSearch.Definitions
.Replace("`", "") .Replace("`", "")
.Replace("'", ""); .Replace("'", "");
cleanTitle = NoneWord.Replace(cleanTitle, "+"); cleanTitle = NonWord.Replace(cleanTitle, "+");
//remove any repeating +s //remove any repeating +s
cleanTitle = Regex.Replace(cleanTitle, @"\+{2,}", "+"); cleanTitle = Regex.Replace(cleanTitle, @"\+{2,}", "+");

@ -61,7 +61,7 @@ namespace NzbDrone.Core.IndexerSearch
throw new InvalidOperationException("Daily episode is missing AirDate. Try to refresh series info."); throw new InvalidOperationException("Daily episode is missing AirDate. Try to refresh series info.");
} }
return SearchDaily(series, DateTime.ParseExact(episode.AirDate, Episode.AIR_DATE_FORMAT, CultureInfo.InvariantCulture)); return SearchDaily(series, episode);
} }
return SearchSingle(series, episode); return SearchSingle(series, episode);
@ -69,7 +69,7 @@ namespace NzbDrone.Core.IndexerSearch
private List<DownloadDecision> SearchSingle(Series series, Episode episode) private List<DownloadDecision> SearchSingle(Series series, Episode episode)
{ {
var searchSpec = Get<SingleEpisodeSearchCriteria>(series, episode.SeasonNumber); var searchSpec = Get<SingleEpisodeSearchCriteria>(series, new List<Episode>{episode});
if (series.UseSceneNumbering) if (series.UseSceneNumbering)
{ {
@ -94,9 +94,10 @@ namespace NzbDrone.Core.IndexerSearch
return Dispatch(indexer => _feedFetcher.Fetch(indexer, searchSpec), searchSpec); return Dispatch(indexer => _feedFetcher.Fetch(indexer, searchSpec), searchSpec);
} }
private List<DownloadDecision> SearchDaily(Series series, DateTime airDate) private List<DownloadDecision> SearchDaily(Series series, Episode episode)
{ {
var searchSpec = Get<DailyEpisodeSearchCriteria>(series); var airDate = DateTime.ParseExact(episode.AirDate, Episode.AIR_DATE_FORMAT, CultureInfo.InvariantCulture);
var searchSpec = Get<DailyEpisodeSearchCriteria>(series, new List<Episode>{ episode });
searchSpec.Airtime = airDate; searchSpec.Airtime = airDate;
return Dispatch(indexer => _feedFetcher.Fetch(indexer, searchSpec), searchSpec); return Dispatch(indexer => _feedFetcher.Fetch(indexer, searchSpec), searchSpec);
@ -105,20 +106,21 @@ namespace NzbDrone.Core.IndexerSearch
public List<DownloadDecision> SeasonSearch(int seriesId, int seasonNumber) public List<DownloadDecision> SeasonSearch(int seriesId, int seasonNumber)
{ {
var series = _seriesService.GetSeries(seriesId); var series = _seriesService.GetSeries(seriesId);
var episodes = _episodeService.GetEpisodesBySeason(seriesId, seasonNumber);
var searchSpec = Get<SeasonSearchCriteria>(series, seasonNumber); var searchSpec = Get<SeasonSearchCriteria>(series, episodes);
searchSpec.SeasonNumber = seasonNumber; searchSpec.SeasonNumber = seasonNumber;
return Dispatch(indexer => _feedFetcher.Fetch(indexer, searchSpec), searchSpec); return Dispatch(indexer => _feedFetcher.Fetch(indexer, searchSpec), searchSpec);
} }
private TSpec Get<TSpec>(Series series, int seasonNumber = -1) where TSpec : SearchCriteriaBase, new() private TSpec Get<TSpec>(Series series, List<Episode> episodes) where TSpec : SearchCriteriaBase, new()
{ {
var spec = new TSpec(); var spec = new TSpec();
spec.SeriesId = series.Id; spec.Series = series;
spec.SeriesTvRageId = series.TvRageId;
spec.SceneTitle = _sceneMapping.GetSceneName(series.TvdbId); spec.SceneTitle = _sceneMapping.GetSceneName(series.TvdbId);
spec.Episodes = episodes;
if (string.IsNullOrWhiteSpace(spec.SceneTitle)) if (string.IsNullOrWhiteSpace(spec.SceneTitle))
{ {

@ -58,7 +58,7 @@ namespace NzbDrone.Core.Indexers
{ {
_logger.Debug("Searching for {0} offset: {1}", searchCriteria, offset); _logger.Debug("Searching for {0} offset: {1}", searchCriteria, offset);
var searchUrls = indexer.GetSeasonSearchUrls(searchCriteria.QueryTitle, searchCriteria.SeriesTvRageId, searchCriteria.SeasonNumber, offset); var searchUrls = indexer.GetSeasonSearchUrls(searchCriteria.QueryTitle, searchCriteria.Series.TvRageId, searchCriteria.SeasonNumber, offset);
var result = Fetch(indexer, searchUrls); var result = Fetch(indexer, searchUrls);
@ -76,7 +76,7 @@ namespace NzbDrone.Core.Indexers
{ {
_logger.Debug("Searching for {0}", searchCriteria); _logger.Debug("Searching for {0}", searchCriteria);
var searchUrls = indexer.GetEpisodeSearchUrls(searchCriteria.QueryTitle, searchCriteria.SeriesTvRageId, searchCriteria.SeasonNumber, searchCriteria.EpisodeNumber); var searchUrls = indexer.GetEpisodeSearchUrls(searchCriteria.QueryTitle, searchCriteria.Series.TvRageId, searchCriteria.SeasonNumber, searchCriteria.EpisodeNumber);
var result = Fetch(indexer, searchUrls); var result = Fetch(indexer, searchUrls);
@ -88,7 +88,7 @@ namespace NzbDrone.Core.Indexers
{ {
_logger.Debug("Searching for {0}", searchCriteria); _logger.Debug("Searching for {0}", searchCriteria);
var searchUrls = indexer.GetDailyEpisodeSearchUrls(searchCriteria.QueryTitle, searchCriteria.SeriesTvRageId, searchCriteria.Airtime); var searchUrls = indexer.GetDailyEpisodeSearchUrls(searchCriteria.QueryTitle, searchCriteria.Series.TvRageId, searchCriteria.Airtime);
var result = Fetch(indexer, searchUrls); var result = Fetch(indexer, searchUrls);
_logger.Info("Finished searching {0} for {1}. Found {2}", indexer.Name, searchCriteria, result.Count); _logger.Info("Finished searching {0} for {1}. Found {2}", indexer.Name, searchCriteria, result.Count);

@ -1,7 +1,10 @@
using System.Collections.Generic; using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using NLog; using NLog;
using NzbDrone.Common; using NzbDrone.Common;
using NzbDrone.Core.DataAugmentation.Scene;
using NzbDrone.Core.IndexerSearch.Definitions;
using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Tv; using NzbDrone.Core.Tv;
@ -9,10 +12,9 @@ namespace NzbDrone.Core.Parser
{ {
public interface IParsingService public interface IParsingService
{ {
LocalEpisode GetEpisodes(string filename, Series series);
LocalEpisode GetEpisodes(string filename, Series series, bool sceneSource); LocalEpisode GetEpisodes(string filename, Series series, bool sceneSource);
Series GetSeries(string title); Series GetSeries(string title);
RemoteEpisode Map(ParsedEpisodeInfo parsedEpisodeInfo, int tvRageId); RemoteEpisode Map(ParsedEpisodeInfo parsedEpisodeInfo, int tvRageId, SearchCriteriaBase searchCriteria = null);
} }
public class ParsingService : IParsingService public class ParsingService : IParsingService
@ -20,24 +22,22 @@ namespace NzbDrone.Core.Parser
private readonly IEpisodeService _episodeService; private readonly IEpisodeService _episodeService;
private readonly ISeriesService _seriesService; private readonly ISeriesService _seriesService;
private readonly IDiskProvider _diskProvider; private readonly IDiskProvider _diskProvider;
private readonly ISceneMappingService _sceneMappingService;
private readonly Logger _logger; private readonly Logger _logger;
public ParsingService(IEpisodeService episodeService, public ParsingService(IEpisodeService episodeService,
ISeriesService seriesService, ISeriesService seriesService,
IDiskProvider diskProvider, IDiskProvider diskProvider,
ISceneMappingService sceneMappingService,
Logger logger) Logger logger)
{ {
_episodeService = episodeService; _episodeService = episodeService;
_seriesService = seriesService; _seriesService = seriesService;
_diskProvider = diskProvider; _diskProvider = diskProvider;
_sceneMappingService = sceneMappingService;
_logger = logger; _logger = logger;
} }
public LocalEpisode GetEpisodes(string filename, Series series)
{
return GetEpisodes(filename, series, false);
}
public LocalEpisode GetEpisodes(string filename, Series series, bool sceneSource) public LocalEpisode GetEpisodes(string filename, Series series, bool sceneSource)
{ {
var parsedEpisodeInfo = Parser.ParsePath(filename); var parsedEpisodeInfo = Parser.ParsePath(filename);
@ -68,7 +68,6 @@ namespace NzbDrone.Core.Parser
public Series GetSeries(string title) public Series GetSeries(string title)
{ {
var searchTitle = title; var searchTitle = title;
var parsedEpisodeInfo = Parser.ParseTitle(title); var parsedEpisodeInfo = Parser.ParseTitle(title);
if (parsedEpisodeInfo != null) if (parsedEpisodeInfo != null)
@ -79,34 +78,73 @@ namespace NzbDrone.Core.Parser
return _seriesService.FindByTitle(searchTitle); return _seriesService.FindByTitle(searchTitle);
} }
public RemoteEpisode Map(ParsedEpisodeInfo parsedEpisodeInfo, int tvRageId) public RemoteEpisode Map(ParsedEpisodeInfo parsedEpisodeInfo, int tvRageId, SearchCriteriaBase searchCriteria = null)
{ {
var remoteEpisode = new RemoteEpisode var remoteEpisode = new RemoteEpisode
{ {
ParsedEpisodeInfo = parsedEpisodeInfo, ParsedEpisodeInfo = parsedEpisodeInfo,
}; };
var series = searchCriteria == null ? GetSeries(parsedEpisodeInfo, tvRageId) :
GetSeries(parsedEpisodeInfo, tvRageId, searchCriteria);
if (series == null)
{
return remoteEpisode;
}
remoteEpisode.Series = series;
remoteEpisode.Episodes = GetEpisodes(parsedEpisodeInfo, series, true, searchCriteria);
return remoteEpisode;
}
private Series GetSeries(ParsedEpisodeInfo parsedEpisodeInfo, int tvRageId, SearchCriteriaBase searchCriteria)
{
var tvdbId = _sceneMappingService.GetTvDbId(parsedEpisodeInfo.SeriesTitle);
if (tvdbId.HasValue)
{
if (searchCriteria.Series.TvdbId == tvdbId)
{
return searchCriteria.Series;
}
}
if (parsedEpisodeInfo.SeriesTitle.CleanSeriesTitle() == searchCriteria.Series.CleanTitle)
{
return searchCriteria.Series;
}
if (tvRageId == searchCriteria.Series.TvRageId)
{
//TODO: If series is found by TvRageId, we should report it as a scene naming exception, since it will fail to import
return searchCriteria.Series;
}
return GetSeries(parsedEpisodeInfo, tvRageId);
}
private Series GetSeries(ParsedEpisodeInfo parsedEpisodeInfo, int tvRageId)
{
var series = _seriesService.FindByTitle(parsedEpisodeInfo.SeriesTitle); var series = _seriesService.FindByTitle(parsedEpisodeInfo.SeriesTitle);
if (series == null && tvRageId > 0) if (series == null && tvRageId > 0)
{ {
series = _seriesService.FindByTvRageId(tvRageId);
//TODO: If series is found by TvRageId, we should report it as a scene naming exception, since it will fail to import //TODO: If series is found by TvRageId, we should report it as a scene naming exception, since it will fail to import
series = _seriesService.FindByTvRageId(tvRageId);
} }
if (series == null) if (series == null)
{ {
_logger.Trace("No matching series {0}", parsedEpisodeInfo.SeriesTitle); _logger.Trace("No matching series {0}", parsedEpisodeInfo.SeriesTitle);
return remoteEpisode; return null;
} }
remoteEpisode.Series = series; return series;
remoteEpisode.Episodes = GetEpisodes(parsedEpisodeInfo, series, true);
return remoteEpisode;
} }
private List<Episode> GetEpisodes(ParsedEpisodeInfo parsedEpisodeInfo, Series series, bool sceneSource) private List<Episode> GetEpisodes(ParsedEpisodeInfo parsedEpisodeInfo, Series series, bool sceneSource, SearchCriteriaBase searchCriteria = null)
{ {
var result = new List<Episode>(); var result = new List<Episode>();
@ -115,10 +153,10 @@ namespace NzbDrone.Core.Parser
if (series.SeriesType == SeriesTypes.Standard) if (series.SeriesType == SeriesTypes.Standard)
{ {
_logger.Warn("Found daily-style episode for non-daily series: {0}.", series); _logger.Warn("Found daily-style episode for non-daily series: {0}.", series);
return new List<Episode>(); return null;
} }
var episodeInfo = _episodeService.GetEpisode(series.Id, parsedEpisodeInfo.AirDate.Value); var episodeInfo = GetDailyEpisode(series, parsedEpisodeInfo.AirDate.Value, searchCriteria);
if (episodeInfo != null) if (episodeInfo != null)
{ {
@ -137,7 +175,16 @@ namespace NzbDrone.Core.Parser
if (series.UseSceneNumbering && sceneSource) if (series.UseSceneNumbering && sceneSource)
{ {
episodeInfo = _episodeService.FindEpisode(series.Id, parsedEpisodeInfo.SeasonNumber, episodeNumber, true); if (searchCriteria != null)
{
episodeInfo = searchCriteria.Episodes.SingleOrDefault(e => e.SceneSeasonNumber == parsedEpisodeInfo.SeasonNumber &&
e.SceneEpisodeNumber == episodeNumber);
}
if (episodeInfo == null)
{
episodeInfo = _episodeService.FindEpisode(series.Id, parsedEpisodeInfo.SeasonNumber, episodeNumber, true);
}
if (episodeInfo != null) if (episodeInfo != null)
{ {
@ -150,6 +197,12 @@ namespace NzbDrone.Core.Parser
} }
} }
if (episodeInfo == null && searchCriteria != null)
{
episodeInfo = searchCriteria.Episodes.SingleOrDefault(e => e.SeasonNumber == parsedEpisodeInfo.SeasonNumber &&
e.EpisodeNumber == episodeNumber);
}
if (episodeInfo == null) if (episodeInfo == null)
{ {
episodeInfo = _episodeService.GetEpisode(series.Id, parsedEpisodeInfo.SeasonNumber, episodeNumber); episodeInfo = _episodeService.GetEpisode(series.Id, parsedEpisodeInfo.SeasonNumber, episodeNumber);
@ -159,6 +212,7 @@ namespace NzbDrone.Core.Parser
{ {
result.Add(episodeInfo); result.Add(episodeInfo);
} }
else else
{ {
_logger.Debug("Unable to find {0}", parsedEpisodeInfo); _logger.Debug("Unable to find {0}", parsedEpisodeInfo);
@ -167,5 +221,23 @@ namespace NzbDrone.Core.Parser
return result; return result;
} }
private Episode GetDailyEpisode(Series series, DateTime airDate, SearchCriteriaBase searchCriteria)
{
Episode episodeInfo = null;
if (searchCriteria != null)
{
episodeInfo = searchCriteria.Episodes.SingleOrDefault(
e => e.AirDate == airDate.ToString(Episode.AIR_DATE_FORMAT));
}
if (episodeInfo == null)
{
episodeInfo = _episodeService.GetEpisode(series.Id, airDate);
}
return episodeInfo;
}
} }
} }
Loading…
Cancel
Save