Search credits from the end of episode

This commit is contained in:
TwistedUmbrellaX 2024-03-06 10:15:03 -05:00
parent dfe28b7080
commit c6bb3dbad5
2 changed files with 104 additions and 49 deletions

View File

@ -96,6 +96,11 @@ public class ChapterAnalyzer : IMediaFileAnalyzer
config.MaximumIntroDuration : config.MaximumIntroDuration :
config.MaximumEpisodeCreditsDuration; config.MaximumEpisodeCreditsDuration;
if (chapters.Count == 0)
{
return null;
}
if (mode == AnalysisMode.Credits) if (mode == AnalysisMode.Credits)
{ {
// Since the ending credits chapter may be the last chapter in the file, append a virtual // 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 StartPositionTicks = TimeSpan.FromSeconds(episode.Duration).Ticks
}); });
}
// Check all chapters // Check all chapters in reverse order, skipping the virtual chapter
for (int i = 0; i < chapters.Count - 1; i++) for (int i = chapters.Count - 2; i >= 0; i--)
{
var current = chapters[i];
var next = chapters[i + 1];
if (string.IsNullOrWhiteSpace(current.Name))
{ {
continue; var current = chapters[i];
} var next = chapters[i + 1];
var currentRange = new TimeRange( if (string.IsNullOrWhiteSpace(current.Name))
TimeSpan.FromTicks(current.StartPositionTicks).TotalSeconds, {
TimeSpan.FromTicks(next.StartPositionTicks).TotalSeconds); continue;
}
var baseMessage = string.Format( var currentRange = new TimeRange(
CultureInfo.InvariantCulture, TimeSpan.FromTicks(current.StartPositionTicks).TotalSeconds,
"{0}: Chapter \"{1}\" ({2} - {3})", TimeSpan.FromTicks(next.StartPositionTicks).TotalSeconds);
episode.Path,
current.Name,
currentRange.Start,
currentRange.End);
if (currentRange.Duration < minDuration || currentRange.Duration > maxDuration) var baseMessage = string.Format(
{ CultureInfo.InvariantCulture,
_logger.LogTrace("{Base}: ignoring (invalid duration)", baseMessage); "{0}: Chapter \"{1}\" ({2} - {3})",
continue; episode.Path,
} current.Name,
currentRange.Start,
currentRange.End);
// Regex.IsMatch() is used here in order to allow the runtime to cache the compiled regex if (currentRange.Duration < minDuration || currentRange.Duration > maxDuration)
// between function invocations. {
var match = Regex.IsMatch( _logger.LogTrace("{Base}: ignoring (invalid duration)", baseMessage);
current.Name, continue;
expression, }
RegexOptions.None,
TimeSpan.FromSeconds(1));
if (!match) // Regex.IsMatch() is used here in order to allow the runtime to cache the compiled regex
{ // between function invocations.
_logger.LogTrace("{Base}: ignoring (does not match regular expression)", baseMessage); var match = Regex.IsMatch(
continue; current.Name,
}
if (!string.IsNullOrWhiteSpace(next.Name))
{
// Check for possibility of overlapping keywords
var overlap = Regex.IsMatch(
next.Name,
expression, expression,
RegexOptions.None, RegexOptions.None,
TimeSpan.FromSeconds(1)); 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; continue;
} }
}
matchingChapter = new(episode.EpisodeId, currentRange); var currentRange = new TimeRange(
_logger.LogTrace("{Base}: okay", baseMessage); TimeSpan.FromTicks(current.StartPositionTicks).TotalSeconds,
break; 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; return matchingChapter;

View File

@ -103,7 +103,7 @@ public class PluginConfiguration : BasePluginConfiguration
/// Gets or sets the regular expression used to detect ending credit chapters. /// Gets or sets the regular expression used to detect ending credit chapters.
/// </summary> /// </summary>
public string ChapterAnalyzerEndCreditsPattern { get; set; } = public string ChapterAnalyzerEndCreditsPattern { get; set; } =
@"(^|\s)(Credits?|Ending)(\s|$)"; @"(^|\s)(Credits?|ED|Ending)(\s|$)";
// ===== Playback settings ===== // ===== Playback settings =====