New: Support for using parsed season number for some anime releases without aliases

Closes #4377
This commit is contained in:
Mark McDowall 2021-03-17 17:14:54 -07:00
parent eea6be459d
commit d4167d7169
4 changed files with 67 additions and 31 deletions

View File

@ -102,6 +102,7 @@ namespace NzbDrone.Core.Test.ParserTests
[TestCase("[abc] Adventure Series: 30 [Web][MKV][h264][720p][AAC 2.0][abc]", "Adventure Series:", 30, 0, 0)]
[TestCase("[XKsub] Series Title S2 [05][HEVC-10bit 1080p AAC][CHS&CHT&JPN]", "Series Title S2", 5, 0, 0)]
[TestCase("[Cheetah-Raws] Super Long Anime - 1000 (YTV 1280x720 x264 AAC)", "Super Long Anime", 1000, 0, 0)]
[TestCase("[DameDesuYo] ReZero kara Hajimeru Isekai Seikatsu (Season 2) - 33 (1280x720 10bit EAC3) [42A12A76].mkv", "ReZero kara Hajimeru Isekai Seikatsu", 33, 2, 0)]
//[TestCase("", "", 0, 0, 0)]
public void should_parse_absolute_numbers(string postTitle, string title, int absoluteEpisodeNumber, int seasonNumber, int episodeNumber)
{

View File

@ -268,6 +268,26 @@ namespace NzbDrone.Core.Test.ParserTests.ParsingServiceTests
.Verify(v => v.FindEpisode(It.IsAny<int>(), seasonNumber, It.IsAny<int>()), Times.Never());
}
[TestCase(2)]
[TestCase(20)]
public void should_find_episode_by_parsed_season_and_absolute_episode_number_when_season_number_is_2_or_higher(int seasonNumber)
{
GivenAbsoluteNumberingSeries();
_parsedEpisodeInfo.SeasonNumber = seasonNumber;
Mocker.GetMock<IEpisodeService>()
.Setup(s => s.FindEpisodesBySceneNumbering(It.IsAny<int>(), seasonNumber, It.IsAny<int>()))
.Returns(new List<Episode> { _episodes.First() });
Subject.GetEpisodes(_parsedEpisodeInfo, _series, true, null);
Mocker.GetMock<IEpisodeService>()
.Verify(v => v.FindEpisodesBySceneNumbering(It.IsAny<int>(), seasonNumber, It.IsAny<int>()), Times.Once());
Mocker.GetMock<IEpisodeService>()
.Verify(v => v.FindEpisode(It.IsAny<int>(), seasonNumber, It.IsAny<int>()), Times.Never());
}
[TestCase(0)]
[TestCase(1)]
[TestCase(2)]

View File

@ -73,6 +73,10 @@ namespace NzbDrone.Core.Parser
new Regex(@"^\[(?<subgroup>.+?)\][-_. ]?(?<title>[^-]+?)(?:(?<![-_. ]|\b[0]\d+) - )[-_. ]?(?<absoluteepisode>\d{2,3}(\.\d{1,2})?(?!\d+))~(?<absoluteepisode>\d{2,3}(\.\d{1,2})?(?!\d+))(?:[-_. ]+(?<special>special|ova|ovd))?.*?(?<hash>\[\w{8}\])?(?:$|\.mkv)",
RegexOptions.IgnoreCase | RegexOptions.Compiled),
//Anime - [SubGroup] Title with season number in brackets Absolute Episode Number
new Regex(@"^\[(?<subgroup>.+?)\][-_. ]?(?<title>[^-]+?)[_. ]+?\(Season[_. ](?<season>\d+)\)[-_. ]+?(?:[-_. ]?(?<absoluteepisode>\d{2,3}(\.\d{1,2})?(?!\d+)))+(?:[-_. ]+(?<special>special|ova|ovd))?.*?(?<hash>\[\w{8}\])?(?:$|\.mkv)",
RegexOptions.IgnoreCase | RegexOptions.Compiled),
//Anime - [SubGroup] Title with trailing number Absolute Episode Number
new Regex(@"^\[(?<subgroup>.+?)\][-_. ]?(?<title>[^-]+?)(?:(?<![-_. ]|\b[0]\d+) - )(?:[-_. ]?(?<absoluteepisode>\d{2,3}(\.\d{1,2})?(?!\d+)))+(?:[-_. ]+(?<special>special|ova|ovd))?.*?(?<hash>\[\w{8}\])?(?:$|\.mkv)",
RegexOptions.IgnoreCase | RegexOptions.Compiled),
@ -748,37 +752,13 @@ namespace NzbDrone.Core.Parser
if (airYear < 1900)
{
var seasons = new List<int>();
foreach (Capture seasonCapture in matchCollection[0].Groups["season"].Captures)
{
int parsedSeason;
if (int.TryParse(seasonCapture.Value, out parsedSeason))
{
seasons.Add(parsedSeason);
lastSeasonEpisodeStringIndex = Math.Max(lastSeasonEpisodeStringIndex, seasonCapture.EndIndex());
}
}
//If no season was found it should be treated as a mini series and season 1
if (seasons.Count == 0) seasons.Add(1);
result = new ParsedEpisodeInfo
{
ReleaseTitle = releaseTitle,
SeasonNumber = seasons.First(),
EpisodeNumbers = new int[0],
AbsoluteEpisodeNumbers = new int[0]
};
//If more than 1 season was parsed set IsMultiSeason to true so it can be rejected later
if (seasons.Distinct().Count() > 1)
{
result.IsMultiSeason = true;
}
foreach (Match matchGroup in matchCollection)
{
var episodeCaptures = matchGroup.Groups["episode"].Captures.Cast<Capture>().ToList();
@ -860,9 +840,34 @@ namespace NzbDrone.Core.Parser
}
}
if (result.AbsoluteEpisodeNumbers.Any() && !result.EpisodeNumbers.Any())
var seasons = new List<int>();
foreach (Capture seasonCapture in matchCollection[0].Groups["season"].Captures)
{
result.SeasonNumber = 0;
int parsedSeason;
if (int.TryParse(seasonCapture.Value, out parsedSeason))
{
seasons.Add(parsedSeason);
lastSeasonEpisodeStringIndex = Math.Max(lastSeasonEpisodeStringIndex, seasonCapture.EndIndex());
}
}
//If more than 1 season was parsed set IsMultiSeason to true so it can be rejected later
if (seasons.Distinct().Count() > 1)
{
result.IsMultiSeason = true;
}
if (seasons.Any())
{
// If at least one season was parsed use the first season as the season
result.SeasonNumber = seasons.First();
}
else if (!result.AbsoluteEpisodeNumbers.Any() && result.EpisodeNumbers.Any())
{
// If no season was found and it's not an absolute only release it should be treated as a mini series and season 1
result.SeasonNumber = 1;
}
}

View File

@ -465,6 +465,16 @@ namespace NzbDrone.Core.Parser
episodes.AddIfNotNull(episode);
}
}
else if (parsedEpisodeInfo.SeasonNumber > 1)
{
episodes = _episodeService.FindEpisodesBySceneNumbering(series.Id, parsedEpisodeInfo.SeasonNumber, absoluteEpisodeNumber);
if (episodes.Empty())
{
var episode = _episodeService.FindEpisode(series.Id, sceneSeasonNumber.Value, absoluteEpisodeNumber);
episodes.AddIfNotNull(episode);
}
}
else
{
episodes = _episodeService.FindEpisodesBySceneNumbering(series.Id, absoluteEpisodeNumber);