support more multi-part grouping names (#2165)
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
[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
|
||||
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")]
|
||||
|
||||
@@ -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
|
||||
{
|
||||
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
|
||||
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();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user