From 5b5ddf67f85c884af55b744ae15a618bc60fab5e Mon Sep 17 00:00:00 2001 From: Mark McDowall Date: Sat, 14 Sep 2013 20:49:58 -0700 Subject: [PATCH] Use episodes attached to search criteria instead of fetching from DB --- .../DownloadDecisionMakerFixture.cs | 12 +- .../DecisionEngine/DownloadDecisionMaker.cs | 2 +- .../Search/DailyEpisodeMatchSpecification.cs | 2 +- .../Definitions/SearchCriteriaBase.cs | 10 +- .../IndexerSearch/NzbSearchService.cs | 18 +-- NzbDrone.Core/Indexers/IndexerFetchService.cs | 6 +- NzbDrone.Core/Parser/ParsingService.cs | 114 ++++++++++++++---- 7 files changed, 121 insertions(+), 43 deletions(-) diff --git a/NzbDrone.Core.Test/DecisionEngineTests/DownloadDecisionMakerFixture.cs b/NzbDrone.Core.Test/DecisionEngineTests/DownloadDecisionMakerFixture.cs index 697e0b8ae..d250c4d98 100644 --- a/NzbDrone.Core.Test/DecisionEngineTests/DownloadDecisionMakerFixture.cs +++ b/NzbDrone.Core.Test/DecisionEngineTests/DownloadDecisionMakerFixture.cs @@ -4,6 +4,7 @@ using FluentAssertions; using Moq; using NUnit.Framework; using NzbDrone.Core.DecisionEngine; +using NzbDrone.Core.IndexerSearch.Definitions; using NzbDrone.Core.Parser; using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Test.Framework; @@ -59,7 +60,8 @@ namespace NzbDrone.Core.Test.DecisionEngineTests _reports = new List { new ReleaseInfo { Title = "The.Office.S03E115.DVDRip.XviD-OSiTV" } }; _remoteEpisode = new RemoteEpisode { Series = new Series() }; - Mocker.GetMock().Setup(c => c.Map(It.IsAny(), It.IsAny())) + Mocker.GetMock() + .Setup(c => c.Map(It.IsAny(), It.IsAny(), It.IsAny())) .Returns(_remoteEpisode); } @@ -130,7 +132,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests var results = Subject.GetRssDecision(_reports).ToList(); - Mocker.GetMock().Verify(c => c.Map(It.IsAny(), It.IsAny()), Times.Never()); + Mocker.GetMock().Verify(c => c.Map(It.IsAny(), It.IsAny(), It.IsAny()), Times.Never()); _pass1.Verify(c => c.IsSatisfiedBy(It.IsAny(), null), Times.Never()); _pass2.Verify(c => c.IsSatisfiedBy(It.IsAny(), null), Times.Never()); @@ -146,7 +148,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests var results = Subject.GetRssDecision(_reports).ToList(); - Mocker.GetMock().Verify(c => c.Map(It.IsAny(), It.IsAny()), Times.Never()); + Mocker.GetMock().Verify(c => c.Map(It.IsAny(), It.IsAny(), It.IsAny()), Times.Never()); _pass1.Verify(c => c.IsSatisfiedBy(It.IsAny(), null), Times.Never()); _pass2.Verify(c => c.IsSatisfiedBy(It.IsAny(), null), Times.Never()); @@ -174,7 +176,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests { GivenSpecifications(_pass1); - Mocker.GetMock().Setup(c => c.Map(It.IsAny(), It.IsAny())) + Mocker.GetMock().Setup(c => c.Map(It.IsAny(), It.IsAny(), It.IsAny())) .Throws(); _reports = new List @@ -186,7 +188,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests Subject.GetRssDecision(_reports); - Mocker.GetMock().Verify(c => c.Map(It.IsAny(), It.IsAny()), Times.Exactly(_reports.Count)); + Mocker.GetMock().Verify(c => c.Map(It.IsAny(), It.IsAny(), It.IsAny()), Times.Exactly(_reports.Count)); ExceptionVerification.ExpectedErrors(3); } diff --git a/NzbDrone.Core/DecisionEngine/DownloadDecisionMaker.cs b/NzbDrone.Core/DecisionEngine/DownloadDecisionMaker.cs index 425cc8bae..f2b1404cf 100644 --- a/NzbDrone.Core/DecisionEngine/DownloadDecisionMaker.cs +++ b/NzbDrone.Core/DecisionEngine/DownloadDecisionMaker.cs @@ -65,7 +65,7 @@ namespace NzbDrone.Core.DecisionEngine 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; if (remoteEpisode.Series != null) diff --git a/NzbDrone.Core/DecisionEngine/Specifications/Search/DailyEpisodeMatchSpecification.cs b/NzbDrone.Core/DecisionEngine/Specifications/Search/DailyEpisodeMatchSpecification.cs index df4bb37b0..daaf3146c 100644 --- a/NzbDrone.Core/DecisionEngine/Specifications/Search/DailyEpisodeMatchSpecification.cs +++ b/NzbDrone.Core/DecisionEngine/Specifications/Search/DailyEpisodeMatchSpecification.cs @@ -34,7 +34,7 @@ namespace NzbDrone.Core.DecisionEngine.Specifications.Search 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) { diff --git a/NzbDrone.Core/IndexerSearch/Definitions/SearchCriteriaBase.cs b/NzbDrone.Core/IndexerSearch/Definitions/SearchCriteriaBase.cs index 11d3d4d79..20a6e8a39 100644 --- a/NzbDrone.Core/IndexerSearch/Definitions/SearchCriteriaBase.cs +++ b/NzbDrone.Core/IndexerSearch/Definitions/SearchCriteriaBase.cs @@ -1,17 +1,19 @@ using System; +using System.Collections.Generic; using System.Text.RegularExpressions; using NzbDrone.Common.EnsureThat; +using NzbDrone.Core.Tv; namespace NzbDrone.Core.IndexerSearch.Definitions { 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); - public int SeriesId { get; set; } - public int SeriesTvRageId { get; set; } + public Series Series { get; set; } public string SceneTitle { get; set; } + public List Episodes { get; set; } public string QueryTitle { @@ -32,7 +34,7 @@ namespace NzbDrone.Core.IndexerSearch.Definitions .Replace("`", "") .Replace("'", ""); - cleanTitle = NoneWord.Replace(cleanTitle, "+"); + cleanTitle = NonWord.Replace(cleanTitle, "+"); //remove any repeating +s cleanTitle = Regex.Replace(cleanTitle, @"\+{2,}", "+"); diff --git a/NzbDrone.Core/IndexerSearch/NzbSearchService.cs b/NzbDrone.Core/IndexerSearch/NzbSearchService.cs index 10ce200d6..5770687b8 100644 --- a/NzbDrone.Core/IndexerSearch/NzbSearchService.cs +++ b/NzbDrone.Core/IndexerSearch/NzbSearchService.cs @@ -61,7 +61,7 @@ namespace NzbDrone.Core.IndexerSearch 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); @@ -69,7 +69,7 @@ namespace NzbDrone.Core.IndexerSearch private List SearchSingle(Series series, Episode episode) { - var searchSpec = Get(series, episode.SeasonNumber); + var searchSpec = Get(series, new List{episode}); if (series.UseSceneNumbering) { @@ -94,9 +94,10 @@ namespace NzbDrone.Core.IndexerSearch return Dispatch(indexer => _feedFetcher.Fetch(indexer, searchSpec), searchSpec); } - private List SearchDaily(Series series, DateTime airDate) + private List SearchDaily(Series series, Episode episode) { - var searchSpec = Get(series); + var airDate = DateTime.ParseExact(episode.AirDate, Episode.AIR_DATE_FORMAT, CultureInfo.InvariantCulture); + var searchSpec = Get(series, new List{ episode }); searchSpec.Airtime = airDate; return Dispatch(indexer => _feedFetcher.Fetch(indexer, searchSpec), searchSpec); @@ -105,20 +106,21 @@ namespace NzbDrone.Core.IndexerSearch public List SeasonSearch(int seriesId, int seasonNumber) { var series = _seriesService.GetSeries(seriesId); + var episodes = _episodeService.GetEpisodesBySeason(seriesId, seasonNumber); - var searchSpec = Get(series, seasonNumber); + var searchSpec = Get(series, episodes); searchSpec.SeasonNumber = seasonNumber; return Dispatch(indexer => _feedFetcher.Fetch(indexer, searchSpec), searchSpec); } - private TSpec Get(Series series, int seasonNumber = -1) where TSpec : SearchCriteriaBase, new() + private TSpec Get(Series series, List episodes) where TSpec : SearchCriteriaBase, new() { var spec = new TSpec(); - spec.SeriesId = series.Id; - spec.SeriesTvRageId = series.TvRageId; + spec.Series = series; spec.SceneTitle = _sceneMapping.GetSceneName(series.TvdbId); + spec.Episodes = episodes; if (string.IsNullOrWhiteSpace(spec.SceneTitle)) { diff --git a/NzbDrone.Core/Indexers/IndexerFetchService.cs b/NzbDrone.Core/Indexers/IndexerFetchService.cs index 638e2a4af..9814adc0d 100644 --- a/NzbDrone.Core/Indexers/IndexerFetchService.cs +++ b/NzbDrone.Core/Indexers/IndexerFetchService.cs @@ -58,7 +58,7 @@ namespace NzbDrone.Core.Indexers { _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); @@ -76,7 +76,7 @@ namespace NzbDrone.Core.Indexers { _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); @@ -88,7 +88,7 @@ namespace NzbDrone.Core.Indexers { _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); _logger.Info("Finished searching {0} for {1}. Found {2}", indexer.Name, searchCriteria, result.Count); diff --git a/NzbDrone.Core/Parser/ParsingService.cs b/NzbDrone.Core/Parser/ParsingService.cs index 11b236dc1..659e3e5ca 100644 --- a/NzbDrone.Core/Parser/ParsingService.cs +++ b/NzbDrone.Core/Parser/ParsingService.cs @@ -1,7 +1,10 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Linq; using NLog; using NzbDrone.Common; +using NzbDrone.Core.DataAugmentation.Scene; +using NzbDrone.Core.IndexerSearch.Definitions; using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Tv; @@ -9,10 +12,9 @@ namespace NzbDrone.Core.Parser { public interface IParsingService { - LocalEpisode GetEpisodes(string filename, Series series); LocalEpisode GetEpisodes(string filename, Series series, bool sceneSource); Series GetSeries(string title); - RemoteEpisode Map(ParsedEpisodeInfo parsedEpisodeInfo, int tvRageId); + RemoteEpisode Map(ParsedEpisodeInfo parsedEpisodeInfo, int tvRageId, SearchCriteriaBase searchCriteria = null); } public class ParsingService : IParsingService @@ -20,24 +22,22 @@ namespace NzbDrone.Core.Parser private readonly IEpisodeService _episodeService; private readonly ISeriesService _seriesService; private readonly IDiskProvider _diskProvider; + private readonly ISceneMappingService _sceneMappingService; private readonly Logger _logger; public ParsingService(IEpisodeService episodeService, ISeriesService seriesService, IDiskProvider diskProvider, + ISceneMappingService sceneMappingService, Logger logger) { _episodeService = episodeService; _seriesService = seriesService; _diskProvider = diskProvider; + _sceneMappingService = sceneMappingService; _logger = logger; } - public LocalEpisode GetEpisodes(string filename, Series series) - { - return GetEpisodes(filename, series, false); - } - public LocalEpisode GetEpisodes(string filename, Series series, bool sceneSource) { var parsedEpisodeInfo = Parser.ParsePath(filename); @@ -68,7 +68,6 @@ namespace NzbDrone.Core.Parser public Series GetSeries(string title) { var searchTitle = title; - var parsedEpisodeInfo = Parser.ParseTitle(title); if (parsedEpisodeInfo != null) @@ -79,34 +78,73 @@ namespace NzbDrone.Core.Parser 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 { 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); 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 + series = _seriesService.FindByTvRageId(tvRageId); } if (series == null) { _logger.Trace("No matching series {0}", parsedEpisodeInfo.SeriesTitle); - return remoteEpisode; + return null; } - remoteEpisode.Series = series; - remoteEpisode.Episodes = GetEpisodes(parsedEpisodeInfo, series, true); - - return remoteEpisode; + return series; } - private List GetEpisodes(ParsedEpisodeInfo parsedEpisodeInfo, Series series, bool sceneSource) + private List GetEpisodes(ParsedEpisodeInfo parsedEpisodeInfo, Series series, bool sceneSource, SearchCriteriaBase searchCriteria = null) { var result = new List(); @@ -115,10 +153,10 @@ namespace NzbDrone.Core.Parser if (series.SeriesType == SeriesTypes.Standard) { _logger.Warn("Found daily-style episode for non-daily series: {0}.", series); - return new List(); + return null; } - - var episodeInfo = _episodeService.GetEpisode(series.Id, parsedEpisodeInfo.AirDate.Value); + + var episodeInfo = GetDailyEpisode(series, parsedEpisodeInfo.AirDate.Value, searchCriteria); if (episodeInfo != null) { @@ -137,7 +175,16 @@ namespace NzbDrone.Core.Parser 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) { @@ -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) { episodeInfo = _episodeService.GetEpisode(series.Id, parsedEpisodeInfo.SeasonNumber, episodeNumber); @@ -159,6 +212,7 @@ namespace NzbDrone.Core.Parser { result.Add(episodeInfo); } + else { _logger.Debug("Unable to find {0}", parsedEpisodeInfo); @@ -167,5 +221,23 @@ namespace NzbDrone.Core.Parser 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; + } } } \ No newline at end of file