From f2c8156c00df8d7da2336a2be6741b2eed869d70 Mon Sep 17 00:00:00 2001 From: Mark McDowall Date: Sun, 27 Mar 2016 14:17:56 -0700 Subject: [PATCH] ParsingService.GetEpisodes will use TVDB season number when available --- .../ParsingServiceTests/GetEpisodesFixture.cs | 68 +++++ .../Scene/SceneMappingService.cs | 44 ++- src/NzbDrone.Core/Parser/ParsingService.cs | 261 ++++++++++-------- 3 files changed, 252 insertions(+), 121 deletions(-) diff --git a/src/NzbDrone.Core.Test/ParserTests/ParsingServiceTests/GetEpisodesFixture.cs b/src/NzbDrone.Core.Test/ParserTests/ParsingServiceTests/GetEpisodesFixture.cs index f1f5b4516..7221038e7 100644 --- a/src/NzbDrone.Core.Test/ParserTests/ParsingServiceTests/GetEpisodesFixture.cs +++ b/src/NzbDrone.Core.Test/ParserTests/ParsingServiceTests/GetEpisodesFixture.cs @@ -273,5 +273,73 @@ namespace NzbDrone.Core.Test.ParserTests.ParsingServiceTests Mocker.GetMock() .Verify(v => v.FindEpisode(It.IsAny(), seasonNumber, It.IsAny()), Times.Once()); } + + [Test] + public void should_use_tvdb_season_number_when_available_and_a_scene_source() + { + const int tvdbSeasonNumber = 5; + + Mocker.GetMock() + .Setup(s => s.FindSceneMapping(_parsedEpisodeInfo.SeriesTitle)) + .Returns(new SceneMapping { SeasonNumber = tvdbSeasonNumber, SceneSeasonNumber = _parsedEpisodeInfo.SeasonNumber }); + + Subject.GetEpisodes(_parsedEpisodeInfo, _series, true, null); + + Mocker.GetMock() + .Verify(v => v.FindEpisode(_series.Id, _parsedEpisodeInfo.SeasonNumber, _parsedEpisodeInfo.EpisodeNumbers.First()), Times.Never()); + + Mocker.GetMock() + .Verify(v => v.FindEpisode(_series.Id, tvdbSeasonNumber, _parsedEpisodeInfo.EpisodeNumbers.First()), Times.Once()); + } + + [Test] + public void should_not_use_tvdb_season_number_when_available_for_a_different_season_and_a_scene_source() + { + const int tvdbSeasonNumber = 5; + + Mocker.GetMock() + .Setup(s => s.FindSceneMapping(_parsedEpisodeInfo.SeriesTitle)) + .Returns(new SceneMapping { SeasonNumber = tvdbSeasonNumber, SceneSeasonNumber = _parsedEpisodeInfo.SeasonNumber + 100 }); + + Subject.GetEpisodes(_parsedEpisodeInfo, _series, true, null); + + Mocker.GetMock() + .Verify(v => v.FindEpisode(_series.Id, tvdbSeasonNumber, _parsedEpisodeInfo.EpisodeNumbers.First()), Times.Never()); + + Mocker.GetMock() + .Verify(v => v.FindEpisode(_series.Id, _parsedEpisodeInfo.SeasonNumber, _parsedEpisodeInfo.EpisodeNumbers.First()), Times.Once()); + } + + [Test] + public void should_not_use_tvdb_season_when_not_a_scene_source() + { + const int tvdbSeasonNumber = 5; + + Subject.GetEpisodes(_parsedEpisodeInfo, _series, false, null); + + Mocker.GetMock() + .Verify(v => v.FindEpisode(_series.Id, tvdbSeasonNumber, _parsedEpisodeInfo.EpisodeNumbers.First()), Times.Never()); + + Mocker.GetMock() + .Verify(v => v.FindEpisode(_series.Id, _parsedEpisodeInfo.SeasonNumber, _parsedEpisodeInfo.EpisodeNumbers.First()), Times.Once()); + } + + [Test] + public void should_not_use_tvdb_season_when_tvdb_season_number_is_less_than_zero() + { + const int tvdbSeasonNumber = -1; + + Mocker.GetMock() + .Setup(s => s.FindSceneMapping(_parsedEpisodeInfo.SeriesTitle)) + .Returns(new SceneMapping { SeasonNumber = tvdbSeasonNumber, SceneSeasonNumber = _parsedEpisodeInfo.SeasonNumber }); + + Subject.GetEpisodes(_parsedEpisodeInfo, _series, true, null); + + Mocker.GetMock() + .Verify(v => v.FindEpisode(_series.Id, tvdbSeasonNumber, _parsedEpisodeInfo.EpisodeNumbers.First()), Times.Never()); + + Mocker.GetMock() + .Verify(v => v.FindEpisode(_series.Id, _parsedEpisodeInfo.SeasonNumber, _parsedEpisodeInfo.EpisodeNumbers.First()), Times.Once()); + } } } diff --git a/src/NzbDrone.Core/DataAugmentation/Scene/SceneMappingService.cs b/src/NzbDrone.Core/DataAugmentation/Scene/SceneMappingService.cs index ea7cb58c7..e914490a7 100644 --- a/src/NzbDrone.Core/DataAugmentation/Scene/SceneMappingService.cs +++ b/src/NzbDrone.Core/DataAugmentation/Scene/SceneMappingService.cs @@ -3,7 +3,6 @@ using System.Linq; using NLog; using NzbDrone.Common.Cache; using NzbDrone.Common.Extensions; -using NzbDrone.Core.Lifecycle; using NzbDrone.Core.Messaging.Commands; using NzbDrone.Core.Messaging.Events; using NzbDrone.Core.Parser; @@ -18,7 +17,10 @@ namespace NzbDrone.Core.DataAugmentation.Scene List GetSceneNamesBySceneSeasonNumbers(int tvdbId, IEnumerable sceneSeasonNumbers); int? FindTvdbId(string title); List FindByTvdbId(int tvdbId); + SceneMapping FindSceneMapping(string title); int? GetSceneSeasonNumber(string title); + int? GetTvdbSeasonNumber(string title); + int? GetSceneSeasonNumber(int tvdbId, int seasonNumber); } public class SceneMappingService : ISceneMappingService, @@ -49,7 +51,7 @@ namespace NzbDrone.Core.DataAugmentation.Scene public List GetSceneNamesBySeasonNumbers(int tvdbId, IEnumerable seasonNumbers) { - var names = _findByTvdbIdCache.Find(tvdbId.ToString()); + var names = FindByTvdbId(tvdbId); if (names == null) { @@ -63,7 +65,7 @@ namespace NzbDrone.Core.DataAugmentation.Scene public List GetSceneNamesBySceneSeasonNumbers(int tvdbId, IEnumerable sceneSeasonNumbers) { - var names = _findByTvdbIdCache.Find(tvdbId.ToString()); + var names = FindByTvdbId(tvdbId); if (names == null) { @@ -102,6 +104,11 @@ namespace NzbDrone.Core.DataAugmentation.Scene return mappings; } + public SceneMapping FindSceneMapping(string title) + { + return FindMapping(title); + } + public int? GetSceneSeasonNumber(string title) { var mapping = FindMapping(title); @@ -114,6 +121,37 @@ namespace NzbDrone.Core.DataAugmentation.Scene return mapping.SceneSeasonNumber; } + public int? GetTvdbSeasonNumber(string title) + { + var mapping = FindMapping(title); + + if (mapping == null) + { + return null; + } + + return mapping.SeasonNumber; + } + + public int? GetSceneSeasonNumber(int tvdbId, int seasonNumber) + { + var mappings = FindByTvdbId(tvdbId); + + if (mappings == null) + { + return null; + } + + var mapping = mappings.FirstOrDefault(e => e.SeasonNumber == seasonNumber && e.SceneSeasonNumber.HasValue); + + if (mapping == null) + { + return null; + } + + return mapping.SceneSeasonNumber; + } + private void UpdateMappings() { _logger.Info("Updating Scene mappings"); diff --git a/src/NzbDrone.Core/Parser/ParsingService.cs b/src/NzbDrone.Core/Parser/ParsingService.cs index b1418a96f..5b2cbfcd1 100644 --- a/src/NzbDrone.Core/Parser/ParsingService.cs +++ b/src/NzbDrone.Core/Parser/ParsingService.cs @@ -141,8 +141,6 @@ namespace NzbDrone.Core.Parser public List GetEpisodes(ParsedEpisodeInfo parsedEpisodeInfo, Series series, bool sceneSource, SearchCriteriaBase searchCriteria = null) { - var result = new List(); - if (parsedEpisodeInfo.FullSeason) { return _episodeService.GetEpisodesBySeason(series.Id, parsedEpisodeInfo.SeasonNumber); @@ -153,136 +151,25 @@ namespace NzbDrone.Core.Parser if (series.SeriesType == SeriesTypes.Standard) { _logger.Warn("Found daily-style episode for non-daily series: {0}.", series); - return result; + return new List(); } var episodeInfo = GetDailyEpisode(series, parsedEpisodeInfo.AirDate, searchCriteria); if (episodeInfo != null) { - result.Add(episodeInfo); + return new List { episodeInfo }; } - return result; + return new List(); } if (parsedEpisodeInfo.IsAbsoluteNumbering) { - var sceneSeasonNumber = _sceneMappingService.GetSceneSeasonNumber(parsedEpisodeInfo.SeriesTitle); - - foreach (var absoluteEpisodeNumber in parsedEpisodeInfo.AbsoluteEpisodeNumbers) - { - Episode episode = null; - - if (parsedEpisodeInfo.Special) - { - episode = _episodeService.FindEpisode(series.Id, 0, absoluteEpisodeNumber); - } - - else if (sceneSource) - { - // Is there a reason why we excluded season 1 from this handling before? - // Might have something to do with the scene name to season number check - // If this needs to be reverted tests will need to be added - if (sceneSeasonNumber.HasValue) - { - var episodes = _episodeService.FindEpisodesBySceneNumbering(series.Id, sceneSeasonNumber.Value, absoluteEpisodeNumber); - - if (episodes.Count == 1) - { - episode = episodes.First(); - } - - if (episode == null) - { - episode = _episodeService.FindEpisode(series.Id, sceneSeasonNumber.Value, absoluteEpisodeNumber); - } - } - - else - { - episode = _episodeService.FindEpisodeBySceneNumbering(series.Id, absoluteEpisodeNumber); - } - } - - if (episode == null) - { - episode = _episodeService.FindEpisode(series.Id, absoluteEpisodeNumber); - } - - if (episode != null) - { - _logger.Debug("Using absolute episode number {0} for: {1} - TVDB: {2}x{3:00}", - absoluteEpisodeNumber, - series.Title, - episode.SeasonNumber, - episode.EpisodeNumber); - - result.Add(episode); - } - } - - return result; + return GetAnimeEpisodes(series, parsedEpisodeInfo, sceneSource); } - if (parsedEpisodeInfo.EpisodeNumbers == null) - return result; - - foreach (var episodeNumber in parsedEpisodeInfo.EpisodeNumbers) - { - if (series.UseSceneNumbering && sceneSource) - { - List episodes = new List(); - - if (searchCriteria != null) - { - episodes = searchCriteria.Episodes.Where(e => e.SceneSeasonNumber == parsedEpisodeInfo.SeasonNumber && - e.SceneEpisodeNumber == episodeNumber).ToList(); - } - - if (!episodes.Any()) - { - episodes = _episodeService.FindEpisodesBySceneNumbering(series.Id, parsedEpisodeInfo.SeasonNumber, episodeNumber); - } - - if (episodes != null && episodes.Any()) - { - _logger.Debug("Using Scene to TVDB Mapping for: {0} - Scene: {1}x{2:00} - TVDB: {3}", - series.Title, - 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; - } - } - - Episode episodeInfo = null; - - if (searchCriteria != null) - { - episodeInfo = searchCriteria.Episodes.SingleOrDefault(e => e.SeasonNumber == parsedEpisodeInfo.SeasonNumber && - e.EpisodeNumber == episodeNumber); - } - - if (episodeInfo == null) - { - episodeInfo = _episodeService.FindEpisode(series.Id, parsedEpisodeInfo.SeasonNumber, episodeNumber); - } - - if (episodeInfo != null) - { - result.Add(episodeInfo); - } - - else - { - _logger.Debug("Unable to find {0}", parsedEpisodeInfo); - } - } - - return result; + return GetStandardEpisodes(series, parsedEpisodeInfo, sceneSource, searchCriteria); } public ParsedEpisodeInfo ParseSpecialEpisodeTitle(string title, int tvdbId, int tvRageId, SearchCriteriaBase searchCriteria = null) @@ -439,5 +326,143 @@ namespace NzbDrone.Core.Parser return episodeInfo; } + + private List GetAnimeEpisodes(Series series, ParsedEpisodeInfo parsedEpisodeInfo, bool sceneSource) + { + var result = new List(); + + var sceneSeasonNumber = _sceneMappingService.GetSceneSeasonNumber(parsedEpisodeInfo.SeriesTitle); + + foreach (var absoluteEpisodeNumber in parsedEpisodeInfo.AbsoluteEpisodeNumbers) + { + Episode episode = null; + + if (parsedEpisodeInfo.Special) + { + episode = _episodeService.FindEpisode(series.Id, 0, absoluteEpisodeNumber); + } + + else if (sceneSource) + { + // Is there a reason why we excluded season 1 from this handling before? + // Might have something to do with the scene name to season number check + // If this needs to be reverted tests will need to be added + if (sceneSeasonNumber.HasValue) + { + var episodes = _episodeService.FindEpisodesBySceneNumbering(series.Id, sceneSeasonNumber.Value, absoluteEpisodeNumber); + + if (episodes.Count == 1) + { + episode = episodes.First(); + } + + if (episode == null) + { + episode = _episodeService.FindEpisode(series.Id, sceneSeasonNumber.Value, absoluteEpisodeNumber); + } + } + + else + { + episode = _episodeService.FindEpisodeBySceneNumbering(series.Id, absoluteEpisodeNumber); + } + } + + if (episode == null) + { + episode = _episodeService.FindEpisode(series.Id, absoluteEpisodeNumber); + } + + if (episode != null) + { + _logger.Debug("Using absolute episode number {0} for: {1} - TVDB: {2}x{3:00}", + absoluteEpisodeNumber, + series.Title, + episode.SeasonNumber, + episode.EpisodeNumber); + + result.Add(episode); + } + } + + return result; + } + + private List GetStandardEpisodes(Series series, ParsedEpisodeInfo parsedEpisodeInfo, bool sceneSource, SearchCriteriaBase searchCriteria) + { + var result = new List(); + var seasonNumber = parsedEpisodeInfo.SeasonNumber; + + if (sceneSource) + { + var sceneMapping = _sceneMappingService.FindSceneMapping(parsedEpisodeInfo.SeriesTitle); + + if (sceneMapping != null && sceneMapping.SeasonNumber.HasValue && sceneMapping.SeasonNumber.Value >= 0 && + sceneMapping.SceneSeasonNumber == seasonNumber) + { + seasonNumber = sceneMapping.SeasonNumber.Value; + } + } + + if (parsedEpisodeInfo.EpisodeNumbers == null) + { + return new List(); + } + + foreach (var episodeNumber in parsedEpisodeInfo.EpisodeNumbers) + { + if (series.UseSceneNumbering && sceneSource) + { + List episodes = new List(); + + if (searchCriteria != null) + { + episodes = searchCriteria.Episodes.Where(e => e.SceneSeasonNumber == parsedEpisodeInfo.SeasonNumber && + e.SceneEpisodeNumber == episodeNumber).ToList(); + } + + if (!episodes.Any()) + { + episodes = _episodeService.FindEpisodesBySceneNumbering(series.Id, seasonNumber, episodeNumber); + } + + if (episodes != null && episodes.Any()) + { + _logger.Debug("Using Scene to TVDB Mapping for: {0} - Scene: {1}x{2:00} - TVDB: {3}", + series.Title, + 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; + } + } + + Episode episodeInfo = null; + + if (searchCriteria != null) + { + episodeInfo = searchCriteria.Episodes.SingleOrDefault(e => e.SeasonNumber == seasonNumber && e.EpisodeNumber == episodeNumber); + } + + if (episodeInfo == null) + { + episodeInfo = _episodeService.FindEpisode(series.Id, seasonNumber, episodeNumber); + } + + if (episodeInfo != null) + { + result.Add(episodeInfo); + } + + else + { + _logger.Debug("Unable to find {0}", parsedEpisodeInfo); + } + } + + return result; + } } } \ No newline at end of file