Properly handling multi episode in one scene numbered release

Fixed: Multiple episodes under one scene episode for some shows
This commit is contained in:
Mark McDowall 2014-02-26 22:51:41 -08:00
parent 434ad5f340
commit 669f351d08
6 changed files with 75 additions and 53 deletions

View File

@ -130,7 +130,7 @@ namespace NzbDrone.Core.Test.ParserTests.ParsingServiceTests
Subject.Map(_parsedEpisodeInfo, _series.TvRageId); Subject.Map(_parsedEpisodeInfo, _series.TvRageId);
Mocker.GetMock<IEpisodeService>() Mocker.GetMock<IEpisodeService>()
.Verify(v => v.FindEpisode(It.IsAny<Int32>(), It.IsAny<Int32>(), It.IsAny<Int32>(), true), Times.Once()); .Verify(v => v.FindEpisodesBySceneNumbering(It.IsAny<Int32>(), It.IsAny<Int32>(), It.IsAny<Int32>()), Times.Once());
} }
[Test] [Test]
@ -141,7 +141,7 @@ namespace NzbDrone.Core.Test.ParserTests.ParsingServiceTests
Subject.Map(_parsedEpisodeInfo, _series.TvRageId, _singleEpisodeSearchCriteria); Subject.Map(_parsedEpisodeInfo, _series.TvRageId, _singleEpisodeSearchCriteria);
Mocker.GetMock<IEpisodeService>() Mocker.GetMock<IEpisodeService>()
.Verify(v => v.FindEpisode(It.IsAny<Int32>(), It.IsAny<Int32>(), It.IsAny<Int32>(), true), Times.Never()); .Verify(v => v.FindEpisodesBySceneNumbering(It.IsAny<Int32>(), It.IsAny<Int32>(), It.IsAny<Int32>()), Times.Never());
} }
[Test] [Test]
@ -153,7 +153,7 @@ namespace NzbDrone.Core.Test.ParserTests.ParsingServiceTests
Subject.Map(_parsedEpisodeInfo, _series.TvRageId, _singleEpisodeSearchCriteria); Subject.Map(_parsedEpisodeInfo, _series.TvRageId, _singleEpisodeSearchCriteria);
Mocker.GetMock<IEpisodeService>() Mocker.GetMock<IEpisodeService>()
.Verify(v => v.FindEpisode(It.IsAny<Int32>(), It.IsAny<Int32>(), It.IsAny<Int32>(), true), Times.Once()); .Verify(v => v.FindEpisodesBySceneNumbering(It.IsAny<Int32>(), It.IsAny<Int32>(), It.IsAny<Int32>()), Times.Once());
} }
[Test] [Test]
@ -162,7 +162,7 @@ namespace NzbDrone.Core.Test.ParserTests.ParsingServiceTests
Subject.Map(_parsedEpisodeInfo, _series.TvRageId); Subject.Map(_parsedEpisodeInfo, _series.TvRageId);
Mocker.GetMock<IEpisodeService>() Mocker.GetMock<IEpisodeService>()
.Verify(v => v.FindEpisode(It.IsAny<Int32>(), It.IsAny<Int32>(), It.IsAny<Int32>(), false), Times.Once()); .Verify(v => v.FindEpisode(It.IsAny<Int32>(), It.IsAny<Int32>(), It.IsAny<Int32>()), Times.Once());
} }
[Test] [Test]
@ -171,7 +171,7 @@ namespace NzbDrone.Core.Test.ParserTests.ParsingServiceTests
Subject.Map(_parsedEpisodeInfo, _series.TvRageId, _singleEpisodeSearchCriteria); Subject.Map(_parsedEpisodeInfo, _series.TvRageId, _singleEpisodeSearchCriteria);
Mocker.GetMock<IEpisodeService>() Mocker.GetMock<IEpisodeService>()
.Verify(v => v.FindEpisode(It.IsAny<Int32>(), It.IsAny<Int32>(), It.IsAny<Int32>(), false), Times.Never()); .Verify(v => v.FindEpisode(It.IsAny<Int32>(), It.IsAny<Int32>(), It.IsAny<Int32>()), Times.Never());
} }
[Test] [Test]
@ -182,7 +182,7 @@ namespace NzbDrone.Core.Test.ParserTests.ParsingServiceTests
Subject.Map(_parsedEpisodeInfo, _series.TvRageId, _singleEpisodeSearchCriteria); Subject.Map(_parsedEpisodeInfo, _series.TvRageId, _singleEpisodeSearchCriteria);
Mocker.GetMock<IEpisodeService>() Mocker.GetMock<IEpisodeService>()
.Verify(v => v.FindEpisode(It.IsAny<Int32>(), It.IsAny<Int32>(), It.IsAny<Int32>(), false), Times.Once()); .Verify(v => v.FindEpisode(It.IsAny<Int32>(), It.IsAny<Int32>(), It.IsAny<Int32>()), Times.Once());
} }
} }
} }

