This commit is contained in:
rlauu 2024-09-25 17:23:25 +02:00
parent 5e08381ed5
commit 5beaf35198
8 changed files with 49 additions and 74 deletions

View File

@ -124,12 +124,12 @@ public class TestAudioFingerprinting
var expected = new TimeRange[]
{
new TimeRange(44.6310, 44.8072),
new TimeRange(53.5905, 53.8070),
new TimeRange(53.8458, 54.2024),
new TimeRange(54.2611, 54.5935),
new TimeRange(54.7098, 54.9293),
new TimeRange(54.9294, 55.2590),
new(44.6310, 44.8072),
new(53.5905, 53.8070),
new(53.8458, 54.2024),
new(54.2611, 54.5935),
new(54.7098, 54.9293),
new(54.9294, 55.2590),
};
var range = new TimeRange(0, 60);

View File

@ -19,7 +19,7 @@ public class TestBlackFrames
expected.AddRange(CreateFrameSequence(5, 6));
expected.AddRange(CreateFrameSequence(8, 9.96));
var actual = FFmpegWrapper.DetectBlackFrames(queueFile("rainbow.mp4"), new(0, 10), 85);
var actual = FFmpegWrapper.DetectBlackFrames(QueueFile("rainbow.mp4"), new(0, 10), 85);
for (var i = 0; i < expected.Count; i++)
{
@ -37,7 +37,7 @@ public class TestBlackFrames
var analyzer = CreateBlackFrameAnalyzer();
var episode = queueFile("credits.mp4");
var episode = QueueFile("credits.mp4");
episode.Duration = (int)new TimeSpan(0, 5, 30).TotalSeconds;
var result = analyzer.AnalyzeMediaFile(episode, 240, 30, 85);
@ -45,7 +45,7 @@ public class TestBlackFrames
Assert.InRange(result.Start, 300 - range, 300 + range);
}
private QueuedEpisode queueFile(string path)
private static QueuedEpisode QueueFile(string path)
{
return new()
{
@ -55,7 +55,7 @@ public class TestBlackFrames
};
}
private BlackFrame[] CreateFrameSequence(double start, double end)
private static BlackFrame[] CreateFrameSequence(double start, double end)
{
var frames = new List<BlackFrame>();
@ -64,10 +64,10 @@ public class TestBlackFrames
frames.Add(new(100, i));
}
return frames.ToArray();
return [.. frames];
}
private BlackFrameAnalyzer CreateBlackFrameAnalyzer()
private static BlackFrameAnalyzer CreateBlackFrameAnalyzer()
{
var logger = new LoggerFactory().CreateLogger<BlackFrameAnalyzer>();
return new(logger);

View File

@ -74,7 +74,7 @@ public class TestChapterAnalyzer
/// <param name="name">Chapter name.</param>
/// <param name="position">Chapter position (in seconds).</param>
/// <returns>ChapterInfo.</returns>
private ChapterInfo CreateChapter(string name, int position)
private static ChapterInfo CreateChapter(string name, int position)
{
return new()
{

View File

@ -38,7 +38,7 @@ public class TestEdl
Assert.Equal(edlPath, EdlManager.GetEdlPath(mediaPath));
}
private Segment MakeIntro(double start, double end)
private static Segment MakeIntro(double start, double end)
{
return new Segment(Guid.Empty, new TimeRange(start, end));
}

View File

@ -250,7 +250,7 @@ public class ChromaprintAnalyzer : IMediaFileAnalyzer
/// <param name="rhsId">Second episode id.</param>
/// <param name="rhsRanges">Second episode shared timecodes.</param>
/// <returns>Intros for the first and second episodes.</returns>
private (Segment Lhs, Segment Rhs) GetLongestTimeRange(
private static (Segment Lhs, Segment Rhs) GetLongestTimeRange(
Guid lhsId,
List<TimeRange> lhsRanges,
Guid rhsId,

View File

@ -22,7 +22,7 @@ namespace ConfusedPolarBear.Plugin.IntroSkipper;
/// <summary>
/// Intro skipper plugin. Uses audio analysis to find common sequences of audio shared between episodes.
/// </summary>
public partial class Plugin : BasePlugin<PluginConfiguration>, IHasWebPages
public class Plugin : BasePlugin<PluginConfiguration>, IHasWebPages
{
private readonly object _serializationLock = new();
private readonly object _introsLock = new();
@ -488,7 +488,7 @@ public partial class Plugin : BasePlugin<PluginConfiguration>, IHasWebPages
// Inject a link to the script at the end of the <head> section.
// A regex is used here to ensure the replacement is only done once.
Regex headEnd = HeadRegex();
Regex headEnd = new Regex(@"</head>", RegexOptions.IgnoreCase, TimeSpan.FromSeconds(1));
contents = headEnd.Replace(contents, scriptTag + "</head>", 1);
// Write the modified file contents
@ -496,7 +496,4 @@ public partial class Plugin : BasePlugin<PluginConfiguration>, IHasWebPages
_logger.LogInformation("Skip intro button successfully added");
}
[GeneratedRegex("</head>", RegexOptions.IgnoreCase)]
private static partial Regex HeadRegex();
}

View File

@ -57,7 +57,7 @@ public class BaseItemAnalyzerTask
public void AnalyzeItems(
IProgress<double> progress,
CancellationToken cancellationToken,
HashSet<Guid>? seasonsToAnalyze = null)
IReadOnlyCollection<Guid>? seasonsToAnalyze = null)
{
var ffmpegValid = FFmpegWrapper.CheckFFmpegVersion();
// Assert that ffmpeg with chromaprint is installed
@ -74,20 +74,12 @@ public class BaseItemAnalyzerTask
var queue = queueManager.GetMediaItems();
// Filter the queue based on seasonsToAnalyze
if (seasonsToAnalyze != null && seasonsToAnalyze.Count > 0)
if (seasonsToAnalyze is { Count: > 0 })
{
queue = queue.Where(kvp => seasonsToAnalyze.Contains(kvp.Key))
.ToDictionary(kvp => kvp.Key, kvp => kvp.Value).AsReadOnly();
queue = queue.Where(kvp => seasonsToAnalyze.Contains(kvp.Key)).ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
}
var totalQueued = 0;
foreach (var kvp in queue)
{
totalQueued += kvp.Value.Count;
}
totalQueued *= _analysisModes.Count;
int totalQueued = queue.Sum(kvp => kvp.Value.Count) * _analysisModes.Count;
if (totalQueued == 0)
{
throw new FingerprintException(
@ -100,7 +92,6 @@ public class BaseItemAnalyzerTask
}
var totalProcessed = 0;
var modeCount = _analysisModes.Count;
var options = new ParallelOptions
{
MaxDegreeOfParallelism = Plugin.Instance.Configuration.MaxParallelism
@ -116,32 +107,28 @@ public class BaseItemAnalyzerTask
season.Value,
_analysisModes.Where(m => !Plugin.Instance!.IsIgnored(season.Key, m)).ToList());
var episodeCount = episodes.Count;
if (episodeCount == 0)
if (episodes.Count == 0)
{
return;
}
var first = episodes.First();
var requiredModeCount = requiredModes.Count;
if (requiredModeCount == 0)
if (requiredModes.Count == 0)
{
_logger.LogDebug(
"All episodes in {Name} season {Season} have already been analyzed",
first.SeriesName,
first.SeasonNumber);
Interlocked.Add(ref totalProcessed, episodeCount * modeCount); // Update total Processed directly
Interlocked.Add(ref totalProcessed, episodes.Count * _analysisModes.Count); // Update total Processed directly
progress.Report(totalProcessed * 100 / totalQueued);
return;
}
if (modeCount != requiredModeCount)
if (_analysisModes.Count != requiredModes.Count)
{
Interlocked.Add(ref totalProcessed, episodeCount);
Interlocked.Add(ref totalProcessed, episodes.Count);
progress.Report(totalProcessed * 100 / totalQueued); // Partial analysis some modes have already been analyzed
}
@ -223,29 +210,20 @@ public class BaseItemAnalyzerTask
{
new ChapterAnalyzer(_loggerFactory.CreateLogger<ChapterAnalyzer>())
};
if (first.IsAnime)
{
if (Plugin.Instance!.Configuration.UseChromaprint)
{
analyzers.Add(new ChromaprintAnalyzer(_loggerFactory.CreateLogger<ChromaprintAnalyzer>()));
}
if (mode == AnalysisMode.Credits)
{
analyzers.Add(new BlackFrameAnalyzer(_loggerFactory.CreateLogger<BlackFrameAnalyzer>()));
}
if (first.IsAnime && Plugin.Instance!.Configuration.UseChromaprint)
{
analyzers.Add(new ChromaprintAnalyzer(_loggerFactory.CreateLogger<ChromaprintAnalyzer>()));
}
else
{
if (mode == AnalysisMode.Credits)
{
analyzers.Add(new BlackFrameAnalyzer(_loggerFactory.CreateLogger<BlackFrameAnalyzer>()));
}
if (Plugin.Instance!.Configuration.UseChromaprint)
{
analyzers.Add(new ChromaprintAnalyzer(_loggerFactory.CreateLogger<ChromaprintAnalyzer>()));
}
if (mode == AnalysisMode.Credits)
{
analyzers.Add(new BlackFrameAnalyzer(_loggerFactory.CreateLogger<BlackFrameAnalyzer>()));
}
if (!first.IsAnime && Plugin.Instance!.Configuration.UseChromaprint)
{
analyzers.Add(new ChromaprintAnalyzer(_loggerFactory.CreateLogger<ChromaprintAnalyzer>()));
}
// Use each analyzer to find skippable ranges in all media files, removing successfully

View File

@ -2,6 +2,7 @@ using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using ConfusedPolarBear.Plugin.IntroSkipper.Configuration;
using ConfusedPolarBear.Plugin.IntroSkipper.Data;
using ConfusedPolarBear.Plugin.IntroSkipper.ScheduledTasks;
using MediaBrowser.Controller.Entities.TV;
@ -24,6 +25,7 @@ public sealed class Entrypoint : IHostedService, IDisposable
private readonly ILoggerFactory _loggerFactory;
private readonly HashSet<Guid> _seasonsToAnalyze = [];
private readonly Timer _queueTimer;
private readonly PluginConfiguration _config;
private static readonly ManualResetEventSlim _autoTaskCompletEvent = new(false);
private bool _analyzeAgain;
private static CancellationTokenSource? _cancellationTokenSource;
@ -46,6 +48,7 @@ public sealed class Entrypoint : IHostedService, IDisposable
_logger = logger;
_loggerFactory = loggerFactory;
_config = Plugin.Instance?.Configuration ?? new PluginConfiguration();
_queueTimer = new Timer(
OnTimerCallback,
null,
@ -119,7 +122,7 @@ public sealed class Entrypoint : IHostedService, IDisposable
private void OnItemAdded(object? sender, ItemChangeEventArgs itemChangeEventArgs)
{
// Don't do anything if auto detection is disabled
if (!Plugin.Instance!.Configuration.AutoDetectIntros && !Plugin.Instance.Configuration.AutoDetectCredits)
if (!_config.AutoDetectIntros && !_config.AutoDetectCredits)
{
return;
}
@ -148,7 +151,7 @@ public sealed class Entrypoint : IHostedService, IDisposable
private void OnItemModified(object? sender, ItemChangeEventArgs itemChangeEventArgs)
{
// Don't do anything if auto detection is disabled
if (!Plugin.Instance!.Configuration.AutoDetectIntros && !Plugin.Instance.Configuration.AutoDetectCredits)
if (!_config.AutoDetectIntros && !_config.AutoDetectCredits)
{
return;
}
@ -177,7 +180,7 @@ public sealed class Entrypoint : IHostedService, IDisposable
private void OnLibraryRefresh(object? sender, TaskCompletionEventArgs eventArgs)
{
// Don't do anything if auto detection is disabled
if (!Plugin.Instance!.Configuration.AutoDetectIntros && !Plugin.Instance.Configuration.AutoDetectCredits)
if (!_config.AutoDetectIntros && !_config.AutoDetectCredits)
{
return;
}
@ -257,21 +260,18 @@ public sealed class Entrypoint : IHostedService, IDisposable
var modes = new List<AnalysisMode>();
var tasklogger = _loggerFactory.CreateLogger("DefaultLogger");
if (Plugin.Instance!.Configuration.AutoDetectIntros && Plugin.Instance.Configuration.AutoDetectCredits)
{
modes.Add(AnalysisMode.Introduction);
modes.Add(AnalysisMode.Credits);
tasklogger = _loggerFactory.CreateLogger<DetectIntrosCreditsTask>();
}
else if (Plugin.Instance.Configuration.AutoDetectIntros)
if (_config.AutoDetectIntros)
{
modes.Add(AnalysisMode.Introduction);
tasklogger = _loggerFactory.CreateLogger<DetectIntrosTask>();
}
else if (Plugin.Instance.Configuration.AutoDetectCredits)
if (_config.AutoDetectCredits)
{
modes.Add(AnalysisMode.Credits);
tasklogger = _loggerFactory.CreateLogger<DetectCreditsTask>();
tasklogger = modes.Count == 2
? _loggerFactory.CreateLogger<DetectIntrosCreditsTask>()
: _loggerFactory.CreateLogger<DetectCreditsTask>();
}
var baseCreditAnalyzer = new BaseItemAnalyzerTask(