diff --git a/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj b/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj
index 3dd0c3d08..5035ea37e 100644
--- a/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj
+++ b/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj
@@ -162,6 +162,8 @@
+
+
diff --git a/NzbDrone.Core.Test/ParserTests/ParserFixture.cs b/NzbDrone.Core.Test/ParserTests/ParserFixture.cs
index 6700e12f9..ace78f50e 100644
--- a/NzbDrone.Core.Test/ParserTests/ParserFixture.cs
+++ b/NzbDrone.Core.Test/ParserTests/ParserFixture.cs
@@ -4,11 +4,10 @@ using FluentAssertions;
using Moq;
using NUnit.Framework;
using NzbDrone.Common.Contract;
-using NzbDrone.Core.Indexers;
+using NzbDrone.Common.Expansive;
using NzbDrone.Core.Parser;
using NzbDrone.Core.Test.Framework;
using NzbDrone.Test.Common;
-using NzbDrone.Common.Expansive;
namespace NzbDrone.Core.Test.ParserTests
{
diff --git a/NzbDrone.Core.Test/ParserTests/ParsingServiceTests/GetEpisodesFixture.cs b/NzbDrone.Core.Test/ParserTests/ParsingServiceTests/GetEpisodesFixture.cs
new file mode 100644
index 000000000..b0692b6fb
--- /dev/null
+++ b/NzbDrone.Core.Test/ParserTests/ParsingServiceTests/GetEpisodesFixture.cs
@@ -0,0 +1,173 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using FizzWare.NBuilder;
+using Moq;
+using NUnit.Framework;
+using NzbDrone.Core.DataAugmentation.Scene;
+using NzbDrone.Core.IndexerSearch.Definitions;
+using NzbDrone.Core.Parser;
+using NzbDrone.Core.Parser.Model;
+using NzbDrone.Core.Tv;
+using NzbDrone.Test.Common;
+
+namespace NzbDrone.Core.Test.ParserTests.ParsingServiceTests
+{
+ [TestFixture]
+ public class GetEpisodesFixture : TestBase
+ {
+ private Series _series;
+ private List _episodes;
+ private ParsedEpisodeInfo _parsedEpisodeInfo;
+ private SingleEpisodeSearchCriteria _singleEpisodeSearchCriteria;
+
+ [SetUp]
+ public void Setup()
+ {
+ _series = Builder.CreateNew()
+ .With(s => s.Title = "30 Rock")
+ .With(s => s.CleanTitle = "rock")
+ .Build();
+
+ _episodes = Builder.CreateListOfSize(1)
+ .All()
+ .With(e => e.AirDate = DateTime.Today.ToString(Episode.AIR_DATE_FORMAT))
+ .Build()
+ .ToList();
+
+ _parsedEpisodeInfo = new ParsedEpisodeInfo
+ {
+ SeriesTitle = _series.Title,
+ SeasonNumber = 1,
+ EpisodeNumbers = new[] { 1 }
+ };
+
+ _singleEpisodeSearchCriteria = new SingleEpisodeSearchCriteria
+ {
+ Series = _series,
+ EpisodeNumber = _episodes.First().EpisodeNumber,
+ SeasonNumber = _episodes.First().SeasonNumber,
+ Episodes = _episodes
+ };
+
+ Mocker.GetMock()
+ .Setup(s => s.FindByTitle(It.IsAny()))
+ .Returns(_series);
+ }
+
+ private void GivenDailySeries()
+ {
+ _series.SeriesType = SeriesTypes.Daily;
+ }
+
+ private void GivenDailyParseResult()
+ {
+ _parsedEpisodeInfo.AirDate = DateTime.Today;
+ }
+
+ private void GivenSceneNumberingSeries()
+ {
+ _series.UseSceneNumbering = true;
+ }
+
+ [Test]
+ public void should_get_daily_episode_episode_when_search_criteria_is_null()
+ {
+ GivenDailySeries();
+ GivenDailyParseResult();
+
+ Subject.Map(_parsedEpisodeInfo, _series.TvRageId);
+
+ Mocker.GetMock()
+ .Verify(v => v.FindEpisode(It.IsAny(), It.IsAny()), Times.Once());
+ }
+
+ [Test]
+ public void should_use_search_criteria_episode_when_it_matches_daily()
+ {
+ GivenDailySeries();
+ GivenDailyParseResult();
+
+ Subject.Map(_parsedEpisodeInfo, _series.TvRageId, _singleEpisodeSearchCriteria);
+
+ Mocker.GetMock()
+ .Verify(v => v.FindEpisode(It.IsAny(), It.IsAny()), Times.Never());
+ }
+
+ [Test]
+ public void should_fallback_to_daily_episode_lookup_when_search_criteria_episode_doesnt_match()
+ {
+ GivenDailySeries();
+ _parsedEpisodeInfo.AirDate = DateTime.Today.AddDays(-5);
+
+ Subject.Map(_parsedEpisodeInfo, _series.TvRageId, _singleEpisodeSearchCriteria);
+
+ Mocker.GetMock()
+ .Verify(v => v.FindEpisode(It.IsAny(), It.IsAny()), Times.Once());
+ }
+
+ [Test]
+ public void should_use_scene_numbering_when_series_uses_scene_numbering()
+ {
+ GivenSceneNumberingSeries();
+
+ Subject.Map(_parsedEpisodeInfo, _series.TvRageId);
+
+ Mocker.GetMock()
+ .Verify(v => v.FindEpisode(It.IsAny(), It.IsAny(), It.IsAny(), true), Times.Once());
+ }
+
+ [Test]
+ public void should_match_search_criteria_by_scene_numbering()
+ {
+ GivenSceneNumberingSeries();
+
+ Subject.Map(_parsedEpisodeInfo, _series.TvRageId, _singleEpisodeSearchCriteria);
+
+ Mocker.GetMock()
+ .Verify(v => v.FindEpisode(It.IsAny(), It.IsAny(), It.IsAny(), true), Times.Never());
+ }
+
+ [Test]
+ public void should_fallback_to_findEpisode_when_search_criteria_match_fails_for_scene_numbering()
+ {
+ GivenSceneNumberingSeries();
+ _episodes.First().SceneEpisodeNumber = 10;
+
+ Subject.Map(_parsedEpisodeInfo, _series.TvRageId, _singleEpisodeSearchCriteria);
+
+ Mocker.GetMock()
+ .Verify(v => v.FindEpisode(It.IsAny(), It.IsAny(), It.IsAny(), true), Times.Once());
+ }
+
+ [Test]
+ public void should_find_episode()
+ {
+ Subject.Map(_parsedEpisodeInfo, _series.TvRageId);
+
+ Mocker.GetMock()
+ .Verify(v => v.FindEpisode(It.IsAny(), It.IsAny(), It.IsAny(), false), Times.Once());
+ }
+
+ [Test]
+ public void should_match_episode_with_search_criteria()
+ {
+ Subject.Map(_parsedEpisodeInfo, _series.TvRageId, _singleEpisodeSearchCriteria);
+
+ Mocker.GetMock()
+ .Verify(v => v.FindEpisode(It.IsAny(), It.IsAny(), It.IsAny(), false), Times.Never());
+ }
+
+ [Test]
+ public void should_fallback_to_findEpisode_when_search_criteria_match_fails()
+ {
+ _episodes.First().EpisodeNumber = 10;
+
+ Subject.Map(_parsedEpisodeInfo, _series.TvRageId, _singleEpisodeSearchCriteria);
+
+ Mocker.GetMock()
+ .Verify(v => v.FindEpisode(It.IsAny(), It.IsAny(), It.IsAny(), false), Times.Once());
+ }
+ }
+}
diff --git a/NzbDrone.Core.Test/ParserTests/ParsingServiceTests/MapFixture.cs b/NzbDrone.Core.Test/ParserTests/ParsingServiceTests/MapFixture.cs
new file mode 100644
index 000000000..efa6c5b3d
--- /dev/null
+++ b/NzbDrone.Core.Test/ParserTests/ParsingServiceTests/MapFixture.cs
@@ -0,0 +1,153 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using FizzWare.NBuilder;
+using Moq;
+using NUnit.Framework;
+using NzbDrone.Core.DataAugmentation.Scene;
+using NzbDrone.Core.IndexerSearch.Definitions;
+using NzbDrone.Core.Parser;
+using NzbDrone.Core.Parser.Model;
+using NzbDrone.Core.Tv;
+using NzbDrone.Test.Common;
+
+namespace NzbDrone.Core.Test.ParserTests.ParsingServiceTests
+{
+ [TestFixture]
+ public class MapFixture : TestBase
+ {
+ private Series _series;
+ private List _episodes;
+ private ParsedEpisodeInfo _parsedEpisodeInfo;
+ private SingleEpisodeSearchCriteria _singleEpisodeSearchCriteria;
+
+ [SetUp]
+ public void Setup()
+ {
+ _series = Builder.CreateNew()
+ .With(s => s.Title = "30 Rock")
+ .With(s => s.CleanTitle = "rock")
+ .Build();
+
+ _episodes = Builder.CreateListOfSize(1)
+ .All()
+ .With(e => e.AirDate = DateTime.Today.ToString(Episode.AIR_DATE_FORMAT))
+ .Build()
+ .ToList();
+
+ _parsedEpisodeInfo = new ParsedEpisodeInfo
+ {
+ SeriesTitle = _series.Title,
+ SeasonNumber = 1,
+ EpisodeNumbers = new[] { 1 }
+ };
+
+ _singleEpisodeSearchCriteria = new SingleEpisodeSearchCriteria
+ {
+ Series = _series,
+ EpisodeNumber = _episodes.First().EpisodeNumber,
+ SeasonNumber = _episodes.First().SeasonNumber,
+ Episodes = _episodes
+ };
+ }
+
+ private void GivenMatchBySeriesTitle()
+ {
+ Mocker.GetMock()
+ .Setup(s => s.FindByTitle(It.IsAny()))
+ .Returns(_series);
+ }
+
+ private void GivenMatchByTvRageId()
+ {
+ Mocker.GetMock()
+ .Setup(s => s.FindByTvRageId(It.IsAny()))
+ .Returns(_series);
+ }
+
+ private void GivenParseResultSeriesDoesntMatchSearchCriteria()
+ {
+ _parsedEpisodeInfo.SeriesTitle = "Another Name";
+ }
+
+ [Test]
+ public void should_lookup_series_by_name()
+ {
+ GivenMatchBySeriesTitle();
+
+ Subject.Map(_parsedEpisodeInfo, _series.TvRageId);
+
+ Mocker.GetMock()
+ .Verify(v => v.FindByTitle(It.IsAny()), Times.Once());
+ }
+
+ [Test]
+ public void should_use_tvrageid_when_series_title_lookup_fails()
+ {
+ GivenMatchByTvRageId();
+
+ Subject.Map(_parsedEpisodeInfo, _series.TvRageId);
+
+ Mocker.GetMock()
+ .Verify(v => v.FindByTvRageId(It.IsAny()), Times.Once());
+ }
+
+ [Test]
+ public void should_use_search_criteria_series_title()
+ {
+ GivenMatchBySeriesTitle();
+
+ Subject.Map(_parsedEpisodeInfo, _series.TvRageId, _singleEpisodeSearchCriteria);
+
+ Mocker.GetMock()
+ .Verify(v => v.FindByTitle(It.IsAny()), Times.Never());
+ }
+
+ [Test]
+ public void should_FindByTitle_when_search_criteria_matching_fails()
+ {
+ GivenParseResultSeriesDoesntMatchSearchCriteria();
+
+ Subject.Map(_parsedEpisodeInfo, 10, _singleEpisodeSearchCriteria);
+
+ Mocker.GetMock()
+ .Verify(v => v.FindByTitle(It.IsAny()), Times.Once());
+ }
+
+ [Test]
+ public void should_FindByTvRageId_when_search_criteria_and_FIndByTitle_matching_fails()
+ {
+ GivenParseResultSeriesDoesntMatchSearchCriteria();
+
+ Subject.Map(_parsedEpisodeInfo, 10, _singleEpisodeSearchCriteria);
+
+ Mocker.GetMock()
+ .Verify(v => v.FindByTvRageId(It.IsAny()), Times.Once());
+ }
+
+ [Test]
+ public void should_use_tvdbid_matching_when_alias_is_found()
+ {
+ Mocker.GetMock()
+ .Setup(s => s.GetTvDbId(It.IsAny()))
+ .Returns(_series.TvdbId);
+
+ Subject.Map(_parsedEpisodeInfo, _series.TvRageId, _singleEpisodeSearchCriteria);
+
+ Mocker.GetMock()
+ .Verify(v => v.FindByTitle(It.IsAny()), Times.Never());
+ }
+
+ [Test]
+ public void should_use_tvrageid_match_from_search_criteria_when_title_match_fails()
+ {
+ GivenParseResultSeriesDoesntMatchSearchCriteria();
+
+ Subject.Map(_parsedEpisodeInfo, _series.TvRageId, _singleEpisodeSearchCriteria);
+
+ Mocker.GetMock()
+ .Verify(v => v.FindByTitle(It.IsAny()), Times.Never());
+ }
+ }
+}
diff --git a/NzbDrone.Core/Parser/ParsingService.cs b/NzbDrone.Core/Parser/ParsingService.cs
index 659e3e5ca..b678c5eeb 100644
--- a/NzbDrone.Core/Parser/ParsingService.cs
+++ b/NzbDrone.Core/Parser/ParsingService.cs
@@ -15,6 +15,7 @@ namespace NzbDrone.Core.Parser
LocalEpisode GetEpisodes(string filename, Series series, bool sceneSource);
Series GetSeries(string title);
RemoteEpisode Map(ParsedEpisodeInfo parsedEpisodeInfo, int tvRageId, SearchCriteriaBase searchCriteria = null);
+ List GetEpisodes(ParsedEpisodeInfo parsedEpisodeInfo, Series series, bool sceneSource, SearchCriteriaBase searchCriteria = null);
}
public class ParsingService : IParsingService
@@ -99,52 +100,7 @@ namespace NzbDrone.Core.Parser
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)
- {
- //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 null;
- }
-
- return series;
- }
-
- private List GetEpisodes(ParsedEpisodeInfo parsedEpisodeInfo, Series series, bool sceneSource, SearchCriteriaBase searchCriteria = null)
+ public List GetEpisodes(ParsedEpisodeInfo parsedEpisodeInfo, Series series, bool sceneSource, SearchCriteriaBase searchCriteria = null)
{
var result = new List();
@@ -205,7 +161,7 @@ namespace NzbDrone.Core.Parser
if (episodeInfo == null)
{
- episodeInfo = _episodeService.GetEpisode(series.Id, parsedEpisodeInfo.SeasonNumber, episodeNumber);
+ episodeInfo = _episodeService.FindEpisode(series.Id, parsedEpisodeInfo.SeasonNumber, episodeNumber);
}
if (episodeInfo != null)
@@ -222,6 +178,51 @@ namespace NzbDrone.Core.Parser
return result;
}
+ 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)
+ {
+ //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 null;
+ }
+
+ return series;
+ }
+
private Episode GetDailyEpisode(Series series, DateTime airDate, SearchCriteriaBase searchCriteria)
{
Episode episodeInfo = null;
@@ -234,7 +235,7 @@ namespace NzbDrone.Core.Parser
if (episodeInfo == null)
{
- episodeInfo = _episodeService.GetEpisode(series.Id, airDate);
+ episodeInfo = _episodeService.FindEpisode(series.Id, airDate);
}
return episodeInfo;