diff --git a/CHANGELOG.md b/CHANGELOG.md index 8cb588c79..72ec00cda 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ## [Unreleased] +- Support `(Part #)` name suffixes for multi-part episode grouping ## [0.0.42-prealpha] - 2021-05-31 ### Added diff --git a/ErsatzTV.Core.Tests/Scheduling/MultiPartEpisodeGrouperTests.cs b/ErsatzTV.Core.Tests/Scheduling/MultiPartEpisodeGrouperTests.cs index 9f2ec22b1..5fd21999b 100644 --- a/ErsatzTV.Core.Tests/Scheduling/MultiPartEpisodeGrouperTests.cs +++ b/ErsatzTV.Core.Tests/Scheduling/MultiPartEpisodeGrouperTests.cs @@ -14,6 +14,7 @@ namespace ErsatzTV.Core.Tests.Scheduling [TestCase("Episode 1", "Episode 2 (1)", "Episode 3 (2)", "Episode 4")] [TestCase("Episode 1 - More", "Episode 2 (1) - Title", "Episode 3 (2) - After", "Episode 4 - Dash")] [TestCase("Episode 1", "Episode 2 Part 1", "Episode 3 Part 2", "Episode 4")] + [TestCase("Episode 1", "Episode 2 (Part 1)", "Episode 3 (Part 2)", "Episode 4")] public void NotGrouped_Grouped_NotGrouped(string one, string two, string three, string four) { var mediaItems = new List @@ -36,8 +37,8 @@ namespace ErsatzTV.Core.Tests.Scheduling [TestCase("Episode 1 (1)", "Episode 2 - Part 2", "Episode 3")] [TestCase("Episode 1 Part 1", "Episode 2 (2) - More", "Episode 3 - After")] [TestCase("Episode 1 Part 1", "Episode 2 (II)", "Episode 3")] - [TestCase("Episode 1 Part (V)", "Episode 2 (VI)", "Episode 3")] - [TestCase("Episode 1 Part Three", "Episode 2 (IV)", "Episode 3")] + [TestCase("Episode 1 Part One", "Episode 2 (II)", "Episode 3")] + [TestCase("Episode 1 (1)", "Episode 2 (Part 2)", "Episode 3")] public void MixedNaming_Group(string one, string two, string three) { var mediaItems = new List @@ -54,10 +55,32 @@ namespace ErsatzTV.Core.Tests.Scheduling ShouldHaveOneItem(result, mediaItems[2]); } + [Test] + [TestCase("Episode 1 (5)", "Episode 2 - (6)", "Episode 3")] + [TestCase("Episode 1 Part 5", "Episode 2 Part 6", "Episode 3 - After")] + [TestCase("Episode 1 Part (V)", "Episode 2 (VI)", "Episode 3")] + [TestCase("Episode 1 (Part 5)", "Episode 2 (Part 6)", "Episode 3")] + public void Only_Later_Parts(string one, string two, string three) + { + var mediaItems = new List + { + NamedEpisode(one, 1, 1, 1), + NamedEpisode(two, 1, 1, 2), + NamedEpisode(three, 1, 1, 3) + }; + + List result = MultiPartEpisodeGrouper.GroupMediaItems(mediaItems, false); + + result.Count.Should().Be(2); + ShouldHaveTwoItems(result, mediaItems[0], mediaItems[1]); + ShouldHaveOneItem(result, mediaItems[2]); + } + [Test] [TestCase("Episode 1 (1)", "Episode 2 (2)", "Episode 3")] [TestCase("Episode 1 (1) - More", "Episode 2 (2) - Title", "Episode 3 - After")] [TestCase("Episode 1 Part 1", "Episode 2 Part 2", "Episode 3")] + [TestCase("Episode 1 (Part 1)", "Episode 2 (Part 2)", "Episode 3")] public void Grouped_NotGrouped(string one, string two, string three) { var mediaItems = new List @@ -83,6 +106,7 @@ namespace ErsatzTV.Core.Tests.Scheduling "Episode 4 (1) - Dash", "Episode 5 (2) - Again")] [TestCase("Episode 1 Part 1", "Episode 2 Part 2", "Episode 3", "Episode 4 Part 1", "Episode 5 Part 2")] + [TestCase("Episode 1 (Part 1)", "Episode 2 (Part 2)", "Episode 3", "Episode 4 (Part 1)", "Episode 5 (Part 2)")] public void Grouped_NotGrouped_Grouped(string one, string two, string three, string four, string five) { var mediaItems = new List @@ -106,6 +130,7 @@ namespace ErsatzTV.Core.Tests.Scheduling [TestCase("Episode 1 (1)", "Episode 2 (2)", "Episode 3 (1)", "Episode 4 (2)")] [TestCase("Episode 1 (1) - More", "Episode 2 (2) - Title", "Episode 3 (1) - After", "Episode 4 (2) - Dash")] [TestCase("Episode 1 Part 1", "Episode 2 Part 2", "Episode 3 Part 1", "Episode 4 Part 2")] + [TestCase("Episode 1 (Part 1)", "Episode 2 (Part 2)", "Episode 3 (Part 1)", "Episode 4 (Part 2)")] public void Grouped_Grouped(string one, string two, string three, string four) { var mediaItems = new List @@ -127,6 +152,7 @@ namespace ErsatzTV.Core.Tests.Scheduling [TestCase("Episode 1", "Episode 2 (2)", "Episode 3 (1)", "Episode 4 (2)")] [TestCase("Episode 1 - More", "Episode 2 (2) - Title", "Episode 3 (1) - After", "Episode 4 (2) - Dash")] [TestCase("Episode 1", "Episode 2 Part 2", "Episode 3 Part 1", "Episode 4 Part 2")] + [TestCase("Episode 1", "Episode 2 (Part 2)", "Episode 3 (Part 1)", "Episode 4 (Part 2)")] public void Part2_Without_Part1(string one, string two, string three, string four) { var mediaItems = new List @@ -149,6 +175,7 @@ namespace ErsatzTV.Core.Tests.Scheduling [TestCase("Episode 1", "Episode 2 (2)", "Episode 3 (3)", "Episode 4")] [TestCase("Episode 1 - More", "Episode 2 (2) - Title", "Episode 3 (3) - After", "Episode 4 - Dash")] [TestCase("Episode 1", "Episode 2 Part 2", "Episode 3 Part 3", "Episode 4")] + [TestCase("Episode 1", "Episode 2 (Part 2)", "Episode 3 (Part 3)", "Episode 4")] public void Part2And3_Without_Part1(string one, string two, string three, string four) { var mediaItems = new List @@ -171,6 +198,7 @@ namespace ErsatzTV.Core.Tests.Scheduling [TestCase("Episode 1 (1)", "Episode 3 (3)", "Episode 4", "Episode 5")] [TestCase("Episode 1 (1) - More", "Episode 3 (3) - Title", "Episode 4 - After", "Episode 5 - Dash")] [TestCase("Episode 1 Part 1", "Episode 3 Part 3", "Episode 4", "Episode 5")] + [TestCase("Episode 1 (Part 1)", "Episode 3 (Part 3)", "Episode 4", "Episode 5")] public void Skip_Part(string one, string two, string three, string four) { var mediaItems = new List @@ -194,6 +222,7 @@ namespace ErsatzTV.Core.Tests.Scheduling [TestCase("Episode 1 (1)", "Episode 3 (1)", "Episode 4 (2)", "Episode 5")] [TestCase("Episode 1 (1) - More", "Episode 3 (1) - Title", "Episode 4 (2) - After", "Episode 5 - Dash")] [TestCase("Episode 1 Part 1", "Episode 3 Part 1", "Episode 4 Part 2", "Episode 5")] + [TestCase("Episode 1 (Part 1)", "Episode 3 (Part 1)", "Episode 4 (Part 2)", "Episode 5")] public void Repeat_Part(string one, string two, string three, string four) { var mediaItems = new List @@ -220,6 +249,7 @@ namespace ErsatzTV.Core.Tests.Scheduling "S1 Episode 2 (2) - After", "S1 Episode 5 - Dash")] [TestCase("S1 Episode 1 Part 1", "S2 Episode 3 Part 1", "S1 Episode 2 Part 2", "S1 Episode 5")] + [TestCase("S1 Episode 1 (Part 1)", "S2 Episode 3 (Part 1)", "S1 Episode 2 (Part 2)", "S1 Episode 5")] public void Mixed_Shows_Chronologically(string one, string two, string three, string four) { var mediaItems = new List @@ -246,6 +276,7 @@ namespace ErsatzTV.Core.Tests.Scheduling "S1 Episode 2 (3) - After", "S1 Episode 5 - Dash")] [TestCase("S1 Episode 1 Part 1", "S2 Episode 3 Part 2", "S1 Episode 2 Part 3", "S1 Episode 5")] + [TestCase("S1 Episode 1 (Part 1)", "S2 Episode 3 (Part 2)", "S1 Episode 2 (Part 3)", "S1 Episode 5")] public void Mixed_Shows_Chronologically_Crossover(string one, string two, string three, string four) { var mediaItems = new List diff --git a/ErsatzTV.Core/Scheduling/MultiPartEpisodeGrouper.cs b/ErsatzTV.Core/Scheduling/MultiPartEpisodeGrouper.cs index 525d2daf6..6c64ed0dd 100644 --- a/ErsatzTV.Core/Scheduling/MultiPartEpisodeGrouper.cs +++ b/ErsatzTV.Core/Scheduling/MultiPartEpisodeGrouper.cs @@ -128,7 +128,7 @@ namespace ErsatzTV.Core.Scheduling return value1; } - const string PATTERN_2 = @"^.*Part (\d+)$"; + const string PATTERN_2 = @"^.*\(?Part (\d+)\)?$"; Match match2 = Regex.Match(e.EpisodeMetadata.Head().Title, PATTERN_2); if (match2.Success && int.TryParse(match2.Groups[1].Value, out int value2)) {