cleanup
This commit is contained in:
parent
5e08381ed5
commit
5beaf35198
@ -124,12 +124,12 @@ public class TestAudioFingerprinting
|
|||||||
|
|
||||||
var expected = new TimeRange[]
|
var expected = new TimeRange[]
|
||||||
{
|
{
|
||||||
new TimeRange(44.6310, 44.8072),
|
new(44.6310, 44.8072),
|
||||||
new TimeRange(53.5905, 53.8070),
|
new(53.5905, 53.8070),
|
||||||
new TimeRange(53.8458, 54.2024),
|
new(53.8458, 54.2024),
|
||||||
new TimeRange(54.2611, 54.5935),
|
new(54.2611, 54.5935),
|
||||||
new TimeRange(54.7098, 54.9293),
|
new(54.7098, 54.9293),
|
||||||
new TimeRange(54.9294, 55.2590),
|
new(54.9294, 55.2590),
|
||||||
};
|
};
|
||||||
|
|
||||||
var range = new TimeRange(0, 60);
|
var range = new TimeRange(0, 60);
|
||||||
|
@ -19,7 +19,7 @@ public class TestBlackFrames
|
|||||||
expected.AddRange(CreateFrameSequence(5, 6));
|
expected.AddRange(CreateFrameSequence(5, 6));
|
||||||
expected.AddRange(CreateFrameSequence(8, 9.96));
|
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++)
|
for (var i = 0; i < expected.Count; i++)
|
||||||
{
|
{
|
||||||
@ -37,7 +37,7 @@ public class TestBlackFrames
|
|||||||
|
|
||||||
var analyzer = CreateBlackFrameAnalyzer();
|
var analyzer = CreateBlackFrameAnalyzer();
|
||||||
|
|
||||||
var episode = queueFile("credits.mp4");
|
var episode = QueueFile("credits.mp4");
|
||||||
episode.Duration = (int)new TimeSpan(0, 5, 30).TotalSeconds;
|
episode.Duration = (int)new TimeSpan(0, 5, 30).TotalSeconds;
|
||||||
|
|
||||||
var result = analyzer.AnalyzeMediaFile(episode, 240, 30, 85);
|
var result = analyzer.AnalyzeMediaFile(episode, 240, 30, 85);
|
||||||
@ -45,7 +45,7 @@ public class TestBlackFrames
|
|||||||
Assert.InRange(result.Start, 300 - range, 300 + range);
|
Assert.InRange(result.Start, 300 - range, 300 + range);
|
||||||
}
|
}
|
||||||
|
|
||||||
private QueuedEpisode queueFile(string path)
|
private static QueuedEpisode QueueFile(string path)
|
||||||
{
|
{
|
||||||
return new()
|
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>();
|
var frames = new List<BlackFrame>();
|
||||||
|
|
||||||
@ -64,10 +64,10 @@ public class TestBlackFrames
|
|||||||
frames.Add(new(100, i));
|
frames.Add(new(100, i));
|
||||||
}
|
}
|
||||||
|
|
||||||
return frames.ToArray();
|
return [.. frames];
|
||||||
}
|
}
|
||||||
|
|
||||||
private BlackFrameAnalyzer CreateBlackFrameAnalyzer()
|
private static BlackFrameAnalyzer CreateBlackFrameAnalyzer()
|
||||||
{
|
{
|
||||||
var logger = new LoggerFactory().CreateLogger<BlackFrameAnalyzer>();
|
var logger = new LoggerFactory().CreateLogger<BlackFrameAnalyzer>();
|
||||||
return new(logger);
|
return new(logger);
|
||||||
|
@ -74,7 +74,7 @@ public class TestChapterAnalyzer
|
|||||||
/// <param name="name">Chapter name.</param>
|
/// <param name="name">Chapter name.</param>
|
||||||
/// <param name="position">Chapter position (in seconds).</param>
|
/// <param name="position">Chapter position (in seconds).</param>
|
||||||
/// <returns>ChapterInfo.</returns>
|
/// <returns>ChapterInfo.</returns>
|
||||||
private ChapterInfo CreateChapter(string name, int position)
|
private static ChapterInfo CreateChapter(string name, int position)
|
||||||
{
|
{
|
||||||
return new()
|
return new()
|
||||||
{
|
{
|
||||||
|
@ -38,7 +38,7 @@ public class TestEdl
|
|||||||
Assert.Equal(edlPath, EdlManager.GetEdlPath(mediaPath));
|
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));
|
return new Segment(Guid.Empty, new TimeRange(start, end));
|
||||||
}
|
}
|
||||||
|
@ -250,7 +250,7 @@ public class ChromaprintAnalyzer : IMediaFileAnalyzer
|
|||||||
/// <param name="rhsId">Second episode id.</param>
|
/// <param name="rhsId">Second episode id.</param>
|
||||||
/// <param name="rhsRanges">Second episode shared timecodes.</param>
|
/// <param name="rhsRanges">Second episode shared timecodes.</param>
|
||||||
/// <returns>Intros for the first and second episodes.</returns>
|
/// <returns>Intros for the first and second episodes.</returns>
|
||||||
private (Segment Lhs, Segment Rhs) GetLongestTimeRange(
|
private static (Segment Lhs, Segment Rhs) GetLongestTimeRange(
|
||||||
Guid lhsId,
|
Guid lhsId,
|
||||||
List<TimeRange> lhsRanges,
|
List<TimeRange> lhsRanges,
|
||||||
Guid rhsId,
|
Guid rhsId,
|
||||||
|
@ -22,7 +22,7 @@ namespace ConfusedPolarBear.Plugin.IntroSkipper;
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Intro skipper plugin. Uses audio analysis to find common sequences of audio shared between episodes.
|
/// Intro skipper plugin. Uses audio analysis to find common sequences of audio shared between episodes.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public partial class Plugin : BasePlugin<PluginConfiguration>, IHasWebPages
|
public class Plugin : BasePlugin<PluginConfiguration>, IHasWebPages
|
||||||
{
|
{
|
||||||
private readonly object _serializationLock = new();
|
private readonly object _serializationLock = new();
|
||||||
private readonly object _introsLock = 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.
|
// 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.
|
// 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);
|
contents = headEnd.Replace(contents, scriptTag + "</head>", 1);
|
||||||
|
|
||||||
// Write the modified file contents
|
// Write the modified file contents
|
||||||
@ -496,7 +496,4 @@ public partial class Plugin : BasePlugin<PluginConfiguration>, IHasWebPages
|
|||||||
|
|
||||||
_logger.LogInformation("Skip intro button successfully added");
|
_logger.LogInformation("Skip intro button successfully added");
|
||||||
}
|
}
|
||||||
|
|
||||||
[GeneratedRegex("</head>", RegexOptions.IgnoreCase)]
|
|
||||||
private static partial Regex HeadRegex();
|
|
||||||
}
|
}
|
||||||
|
@ -57,7 +57,7 @@ public class BaseItemAnalyzerTask
|
|||||||
public void AnalyzeItems(
|
public void AnalyzeItems(
|
||||||
IProgress<double> progress,
|
IProgress<double> progress,
|
||||||
CancellationToken cancellationToken,
|
CancellationToken cancellationToken,
|
||||||
HashSet<Guid>? seasonsToAnalyze = null)
|
IReadOnlyCollection<Guid>? seasonsToAnalyze = null)
|
||||||
{
|
{
|
||||||
var ffmpegValid = FFmpegWrapper.CheckFFmpegVersion();
|
var ffmpegValid = FFmpegWrapper.CheckFFmpegVersion();
|
||||||
// Assert that ffmpeg with chromaprint is installed
|
// Assert that ffmpeg with chromaprint is installed
|
||||||
@ -74,20 +74,12 @@ public class BaseItemAnalyzerTask
|
|||||||
var queue = queueManager.GetMediaItems();
|
var queue = queueManager.GetMediaItems();
|
||||||
|
|
||||||
// Filter the queue based on seasonsToAnalyze
|
// 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))
|
queue = queue.Where(kvp => seasonsToAnalyze.Contains(kvp.Key)).ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
|
||||||
.ToDictionary(kvp => kvp.Key, kvp => kvp.Value).AsReadOnly();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var totalQueued = 0;
|
int totalQueued = queue.Sum(kvp => kvp.Value.Count) * _analysisModes.Count;
|
||||||
foreach (var kvp in queue)
|
|
||||||
{
|
|
||||||
totalQueued += kvp.Value.Count;
|
|
||||||
}
|
|
||||||
|
|
||||||
totalQueued *= _analysisModes.Count;
|
|
||||||
|
|
||||||
if (totalQueued == 0)
|
if (totalQueued == 0)
|
||||||
{
|
{
|
||||||
throw new FingerprintException(
|
throw new FingerprintException(
|
||||||
@ -100,7 +92,6 @@ public class BaseItemAnalyzerTask
|
|||||||
}
|
}
|
||||||
|
|
||||||
var totalProcessed = 0;
|
var totalProcessed = 0;
|
||||||
var modeCount = _analysisModes.Count;
|
|
||||||
var options = new ParallelOptions
|
var options = new ParallelOptions
|
||||||
{
|
{
|
||||||
MaxDegreeOfParallelism = Plugin.Instance.Configuration.MaxParallelism
|
MaxDegreeOfParallelism = Plugin.Instance.Configuration.MaxParallelism
|
||||||
@ -116,32 +107,28 @@ public class BaseItemAnalyzerTask
|
|||||||
season.Value,
|
season.Value,
|
||||||
_analysisModes.Where(m => !Plugin.Instance!.IsIgnored(season.Key, m)).ToList());
|
_analysisModes.Where(m => !Plugin.Instance!.IsIgnored(season.Key, m)).ToList());
|
||||||
|
|
||||||
var episodeCount = episodes.Count;
|
if (episodes.Count == 0)
|
||||||
|
|
||||||
if (episodeCount == 0)
|
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var first = episodes.First();
|
var first = episodes.First();
|
||||||
var requiredModeCount = requiredModes.Count;
|
if (requiredModes.Count == 0)
|
||||||
|
|
||||||
if (requiredModeCount == 0)
|
|
||||||
{
|
{
|
||||||
_logger.LogDebug(
|
_logger.LogDebug(
|
||||||
"All episodes in {Name} season {Season} have already been analyzed",
|
"All episodes in {Name} season {Season} have already been analyzed",
|
||||||
first.SeriesName,
|
first.SeriesName,
|
||||||
first.SeasonNumber);
|
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);
|
progress.Report(totalProcessed * 100 / totalQueued);
|
||||||
|
|
||||||
return;
|
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
|
progress.Report(totalProcessed * 100 / totalQueued); // Partial analysis some modes have already been analyzed
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -223,9 +210,8 @@ public class BaseItemAnalyzerTask
|
|||||||
{
|
{
|
||||||
new ChapterAnalyzer(_loggerFactory.CreateLogger<ChapterAnalyzer>())
|
new ChapterAnalyzer(_loggerFactory.CreateLogger<ChapterAnalyzer>())
|
||||||
};
|
};
|
||||||
if (first.IsAnime)
|
|
||||||
{
|
if (first.IsAnime && Plugin.Instance!.Configuration.UseChromaprint)
|
||||||
if (Plugin.Instance!.Configuration.UseChromaprint)
|
|
||||||
{
|
{
|
||||||
analyzers.Add(new ChromaprintAnalyzer(_loggerFactory.CreateLogger<ChromaprintAnalyzer>()));
|
analyzers.Add(new ChromaprintAnalyzer(_loggerFactory.CreateLogger<ChromaprintAnalyzer>()));
|
||||||
}
|
}
|
||||||
@ -234,19 +220,11 @@ public class BaseItemAnalyzerTask
|
|||||||
{
|
{
|
||||||
analyzers.Add(new BlackFrameAnalyzer(_loggerFactory.CreateLogger<BlackFrameAnalyzer>()));
|
analyzers.Add(new BlackFrameAnalyzer(_loggerFactory.CreateLogger<BlackFrameAnalyzer>()));
|
||||||
}
|
}
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (mode == AnalysisMode.Credits)
|
|
||||||
{
|
|
||||||
analyzers.Add(new BlackFrameAnalyzer(_loggerFactory.CreateLogger<BlackFrameAnalyzer>()));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Plugin.Instance!.Configuration.UseChromaprint)
|
if (!first.IsAnime && Plugin.Instance!.Configuration.UseChromaprint)
|
||||||
{
|
{
|
||||||
analyzers.Add(new ChromaprintAnalyzer(_loggerFactory.CreateLogger<ChromaprintAnalyzer>()));
|
analyzers.Add(new ChromaprintAnalyzer(_loggerFactory.CreateLogger<ChromaprintAnalyzer>()));
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Use each analyzer to find skippable ranges in all media files, removing successfully
|
// Use each analyzer to find skippable ranges in all media files, removing successfully
|
||||||
// analyzed items from the queue.
|
// analyzed items from the queue.
|
||||||
|
@ -2,6 +2,7 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using ConfusedPolarBear.Plugin.IntroSkipper.Configuration;
|
||||||
using ConfusedPolarBear.Plugin.IntroSkipper.Data;
|
using ConfusedPolarBear.Plugin.IntroSkipper.Data;
|
||||||
using ConfusedPolarBear.Plugin.IntroSkipper.ScheduledTasks;
|
using ConfusedPolarBear.Plugin.IntroSkipper.ScheduledTasks;
|
||||||
using MediaBrowser.Controller.Entities.TV;
|
using MediaBrowser.Controller.Entities.TV;
|
||||||
@ -24,6 +25,7 @@ public sealed class Entrypoint : IHostedService, IDisposable
|
|||||||
private readonly ILoggerFactory _loggerFactory;
|
private readonly ILoggerFactory _loggerFactory;
|
||||||
private readonly HashSet<Guid> _seasonsToAnalyze = [];
|
private readonly HashSet<Guid> _seasonsToAnalyze = [];
|
||||||
private readonly Timer _queueTimer;
|
private readonly Timer _queueTimer;
|
||||||
|
private readonly PluginConfiguration _config;
|
||||||
private static readonly ManualResetEventSlim _autoTaskCompletEvent = new(false);
|
private static readonly ManualResetEventSlim _autoTaskCompletEvent = new(false);
|
||||||
private bool _analyzeAgain;
|
private bool _analyzeAgain;
|
||||||
private static CancellationTokenSource? _cancellationTokenSource;
|
private static CancellationTokenSource? _cancellationTokenSource;
|
||||||
@ -46,6 +48,7 @@ public sealed class Entrypoint : IHostedService, IDisposable
|
|||||||
_logger = logger;
|
_logger = logger;
|
||||||
_loggerFactory = loggerFactory;
|
_loggerFactory = loggerFactory;
|
||||||
|
|
||||||
|
_config = Plugin.Instance?.Configuration ?? new PluginConfiguration();
|
||||||
_queueTimer = new Timer(
|
_queueTimer = new Timer(
|
||||||
OnTimerCallback,
|
OnTimerCallback,
|
||||||
null,
|
null,
|
||||||
@ -119,7 +122,7 @@ public sealed class Entrypoint : IHostedService, IDisposable
|
|||||||
private void OnItemAdded(object? sender, ItemChangeEventArgs itemChangeEventArgs)
|
private void OnItemAdded(object? sender, ItemChangeEventArgs itemChangeEventArgs)
|
||||||
{
|
{
|
||||||
// Don't do anything if auto detection is disabled
|
// Don't do anything if auto detection is disabled
|
||||||
if (!Plugin.Instance!.Configuration.AutoDetectIntros && !Plugin.Instance.Configuration.AutoDetectCredits)
|
if (!_config.AutoDetectIntros && !_config.AutoDetectCredits)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -148,7 +151,7 @@ public sealed class Entrypoint : IHostedService, IDisposable
|
|||||||
private void OnItemModified(object? sender, ItemChangeEventArgs itemChangeEventArgs)
|
private void OnItemModified(object? sender, ItemChangeEventArgs itemChangeEventArgs)
|
||||||
{
|
{
|
||||||
// Don't do anything if auto detection is disabled
|
// Don't do anything if auto detection is disabled
|
||||||
if (!Plugin.Instance!.Configuration.AutoDetectIntros && !Plugin.Instance.Configuration.AutoDetectCredits)
|
if (!_config.AutoDetectIntros && !_config.AutoDetectCredits)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -177,7 +180,7 @@ public sealed class Entrypoint : IHostedService, IDisposable
|
|||||||
private void OnLibraryRefresh(object? sender, TaskCompletionEventArgs eventArgs)
|
private void OnLibraryRefresh(object? sender, TaskCompletionEventArgs eventArgs)
|
||||||
{
|
{
|
||||||
// Don't do anything if auto detection is disabled
|
// Don't do anything if auto detection is disabled
|
||||||
if (!Plugin.Instance!.Configuration.AutoDetectIntros && !Plugin.Instance.Configuration.AutoDetectCredits)
|
if (!_config.AutoDetectIntros && !_config.AutoDetectCredits)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -257,21 +260,18 @@ public sealed class Entrypoint : IHostedService, IDisposable
|
|||||||
var modes = new List<AnalysisMode>();
|
var modes = new List<AnalysisMode>();
|
||||||
var tasklogger = _loggerFactory.CreateLogger("DefaultLogger");
|
var tasklogger = _loggerFactory.CreateLogger("DefaultLogger");
|
||||||
|
|
||||||
if (Plugin.Instance!.Configuration.AutoDetectIntros && Plugin.Instance.Configuration.AutoDetectCredits)
|
if (_config.AutoDetectIntros)
|
||||||
{
|
|
||||||
modes.Add(AnalysisMode.Introduction);
|
|
||||||
modes.Add(AnalysisMode.Credits);
|
|
||||||
tasklogger = _loggerFactory.CreateLogger<DetectIntrosCreditsTask>();
|
|
||||||
}
|
|
||||||
else if (Plugin.Instance.Configuration.AutoDetectIntros)
|
|
||||||
{
|
{
|
||||||
modes.Add(AnalysisMode.Introduction);
|
modes.Add(AnalysisMode.Introduction);
|
||||||
tasklogger = _loggerFactory.CreateLogger<DetectIntrosTask>();
|
tasklogger = _loggerFactory.CreateLogger<DetectIntrosTask>();
|
||||||
}
|
}
|
||||||
else if (Plugin.Instance.Configuration.AutoDetectCredits)
|
|
||||||
|
if (_config.AutoDetectCredits)
|
||||||
{
|
{
|
||||||
modes.Add(AnalysisMode.Credits);
|
modes.Add(AnalysisMode.Credits);
|
||||||
tasklogger = _loggerFactory.CreateLogger<DetectCreditsTask>();
|
tasklogger = modes.Count == 2
|
||||||
|
? _loggerFactory.CreateLogger<DetectIntrosCreditsTask>()
|
||||||
|
: _loggerFactory.CreateLogger<DetectCreditsTask>();
|
||||||
}
|
}
|
||||||
|
|
||||||
var baseCreditAnalyzer = new BaseItemAnalyzerTask(
|
var baseCreditAnalyzer = new BaseItemAnalyzerTask(
|
||||||
|
Loading…
x
Reference in New Issue
Block a user