Browse Source

support more multi-part grouping names (#2165)

pull/2166/head
Jason Dove 4 weeks ago committed by GitHub
parent
commit
9e56f6552f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 4
      CHANGELOG.md
  2. 21
      ErsatzTV.Core.Tests/Scheduling/MultiPartEpisodeGrouperTests.cs
  3. 26
      ErsatzTV.Core/Scheduling/MultiPartEpisodeGrouper.cs

4
CHANGELOG.md

@ -95,6 +95,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). @@ -95,6 +95,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- The FFmpeg report of the playback attempt
- The media info for the content
- The `Troubleshooting` > `General` output
- Support `(Part [english number])` name suffixes for multi-part episode grouping, for example:
- `Awesome Episode (Part One)`
- `Better Episode (Part Two)`
- `Not So Great (Part Three)`
### Changed
- Allow `Other Video` libraries and `Image` libraries to use the same folders

21
ErsatzTV.Core.Tests/Scheduling/MultiPartEpisodeGrouperTests.cs

@ -12,6 +12,7 @@ public class MultiPartEpisodeGrouperTests @@ -12,6 +12,7 @@ public class MultiPartEpisodeGrouperTests
[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")]
[TestCase("Episode 1", "Episode 2 (Part One)", "Episode 3 (Part Two)", "Episode 4")]
public void NotGrouped_Grouped_NotGrouped(string one, string two, string three, string four)
{
var mediaItems = new List<MediaItem>
@ -35,7 +36,9 @@ public class MultiPartEpisodeGrouperTests @@ -35,7 +36,9 @@ public class MultiPartEpisodeGrouperTests
[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 One", "Episode 2 (II)", "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<MediaItem>
@ -52,6 +55,24 @@ public class MultiPartEpisodeGrouperTests @@ -52,6 +55,24 @@ public class MultiPartEpisodeGrouperTests
ShouldHaveOneItem(result, mediaItems[2]);
}
[Test]
[TestCase("The Meddlers (Part One)", "The Meddlers (Part Two)", "The Meddlers (Part Three)")]
[TestCase("S01E01 The Slaves of Jedikiah, Part 1", "S01E02 The Slaves of Jedikiah, Part 2", "S01E03 The Slaves of Jedikiah, Part 3")]
public void All_Grouped(string one, string two, string three)
{
var mediaItems = new List<MediaItem>
{
NamedEpisode(one, 1, 1, 1),
NamedEpisode(two, 1, 1, 2),
NamedEpisode(three, 1, 1, 3)
};
List<GroupedMediaItem> result = MultiPartEpisodeGrouper.GroupMediaItems(mediaItems, false);
result.Count.ShouldBe(1);
ShouldHaveMultipleItems(result, mediaItems[0], [mediaItems[1], mediaItems[2]]);
}
[Test]
[TestCase("Episode 1 (5)", "Episode 2 - (6)", "Episode 3")]
[TestCase("Episode 1 Part 5", "Episode 2 Part 6", "Episode 3 - After")]

26
ErsatzTV.Core/Scheduling/MultiPartEpisodeGrouper.cs

@ -4,7 +4,7 @@ using LanguageExt.UnsafeValueAccess; @@ -4,7 +4,7 @@ using LanguageExt.UnsafeValueAccess;
namespace ErsatzTV.Core.Scheduling;
public static class MultiPartEpisodeGrouper
public static partial class MultiPartEpisodeGrouper
{
public static List<GroupedMediaItem> GroupMediaItems(IList<MediaItem> mediaItems, bool treatCollectionsAsShows)
{
@ -118,29 +118,25 @@ public static class MultiPartEpisodeGrouper @@ -118,29 +118,25 @@ public static class MultiPartEpisodeGrouper
{
foreach (EpisodeMetadata metadata in e.EpisodeMetadata.HeadOrNone())
{
const string PATTERN = @"^.*\((\d+)\)( - .*)?$";
Match match = Regex.Match(metadata.Title ?? string.Empty, PATTERN);
Match match = Pattern1Regex().Match(metadata.Title ?? string.Empty);
if (match.Success && int.TryParse(match.Groups[1].Value, out int value1))
{
return value1;
}
const string PATTERN_2 = @"^.*\(?Part (\d+)\)?$";
Match match2 = Regex.Match(metadata.Title ?? string.Empty, PATTERN_2);
Match match2 = Pattern2Regex().Match(metadata.Title ?? string.Empty);
if (match2.Success && int.TryParse(match2.Groups[1].Value, out int value2))
{
return value2;
}
const string PATTERN_3 = @"^.*\(([MDCLXVI]+)\)( - .*)?$";
Match match3 = Regex.Match(metadata.Title ?? string.Empty, PATTERN_3);
Match match3 = Pattern3Regex().Match(metadata.Title ?? string.Empty);
if (match3.Success && TryParseRoman(match3.Groups[1].Value, out int value3))
{
return value3;
}
const string PATTERN_4 = @"^.*Part (\w+)$";
Match match4 = Regex.Match(metadata.Title ?? string.Empty, PATTERN_4);
Match match4 = Pattern4Regex().Match(metadata.Title ?? string.Empty);
if (match4.Success && TryParseEnglish(match4.Groups[1].Value, out int value4))
{
return value4;
@ -229,4 +225,16 @@ public static class MultiPartEpisodeGrouper @@ -229,4 +225,16 @@ public static class MultiPartEpisodeGrouper
return false;
}
}
[GeneratedRegex(@"^.*\((\d+)\)( - .*)?$")]
private static partial Regex Pattern1Regex();
[GeneratedRegex(@"^.*\(?Part (\d+)\)?$")]
private static partial Regex Pattern2Regex();
[GeneratedRegex(@"^.*\(([MDCLXVI]+)\)( - .*)?$")]
private static partial Regex Pattern3Regex();
[GeneratedRegex(@"^.*\(?Part (\w+)\)?$")]
private static partial Regex Pattern4Regex();
}

Loading…
Cancel
Save