diff --git a/src/NzbDrone.Core.Test/ParserTests/AbsoluteEpisodeNumberParserFixture.cs b/src/NzbDrone.Core.Test/ParserTests/AbsoluteEpisodeNumberParserFixture.cs index ee96178f2..e0cae8bf2 100644 --- a/src/NzbDrone.Core.Test/ParserTests/AbsoluteEpisodeNumberParserFixture.cs +++ b/src/NzbDrone.Core.Test/ParserTests/AbsoluteEpisodeNumberParserFixture.cs @@ -194,6 +194,8 @@ namespace NzbDrone.Core.Test.ParserTests result.SeasonNumber.Should().Be(seasonNumber); } + [TestCase("[Anime Time] Series no Mayo - 12.5.mkv", "Series no Mayo", 12.5)] + [TestCase("[SubsPlease] Series Title - 26.5.1 (1080p) [29AF1C23].mkv", "Series Title", 26.5)] [TestCase("[HorribleSubs] Show Slayer - 10.5 [1080p].mkv", "Show Slayer", 10.5)] public void should_handle_anime_recap_numbering(string postTitle, string title, double specialEpisodeNumber) { diff --git a/src/NzbDrone.Core.Test/ParserTests/SingleEpisodeParserFixture.cs b/src/NzbDrone.Core.Test/ParserTests/SingleEpisodeParserFixture.cs index a15ef0f79..4ebd0f1e1 100644 --- a/src/NzbDrone.Core.Test/ParserTests/SingleEpisodeParserFixture.cs +++ b/src/NzbDrone.Core.Test/ParserTests/SingleEpisodeParserFixture.cs @@ -193,5 +193,19 @@ namespace NzbDrone.Core.Test.ParserTests result.AbsoluteEpisodeNumbers.Should().BeEmpty(); result.FullSeason.Should().BeFalse(); } + + [TestCase("Series Title S01E11.5 [SP]-The Poppies Bloom Red on the Battlefield", "Series Title", 1, 11)] + public void should_parse_decimal_number_as_special(string postTitle, string title, int seasonNumber, int episodeNumber) + { + var result = Parser.Parser.ParseTitle(postTitle); + result.Should().NotBeNull(); + result.EpisodeNumbers.Should().HaveCount(1); + result.SeasonNumber.Should().Be(seasonNumber); + result.EpisodeNumbers.First().Should().Be(episodeNumber); + result.SeriesTitle.Should().Be(title); + result.AbsoluteEpisodeNumbers.Should().BeEmpty(); + result.FullSeason.Should().BeFalse(); + result.Special.Should().BeTrue(); + } } } diff --git a/src/NzbDrone.Core/Parser/Parser.cs b/src/NzbDrone.Core/Parser/Parser.cs index e24708834..15a8c73c8 100644 --- a/src/NzbDrone.Core/Parser/Parser.cs +++ b/src/NzbDrone.Core/Parser/Parser.cs @@ -134,6 +134,10 @@ namespace NzbDrone.Core.Parser new Regex(@"^(?.+?)(?:(?:[-_\W](?<![()\[!]))+(?<season>(?<!\d+)(?:\d{1,2}|\d{4})(?!\d+))(?:x{1,2}(?<episode>\d{1,3}(?!\d+)))+){2,}", RegexOptions.IgnoreCase | RegexOptions.Compiled), + // Single episodes with a title (S01E05, 1x05, etc) followed by ".5 [SP]" + new Regex(@"^(?<title>.+?)(?:(?:[-_\W](?<![()\[!]))+S?(?<season>(?<!\d+)(?:\d{1,2})(?!\d+))(?:[ex]|\W[ex]|_){1,2}(?<episode>\d{2,3}(?!\d+|(?:[ex]|\W[ex]|_|-){1,2}\d+))(?<special>\.5[ .]\[SP\]))", + RegexOptions.IgnoreCase | RegexOptions.Compiled), + // Single episodes with a title (S01E05, 1x05, etc) and trailing info in slashes new Regex(@"^(?<title>.+?)(?:(?:[-_\W](?<![()\[!]))+S?(?<season>(?<!\d+)(?:\d{1,2})(?!\d+))(?:[ex]|\W[ex]|_){1,2}(?<episode>\d{2,3}(?!\d+|(?:[ex]|\W[ex]|_|-){1,2}\d+))).+?(?:\[.+?\])(?!\\)", RegexOptions.IgnoreCase | RegexOptions.Compiled), @@ -946,6 +950,11 @@ namespace NzbDrone.Core.Parser result.EpisodeNumbers = Enumerable.Range(first, count).ToArray(); lastSeasonEpisodeStringIndex = Math.Max(lastSeasonEpisodeStringIndex, episodeCaptures.Last().EndIndex()); + + if (matchGroup.Groups["special"].Success) + { + result.Special = true; + } } if (absoluteEpisodeCaptures.Any()) diff --git a/src/NzbDrone.Core/Parser/ParsingService.cs b/src/NzbDrone.Core/Parser/ParsingService.cs index 987028f5a..d8cd88cac 100644 --- a/src/NzbDrone.Core/Parser/ParsingService.cs +++ b/src/NzbDrone.Core/Parser/ParsingService.cs @@ -265,6 +265,11 @@ namespace NzbDrone.Core.Parser } } + if (parsedEpisodeInfo.Special && mappedSeasonNumber != 0) + { + return new List<Episode>(); + } + return GetStandardEpisodes(series, parsedEpisodeInfo, mappedSeasonNumber, sceneSource, searchCriteria); }