View File

@ -1,4 +1,5 @@
using FizzWare.NBuilder; using System.Linq;
using FizzWare.NBuilder;
using FluentAssertions; using FluentAssertions;
using NUnit.Framework; using NUnit.Framework;
using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Test.Framework;
@ -9,46 +10,55 @@ namespace NzbDrone.Core.Test.TvTests.EpisodeRepositoryTests
[TestFixture] [TestFixture]
public class FindEpisodeFixture : DbTest<EpisodeRepository, Episode> public class FindEpisodeFixture : DbTest<EpisodeRepository, Episode>
{ {
private Episode _episode; private Episode _episode1;
private Episode _episode2;
[SetUp] [SetUp]
public void Setup() public void Setup()
{ {
_episode = Builder<Episode>.CreateNew() _episode1 = Builder<Episode>.CreateNew()
.With(e => e.Id = 0) .With(e => e.SeriesId = 1)
.With(e => e.SeriesId = 1) .With(e => e.SeasonNumber = 1)
.With(e => e.SeasonNumber = 1) .With(e => e.SceneSeasonNumber = 2)
.With(e => e.SceneSeasonNumber = 2) .With(e => e.EpisodeNumber = 3)
.With(e => e.EpisodeNumber = 3) .With(e => e.AbsoluteEpisodeNumber = 3)
.With(e => e.AbsoluteEpisodeNumber = 3) .With(e => e.SceneEpisodeNumber = 4)
.With(e => e.SceneEpisodeNumber = 4) .BuildNew();
.Build();
_episode = Db.Insert(_episode); _episode2 = Builder<Episode>.CreateNew()
.With(e => e.SeriesId = 1)
.With(e => e.SeasonNumber = 1)
.With(e => e.SceneSeasonNumber = 2)
.With(e => e.EpisodeNumber = 4)
.With(e => e.SceneEpisodeNumber = 4)
.BuildNew();
_episode1 = Db.Insert(_episode1);
} }
[Test] [Test]
public void should_find_episode_by_scene_numbering() public void should_find_episode_by_scene_numbering()
{ {
Subject.FindEpisodeBySceneNumbering(_episode.SeriesId, _episode.SceneSeasonNumber, _episode.SceneEpisodeNumber) Subject.FindEpisodesBySceneNumbering(_episode1.SeriesId, _episode1.SceneSeasonNumber, _episode1.SceneEpisodeNumber)
.First()
.Id .Id
.Should() .Should()
.Be(_episode.Id); .Be(_episode1.Id);
} }
[Test] [Test]
public void should_find_episode_by_standard_numbering() public void should_find_episode_by_standard_numbering()
{ {
Subject.Find(_episode.SeriesId, _episode.SeasonNumber, _episode.EpisodeNumber) Subject.Find(_episode1.SeriesId, _episode1.SeasonNumber, _episode1.EpisodeNumber)
.Id .Id
.Should() .Should()
.Be(_episode.Id); .Be(_episode1.Id);
} }
[Test] [Test]
public void should_not_find_episode_that_does_not_exist() public void should_not_find_episode_that_does_not_exist()
{ {
Subject.Find(_episode.SeriesId, _episode.SeasonNumber + 1, _episode.EpisodeNumber) Subject.Find(_episode1.SeriesId, _episode1.SeasonNumber + 1, _episode1.EpisodeNumber)
.Should() .Should()
.BeNull(); .BeNull();
} }
@ -56,10 +66,20 @@ namespace NzbDrone.Core.Test.TvTests.EpisodeRepositoryTests
[Test] [Test]
public void should_find_episode_by_absolute_numbering() public void should_find_episode_by_absolute_numbering()
{ {
Subject.Find(_episode.SeriesId, _episode.AbsoluteEpisodeNumber.Value) Subject.Find(_episode1.SeriesId, _episode1.AbsoluteEpisodeNumber.Value)
.Id .Id
.Should() .Should()
.Be(_episode.Id); .Be(_episode1.Id);
}
[Test]
public void should_return_multiple_episode_if_multiple_match_by_scene_numbering()
{
_episode2 = Db.Insert(_episode2);
Subject.FindEpisodesBySceneNumbering(_episode1.SeriesId, _episode1.SceneSeasonNumber, _episode1.SceneEpisodeNumber)
.Should()
.HaveCount(2);
} }
} }
} }

View File

