diff --git a/ConfusedPolarBear.Plugin.IntroSkipper.Tests/TestChapterAnalyzer.cs b/ConfusedPolarBear.Plugin.IntroSkipper.Tests/TestChapterAnalyzer.cs index e1da0ab..96feb8e 100644 --- a/ConfusedPolarBear.Plugin.IntroSkipper.Tests/TestChapterAnalyzer.cs +++ b/ConfusedPolarBear.Plugin.IntroSkipper.Tests/TestChapterAnalyzer.cs @@ -51,7 +51,7 @@ public void TestEndCreditsExpression(string chapterName) config.ChapterAnalyzerIntroductionPattern : config.ChapterAnalyzerEndCreditsPattern; - return analyzer.FindMatchingChapter(Guid.Empty, 2000, chapters, expression, mode); + return analyzer.FindMatchingChapter(new() { Duration = 2000 }, chapters, expression, mode); } private Collection CreateChapters(string name, AnalysisMode mode) diff --git a/ConfusedPolarBear.Plugin.IntroSkipper/Analyzers/ChapterAnalyzer.cs b/ConfusedPolarBear.Plugin.IntroSkipper/Analyzers/ChapterAnalyzer.cs index 86efd53..73ec9aa 100644 --- a/ConfusedPolarBear.Plugin.IntroSkipper/Analyzers/ChapterAnalyzer.cs +++ b/ConfusedPolarBear.Plugin.IntroSkipper/Analyzers/ChapterAnalyzer.cs @@ -3,6 +3,7 @@ namespace ConfusedPolarBear.Plugin.IntroSkipper; using System; using System.Collections.Generic; using System.Collections.ObjectModel; +using System.Globalization; using System.Text.RegularExpressions; using System.Threading; using Microsoft.Extensions.Logging; @@ -30,7 +31,9 @@ public ReadOnlyCollection AnalyzeMediaFiles( AnalysisMode mode, CancellationToken cancellationToken) { + var unsuccessful = new List(); var skippableRanges = new Dictionary(); + var expression = mode == AnalysisMode.Introduction ? Plugin.Instance!.Configuration.ChapterAnalyzerIntroductionPattern : Plugin.Instance!.Configuration.ChapterAnalyzerEndCreditsPattern; @@ -43,14 +46,14 @@ public ReadOnlyCollection AnalyzeMediaFiles( } var skipRange = FindMatchingChapter( - episode.EpisodeId, - episode.Duration, + episode, new(Plugin.Instance!.GetChapters(episode.EpisodeId)), expression, mode); if (skipRange is null) { + unsuccessful.Add(episode); continue; } @@ -59,22 +62,20 @@ public ReadOnlyCollection AnalyzeMediaFiles( Plugin.Instance!.UpdateTimestamps(skippableRanges, mode); - return analysisQueue; + return unsuccessful.AsReadOnly(); } /// /// Searches a list of chapter names for one that matches the provided regular expression. /// Only public to allow for unit testing. /// - /// Item id. - /// Duration of media file in seconds. + /// Episode. /// Media item chapters. /// Regular expression pattern. /// Analysis mode. /// Intro object containing skippable time range, or null if no chapter matched. public Intro? FindMatchingChapter( - Guid id, - int duration, + QueuedEpisode episode, Collection chapters, string expression, AnalysisMode mode) @@ -82,6 +83,7 @@ public ReadOnlyCollection AnalyzeMediaFiles( Intro? matchingChapter = null; var config = Plugin.Instance?.Configuration ?? new Configuration.PluginConfiguration(); + var minDuration = config.MinimumIntroDuration; int maxDuration = mode == AnalysisMode.Introduction ? config.MaximumIntroDuration : @@ -91,28 +93,38 @@ public ReadOnlyCollection AnalyzeMediaFiles( { // Since the ending credits chapter may be the last chapter in the file, append a virtual // chapter at the very end of the file. - chapters.Add(new ChapterInfo() + chapters.Add(new() { - StartPositionTicks = TimeSpan.FromSeconds(duration).Ticks + StartPositionTicks = TimeSpan.FromSeconds(episode.Duration).Ticks }); } // Check all chapters for (int i = 0; i < chapters.Count - 1; i++) { - // Calculate chapter position and duration var current = chapters[i]; var next = chapters[i + 1]; + if (string.IsNullOrWhiteSpace(current.Name)) + { + continue; + } + var currentRange = new TimeRange( TimeSpan.FromTicks(current.StartPositionTicks).TotalSeconds, TimeSpan.FromTicks(next.StartPositionTicks).TotalSeconds); - // Skip chapters with that don't have a name or are too short/long - if (string.IsNullOrEmpty(current.Name) || - currentRange.Duration < minDuration || - currentRange.Duration > maxDuration) + 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; } @@ -126,10 +138,12 @@ public ReadOnlyCollection AnalyzeMediaFiles( if (!match) { + _logger.LogTrace("{Base}: ignoring (does not match regular expression)", baseMessage); continue; } - matchingChapter = new Intro(id, currentRange); + matchingChapter = new(episode.EpisodeId, currentRange); + _logger.LogTrace("{Base}: okay", baseMessage); break; } diff --git a/ConfusedPolarBear.Plugin.IntroSkipper/ScheduledTasks/DetectIntroductionsTask.cs b/ConfusedPolarBear.Plugin.IntroSkipper/ScheduledTasks/DetectIntroductionsTask.cs index 240feb7..83df267 100644 --- a/ConfusedPolarBear.Plugin.IntroSkipper/ScheduledTasks/DetectIntroductionsTask.cs +++ b/ConfusedPolarBear.Plugin.IntroSkipper/ScheduledTasks/DetectIntroductionsTask.cs @@ -254,7 +254,7 @@ private int AnalyzeSeason( // Chapter analyzer var chapter = new ChapterAnalyzer(_loggerFactory.CreateLogger()); - chapter.AnalyzeMediaFiles(episodes, AnalysisMode.Introduction, cancellationToken); + episodes = chapter.AnalyzeMediaFiles(episodes, AnalysisMode.Introduction, cancellationToken); // Analyze the season with Chromaprint var chromaprint = new ChromaprintAnalyzer(_loggerFactory.CreateLogger());