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);
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]
@ -141,7 +141,7 @@ namespace NzbDrone.Core.Test.ParserTests.ParsingServiceTests
Subject.Map(_parsedEpisodeInfo, _series.TvRageId, _singleEpisodeSearchCriteria);
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]
@ -153,7 +153,7 @@ namespace NzbDrone.Core.Test.ParserTests.ParsingServiceTests
Subject.Map(_parsedEpisodeInfo, _series.TvRageId, _singleEpisodeSearchCriteria);
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]
@ -162,7 +162,7 @@ namespace NzbDrone.Core.Test.ParserTests.ParsingServiceTests
Subject.Map(_parsedEpisodeInfo, _series.TvRageId);
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]
@ -171,7 +171,7 @@ namespace NzbDrone.Core.Test.ParserTests.ParsingServiceTests
Subject.Map(_parsedEpisodeInfo, _series.TvRageId, _singleEpisodeSearchCriteria);
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]
@ -182,7 +182,7 @@ namespace NzbDrone.Core.Test.ParserTests.ParsingServiceTests
Subject.Map(_parsedEpisodeInfo, _series.TvRageId, _singleEpisodeSearchCriteria);
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 NUnit.Framework;
using NzbDrone.Core.Test.Framework;
@ -9,46 +10,55 @@ namespace NzbDrone.Core.Test.TvTests.EpisodeRepositoryTests
[TestFixture]
public class FindEpisodeFixture : DbTest<EpisodeRepository, Episode>
{
private Episode _episode;
private Episode _episode1;
private Episode _episode2;
[SetUp]
public void Setup()
{
_episode = Builder<Episode>.CreateNew()
.With(e => e.Id = 0)
.With(e => e.SeriesId = 1)
.With(e => e.SeasonNumber = 1)
.With(e => e.SceneSeasonNumber = 2)
.With(e => e.EpisodeNumber = 3)
.With(e => e.AbsoluteEpisodeNumber = 3)
.With(e => e.SceneEpisodeNumber = 4)
.Build();
_episode1 = Builder<Episode>.CreateNew()
.With(e => e.SeriesId = 1)
.With(e => e.SeasonNumber = 1)
.With(e => e.SceneSeasonNumber = 2)
.With(e => e.EpisodeNumber = 3)
.With(e => e.AbsoluteEpisodeNumber = 3)
.With(e => e.SceneEpisodeNumber = 4)
.BuildNew();
_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]
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
.Should()
.Be(_episode.Id);
.Be(_episode1.Id);
}
[Test]
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
.Should()
.Be(_episode.Id);
.Be(_episode1.Id);
}
[Test]
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()
.BeNull();
}
@ -56,10 +66,20 @@ namespace NzbDrone.Core.Test.TvTests.EpisodeRepositoryTests
[Test]
public void should_find_episode_by_absolute_numbering()
{
Subject.Find(_episode.SeriesId, _episode.AbsoluteEpisodeNumber.Value)
Subject.Find(_episode1.SeriesId, _episode1.AbsoluteEpisodeNumber.Value)
.Id
.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)
{
Episode episodeInfo = null;
if (series.UseSceneNumbering && sceneSource)
{
List<Episode> episodes = new List<Episode>();
if (searchCriteria != null)
{
episodeInfo = searchCriteria.Episodes.SingleOrDefault(e => e.SceneSeasonNumber == parsedEpisodeInfo.SeasonNumber &&
e.SceneEpisodeNumber == episodeNumber);
episodes = searchCriteria.Episodes.Where(e => e.SceneSeasonNumber == parsedEpisodeInfo.SeasonNumber &&
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,
episodeInfo.SceneSeasonNumber,
episodeInfo.SceneEpisodeNumber,
episodeInfo.SeasonNumber,
episodeInfo.EpisodeNumber);
episodes.First().SceneSeasonNumber,
episodes.First().SceneEpisodeNumber,
String.Join(", ", episodes.Select(e => String.Format("{0}x{1:00}", e.SeasonNumber, e.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 &&
e.EpisodeNumber == episodeNumber);

View File

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

View File

@ -21,7 +21,7 @@ namespace NzbDrone.Core.Tv
List<Episode> GetEpisodeByFileId(int fileId);
PagingSpec<Episode> EpisodesWithoutFiles(PagingSpec<Episode> pagingSpec, 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);
void SetMonitoredFlat(Episode episode, bool monitored);
void SetMonitoredBySeason(int seriesId, int seasonNumber, bool monitored);
@ -116,12 +116,11 @@ namespace NzbDrone.Core.Tv
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)
.AndWhere(s => s.SceneSeasonNumber == seasonNumber)
.AndWhere(s => s.SceneEpisodeNumber == episodeNumber)
.SingleOrDefault();
.AndWhere(s => s.SceneEpisodeNumber == episodeNumber);
}
public List<Episode> EpisodesBetweenDates(DateTime startDate, DateTime endDate)

View File

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