@ -174,33 +174,37 @@ namespace NzbDrone.Core.Parser
foreach (var episodeNumber in parsedEpisodeInfo.EpisodeNumbers) foreach (var episodeNumber in parsedEpisodeInfo.EpisodeNumbers)
{ {
Episode episodeInfo = null;
if (series.UseSceneNumbering && sceneSource) if (series.UseSceneNumbering && sceneSource)
{ {
List<Episode> episodes = new List<Episode>();
if (searchCriteria != null) if (searchCriteria != null)
{ {
episodeInfo = searchCriteria.Episodes.SingleOrDefault(e => e.SceneSeasonNumber == parsedEpisodeInfo.SeasonNumber && episodes = searchCriteria.Episodes.Where(e => e.SceneSeasonNumber == parsedEpisodeInfo.SeasonNumber &&
e.SceneEpisodeNumber == episodeNumber); e.SceneEpisodeNumber == episodeNumber).ToList();
} }
if (episodeInfo == null) if (!episodes.Any())
{ {
episodeInfo = _episodeService.FindEpisode(series.Id, parsedEpisodeInfo.SeasonNumber, episodeNumber, true); episodes = _episodeService.FindEpisodesBySceneNumbering(series.Id, parsedEpisodeInfo.SeasonNumber, episodeNumber);
} }
if (episodeInfo != null) if (episodes != null && episodes.Any())
{ {
_logger.Info("Using Scene to TVDB Mapping for: {0} - Scene: {1}x{2:00} - TVDB: {3}x{4:00}", _logger.Info("Using Scene to TVDB Mapping for: {0} - Scene: {1}x{2:00} - TVDB: {3}",
series.Title, series.Title,
episodeInfo.SceneSeasonNumber, episodes.First().SceneSeasonNumber,
episodeInfo.SceneEpisodeNumber, episodes.First().SceneEpisodeNumber,
episodeInfo.SeasonNumber, String.Join(", ", episodes.Select(e => String.Format("{0}x{1:00}", e.SeasonNumber, e.EpisodeNumber))));
episodeInfo.EpisodeNumber);
result.AddRange(episodes);
continue;
} }
} }
if (episodeInfo == null && searchCriteria != null) Episode episodeInfo = null;
if (searchCriteria != null)
{ {
episodeInfo = searchCriteria.Episodes.SingleOrDefault(e => e.SeasonNumber == parsedEpisodeInfo.SeasonNumber && episodeInfo = searchCriteria.Episodes.SingleOrDefault(e => e.SeasonNumber == parsedEpisodeInfo.SeasonNumber &&
e.EpisodeNumber == episodeNumber); e.EpisodeNumber == episodeNumber);

View File

@ -53,7 +53,7 @@ namespace NzbDrone.Core.Queue
foreach (var episode in queueItem.RemoteEpisode.Episodes) foreach (var episode in queueItem.RemoteEpisode.Episodes)
{ {
var queue = new Queue(); var queue = new Queue();
queue.Id = queueItem.Id.GetHashCode(); queue.Id = queueItem.Id.GetHashCode() + episode.Id;
queue.Series = queueItem.RemoteEpisode.Series; queue.Series = queueItem.RemoteEpisode.Series;
queue.Episode = episode; queue.Episode = episode;
queue.Quality = queueItem.RemoteEpisode.ParsedEpisodeInfo.Quality; queue.Quality = queueItem.RemoteEpisode.ParsedEpisodeInfo.Quality;

View File

@ -21,7 +21,7 @@ namespace NzbDrone.Core.Tv
List<Episode> GetEpisodeByFileId(int fileId); List<Episode> GetEpisodeByFileId(int fileId);
PagingSpec<Episode> EpisodesWithoutFiles(PagingSpec<Episode> pagingSpec, bool includeSpecials); PagingSpec<Episode> EpisodesWithoutFiles(PagingSpec<Episode> pagingSpec, bool includeSpecials);
PagingSpec<Episode> EpisodesWhereCutoffUnmet(PagingSpec<Episode> pagingSpec, List<QualitiesBelowCutoff> qualitiesBelowCutoff, bool includeSpecials); PagingSpec<Episode> EpisodesWhereCutoffUnmet(PagingSpec<Episode> pagingSpec, List<QualitiesBelowCutoff> qualitiesBelowCutoff, bool includeSpecials);
Episode FindEpisodeBySceneNumbering(int seriesId, int seasonNumber, int episodeNumber); List<Episode> FindEpisodesBySceneNumbering(int seriesId, int seasonNumber, int episodeNumber);
List<Episode> EpisodesBetweenDates(DateTime startDate, DateTime endDate); List<Episode> EpisodesBetweenDates(DateTime startDate, DateTime endDate);
void SetMonitoredFlat(Episode episode, bool monitored); void SetMonitoredFlat(Episode episode, bool monitored);
void SetMonitoredBySeason(int seriesId, int seasonNumber, bool monitored); void SetMonitoredBySeason(int seriesId, int seasonNumber, bool monitored);
@ -116,12 +116,11 @@ namespace NzbDrone.Core.Tv
return pagingSpec; return pagingSpec;
} }
public Episode FindEpisodeBySceneNumbering(int seriesId, int seasonNumber, int episodeNumber) public List<Episode> FindEpisodesBySceneNumbering(int seriesId, int seasonNumber, int episodeNumber)
{ {
return Query.Where(s => s.SeriesId == seriesId) return Query.Where(s => s.SeriesId == seriesId)
.AndWhere(s => s.SceneSeasonNumber == seasonNumber) .AndWhere(s => s.SceneSeasonNumber == seasonNumber)
.AndWhere(s => s.SceneEpisodeNumber == episodeNumber) .AndWhere(s => s.SceneEpisodeNumber == episodeNumber);
.SingleOrDefault();
} }
public List<Episode> EpisodesBetweenDates(DateTime startDate, DateTime endDate) public List<Episode> EpisodesBetweenDates(DateTime startDate, DateTime endDate)

View File

@ -14,9 +14,10 @@ namespace NzbDrone.Core.Tv
public interface IEpisodeService public interface IEpisodeService
{ {
Episode GetEpisode(int id); Episode GetEpisode(int id);
Episode FindEpisode(int seriesId, int seasonNumber, int episodeNumber, bool useScene = false); Episode FindEpisode(int seriesId, int seasonNumber, int episodeNumber);
Episode FindEpisode(int seriesId, int absoluteEpisodeNumber); Episode FindEpisode(int seriesId, int absoluteEpisodeNumber);
Episode FindEpisodeByName(int seriesId, int seasonNumber, string episodeTitle); Episode FindEpisodeByName(int seriesId, int seasonNumber, string episodeTitle);
List<Episode> FindEpisodesBySceneNumbering(int seriesId, int seasonNumber, int episodeNumber);
Episode GetEpisode(int seriesId, String date); Episode GetEpisode(int seriesId, String date);
Episode FindEpisode(int seriesId, String date); Episode FindEpisode(int seriesId, String date);
List<Episode> GetEpisodeBySeries(int seriesId); List<Episode> GetEpisodeBySeries(int seriesId);
@ -39,16 +40,13 @@ namespace NzbDrone.Core.Tv
IHandle<EpisodeFileAddedEvent>, IHandle<EpisodeFileAddedEvent>,
IHandleAsync<SeriesDeletedEvent> IHandleAsync<SeriesDeletedEvent>
{ {
private readonly IEpisodeRepository _episodeRepository; private readonly IEpisodeRepository _episodeRepository;
private readonly IQualityProfileRepository _qualityProfileRepository;
private readonly IConfigService _configService; private readonly IConfigService _configService;
private readonly Logger _logger; private readonly Logger _logger;
public EpisodeService(IEpisodeRepository episodeRepository, IQualityProfileRepository qualityProfileRepository, IConfigService configService, Logger logger) public EpisodeService(IEpisodeRepository episodeRepository, IConfigService configService, Logger logger)
{ {
_episodeRepository = episodeRepository; _episodeRepository = episodeRepository;
_qualityProfileRepository = qualityProfileRepository;
_configService = configService; _configService = configService;
_logger = logger; _logger = logger;
} }
@ -58,12 +56,8 @@ namespace NzbDrone.Core.Tv
return _episodeRepository.Get(id); return _episodeRepository.Get(id);
} }
public Episode FindEpisode(int seriesId, int seasonNumber, int episodeNumber, bool useSceneNumbering = false) public Episode FindEpisode(int seriesId, int seasonNumber, int episodeNumber)
{ {
if (useSceneNumbering)
{
return _episodeRepository.FindEpisodeBySceneNumbering(seriesId, seasonNumber, episodeNumber);
}
return _episodeRepository.Find(seriesId, seasonNumber, episodeNumber); return _episodeRepository.Find(seriesId, seasonNumber, episodeNumber);
} }
@ -72,6 +66,11 @@ namespace NzbDrone.Core.Tv
return _episodeRepository.Find(seriesId, absoluteEpisodeNumber); return _episodeRepository.Find(seriesId, absoluteEpisodeNumber);
} }
public List<Episode> FindEpisodesBySceneNumbering(int seriesId, int seasonNumber, int episodeNumber)
{
return _episodeRepository.FindEpisodesBySceneNumbering(seriesId, seasonNumber, episodeNumber);
}
public Episode GetEpisode(int seriesId, String date) public Episode GetEpisode(int seriesId, String date)
{ {
return _episodeRepository.Get(seriesId, date); return _episodeRepository.Get(seriesId, date);