From c6bb3dbad5c1349eb0d60209f1b4b6abb489b995 Mon Sep 17 00:00:00 2001 From: TwistedUmbrellaX Date: Wed, 6 Mar 2024 10:15:03 -0500 Subject: [PATCH] Search credits from the end of episode --- .../Analyzers/ChapterAnalyzer.cs | 151 ++++++++++++------ .../Configuration/PluginConfiguration.cs | 2 +- 2 files changed, 104 insertions(+), 49 deletions(-) diff --git a/ConfusedPolarBear.Plugin.IntroSkipper/Analyzers/ChapterAnalyzer.cs b/ConfusedPolarBear.Plugin.IntroSkipper/Analyzers/ChapterAnalyzer.cs index 0b146a7..dd2b2cf 100644 --- a/ConfusedPolarBear.Plugin.IntroSkipper/Analyzers/ChapterAnalyzer.cs +++ b/ConfusedPolarBear.Plugin.IntroSkipper/Analyzers/ChapterAnalyzer.cs @@ -96,6 +96,11 @@ public class ChapterAnalyzer : IMediaFileAnalyzer config.MaximumIntroDuration : config.MaximumEpisodeCreditsDuration; + if (chapters.Count == 0) + { + return null; + } + if (mode == AnalysisMode.Credits) { // Since the ending credits chapter may be the last chapter in the file, append a virtual @@ -104,69 +109,119 @@ public class ChapterAnalyzer : IMediaFileAnalyzer { StartPositionTicks = TimeSpan.FromSeconds(episode.Duration).Ticks }); - } - // Check all chapters - for (int i = 0; i < chapters.Count - 1; i++) - { - var current = chapters[i]; - var next = chapters[i + 1]; - - if (string.IsNullOrWhiteSpace(current.Name)) + // Check all chapters in reverse order, skipping the virtual chapter + for (int i = chapters.Count - 2; i >= 0; i--) { - continue; - } + var current = chapters[i]; + var next = chapters[i + 1]; - var currentRange = new TimeRange( - TimeSpan.FromTicks(current.StartPositionTicks).TotalSeconds, - TimeSpan.FromTicks(next.StartPositionTicks).TotalSeconds); + if (string.IsNullOrWhiteSpace(current.Name)) + { + continue; + } - var baseMessage = string.Format( - CultureInfo.InvariantCulture, - "{0}: Chapter \"{1}\" ({2} - {3})", - episode.Path, - current.Name, - currentRange.Start, - currentRange.End); + var currentRange = new TimeRange( + TimeSpan.FromTicks(current.StartPositionTicks).TotalSeconds, + TimeSpan.FromTicks(next.StartPositionTicks).TotalSeconds); - if (currentRange.Duration < minDuration || currentRange.Duration > maxDuration) - { - _logger.LogTrace("{Base}: ignoring (invalid duration)", baseMessage); - continue; - } + var baseMessage = string.Format( + CultureInfo.InvariantCulture, + "{0}: Chapter \"{1}\" ({2} - {3})", + episode.Path, + current.Name, + currentRange.Start, + currentRange.End); - // Regex.IsMatch() is used here in order to allow the runtime to cache the compiled regex - // between function invocations. - var match = Regex.IsMatch( - current.Name, - expression, - RegexOptions.None, - TimeSpan.FromSeconds(1)); + if (currentRange.Duration < minDuration || currentRange.Duration > maxDuration) + { + _logger.LogTrace("{Base}: ignoring (invalid duration)", baseMessage); + continue; + } - if (!match) - { - _logger.LogTrace("{Base}: ignoring (does not match regular expression)", baseMessage); - continue; - } - - if (!string.IsNullOrWhiteSpace(next.Name)) - { - // Check for possibility of overlapping keywords - var overlap = Regex.IsMatch( - next.Name, + // Regex.IsMatch() is used here in order to allow the runtime to cache the compiled regex + // between function invocations. + var match = Regex.IsMatch( + current.Name, expression, RegexOptions.None, TimeSpan.FromSeconds(1)); - if (overlap) + if (!match) + { + _logger.LogTrace("{Base}: ignoring (does not match regular expression)", baseMessage); + continue; + } + + matchingChapter = new(episode.EpisodeId, currentRange); + _logger.LogTrace("{Base}: okay", baseMessage); + break; + } + } + else + { + // Check all chapters + for (int i = 0; i < chapters.Count - 1; i++) + { + var current = chapters[i]; + var next = chapters[i + 1]; + + if (string.IsNullOrWhiteSpace(current.Name)) { continue; } - } - matchingChapter = new(episode.EpisodeId, currentRange); - _logger.LogTrace("{Base}: okay", baseMessage); - break; + var currentRange = new TimeRange( + TimeSpan.FromTicks(current.StartPositionTicks).TotalSeconds, + TimeSpan.FromTicks(next.StartPositionTicks).TotalSeconds); + + var baseMessage = string.Format( + CultureInfo.InvariantCulture, + "{0}: Chapter \"{1}\" ({2} - {3})", + episode.Path, + current.Name, + currentRange.Start, + currentRange.End); + + if (currentRange.Duration < minDuration || currentRange.Duration > maxDuration) + { + _logger.LogTrace("{Base}: ignoring (invalid duration)", baseMessage); + continue; + } + + // Regex.IsMatch() is used here in order to allow the runtime to cache the compiled regex + // between function invocations. + var match = Regex.IsMatch( + current.Name, + expression, + RegexOptions.None, + TimeSpan.FromSeconds(1)); + + if (!match) + { + _logger.LogTrace("{Base}: ignoring (does not match regular expression)", baseMessage); + continue; + } + + if (!string.IsNullOrWhiteSpace(next.Name)) + { + // Check for possibility of overlapping keywords + var overlap = Regex.IsMatch( + next.Name, + expression, + RegexOptions.None, + TimeSpan.FromSeconds(1)); + + if (overlap) + { + continue; + } + } + + matchingChapter = new(episode.EpisodeId, currentRange); + _logger.LogTrace("{Base}: okay", baseMessage); + break; + } } return matchingChapter; diff --git a/ConfusedPolarBear.Plugin.IntroSkipper/Configuration/PluginConfiguration.cs b/ConfusedPolarBear.Plugin.IntroSkipper/Configuration/PluginConfiguration.cs index 76e5896..06384eb 100644 --- a/ConfusedPolarBear.Plugin.IntroSkipper/Configuration/PluginConfiguration.cs +++ b/ConfusedPolarBear.Plugin.IntroSkipper/Configuration/PluginConfiguration.cs @@ -103,7 +103,7 @@ public class PluginConfiguration : BasePluginConfiguration /// Gets or sets the regular expression used to detect ending credit chapters. /// public string ChapterAnalyzerEndCreditsPattern { get; set; } = - @"(^|\s)(Credits?|Ending)(\s|$)"; + @"(^|\s)(Credits?|ED|Ending)(\s|$)"; // ===== Playback settings =====