apply auto-fixes from VS Code (#283)

Co-authored-by: rlauu <46294892+rlauu@users.noreply.github.com>
This commit is contained in:
rlauuzo 2024-09-10 18:08:42 +02:00 committed by GitHub
parent d428efb1f2
commit 60c735282e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
25 changed files with 181 additions and 217 deletions

View File

@ -14,7 +14,7 @@ namespace ConfusedPolarBear.Plugin.IntroSkipper;
public class AnalyzerHelper public class AnalyzerHelper
{ {
private readonly ILogger _logger; private readonly ILogger _logger;
private readonly double silenceDetectionMinimumDuration; private readonly double _silenceDetectionMinimumDuration;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="AnalyzerHelper"/> class. /// Initializes a new instance of the <see cref="AnalyzerHelper"/> class.
@ -23,7 +23,7 @@ public class AnalyzerHelper
public AnalyzerHelper(ILogger logger) public AnalyzerHelper(ILogger logger)
{ {
var config = Plugin.Instance?.Configuration ?? new PluginConfiguration(); var config = Plugin.Instance?.Configuration ?? new PluginConfiguration();
silenceDetectionMinimumDuration = config.SilenceDetectionMinimumDuration; _silenceDetectionMinimumDuration = config.SilenceDetectionMinimumDuration;
_logger = logger; _logger = logger;
} }
@ -126,7 +126,7 @@ public class AnalyzerHelper
private bool IsValidSilenceForIntroAdjustment(TimeRange silenceRange, TimeRange originalIntroEnd, Intro adjustedIntro) private bool IsValidSilenceForIntroAdjustment(TimeRange silenceRange, TimeRange originalIntroEnd, Intro adjustedIntro)
{ {
return originalIntroEnd.Intersects(silenceRange) && return originalIntroEnd.Intersects(silenceRange) &&
silenceRange.Duration >= silenceDetectionMinimumDuration && silenceRange.Duration >= _silenceDetectionMinimumDuration &&
silenceRange.Start >= adjustedIntro.IntroStart; silenceRange.Start >= adjustedIntro.IntroStart;
} }
} }

View File

@ -19,11 +19,11 @@ public class BlackFrameAnalyzer : IMediaFileAnalyzer
private readonly ILogger<BlackFrameAnalyzer> _logger; private readonly ILogger<BlackFrameAnalyzer> _logger;
private int minimumCreditsDuration; private readonly int _minimumCreditsDuration;
private int maximumCreditsDuration; private readonly int _maximumCreditsDuration;
private int blackFrameMinimumPercentage; private readonly int _blackFrameMinimumPercentage;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="BlackFrameAnalyzer"/> class. /// Initializes a new instance of the <see cref="BlackFrameAnalyzer"/> class.
@ -32,9 +32,9 @@ public class BlackFrameAnalyzer : IMediaFileAnalyzer
public BlackFrameAnalyzer(ILogger<BlackFrameAnalyzer> logger) public BlackFrameAnalyzer(ILogger<BlackFrameAnalyzer> logger)
{ {
var config = Plugin.Instance?.Configuration ?? new PluginConfiguration(); var config = Plugin.Instance?.Configuration ?? new PluginConfiguration();
minimumCreditsDuration = config.MinimumCreditsDuration; _minimumCreditsDuration = config.MinimumCreditsDuration;
maximumCreditsDuration = 2 * config.MaximumCreditsDuration; _maximumCreditsDuration = 2 * config.MaximumCreditsDuration;
blackFrameMinimumPercentage = config.BlackFrameMinimumPercentage; _blackFrameMinimumPercentage = config.BlackFrameMinimumPercentage;
_logger = logger; _logger = logger;
} }
@ -56,9 +56,9 @@ public class BlackFrameAnalyzer : IMediaFileAnalyzer
bool isFirstEpisode = true; bool isFirstEpisode = true;
double searchStart = minimumCreditsDuration; double searchStart = _minimumCreditsDuration;
var searchDistance = 2 * minimumCreditsDuration; var searchDistance = 2 * _minimumCreditsDuration;
foreach (var episode in episodeAnalysisQueue.Where(e => !e.State.IsAnalyzed(mode))) foreach (var episode in episodeAnalysisQueue.Where(e => !e.State.IsAnalyzed(mode)))
{ {
@ -73,7 +73,7 @@ public class BlackFrameAnalyzer : IMediaFileAnalyzer
var scanTime = episode.Duration - searchStart; var scanTime = episode.Duration - searchStart;
var tr = new TimeRange(scanTime - 0.5, scanTime); // Short search range since accuracy isn't important here. var tr = new TimeRange(scanTime - 0.5, scanTime); // Short search range since accuracy isn't important here.
var frames = FFmpegWrapper.DetectBlackFrames(episode, tr, blackFrameMinimumPercentage); var frames = FFmpegWrapper.DetectBlackFrames(episode, tr, _blackFrameMinimumPercentage);
while (frames.Length > 0) // While black frames are found increase searchStart while (frames.Length > 0) // While black frames are found increase searchStart
{ {
@ -82,16 +82,16 @@ public class BlackFrameAnalyzer : IMediaFileAnalyzer
scanTime = episode.Duration - searchStart; scanTime = episode.Duration - searchStart;
tr = new TimeRange(scanTime - 0.5, scanTime); tr = new TimeRange(scanTime - 0.5, scanTime);
frames = FFmpegWrapper.DetectBlackFrames(episode, tr, blackFrameMinimumPercentage); frames = FFmpegWrapper.DetectBlackFrames(episode, tr, _blackFrameMinimumPercentage);
if (searchStart > maximumCreditsDuration) if (searchStart > _maximumCreditsDuration)
{ {
searchStart = maximumCreditsDuration; searchStart = _maximumCreditsDuration;
break; break;
} }
} }
if (searchStart == minimumCreditsDuration) // Skip if no black frames were found if (searchStart == _minimumCreditsDuration) // Skip if no black frames were found
{ {
continue; continue;
} }
@ -103,12 +103,12 @@ public class BlackFrameAnalyzer : IMediaFileAnalyzer
episode, episode,
searchStart, searchStart,
searchDistance, searchDistance,
blackFrameMinimumPercentage); _blackFrameMinimumPercentage);
if (credit is null) if (credit is null)
{ {
// If no credits were found, reset the first-episode search logic for the next episode in the sequence. // If no credits were found, reset the first-episode search logic for the next episode in the sequence.
searchStart = minimumCreditsDuration; searchStart = _minimumCreditsDuration;
isFirstEpisode = true; isFirstEpisode = true;
continue; continue;
} }
@ -139,7 +139,7 @@ public class BlackFrameAnalyzer : IMediaFileAnalyzer
{ {
// Start by analyzing the last N minutes of the file. // Start by analyzing the last N minutes of the file.
var upperLimit = searchStart; var upperLimit = searchStart;
var lowerLimit = Math.Max(searchStart - searchDistance, minimumCreditsDuration); var lowerLimit = Math.Max(searchStart - searchDistance, _minimumCreditsDuration);
var start = TimeSpan.FromSeconds(upperLimit); var start = TimeSpan.FromSeconds(upperLimit);
var end = TimeSpan.FromSeconds(lowerLimit); var end = TimeSpan.FromSeconds(lowerLimit);
var firstFrameTime = 0.0; var firstFrameTime = 0.0;
@ -176,7 +176,7 @@ public class BlackFrameAnalyzer : IMediaFileAnalyzer
if (midpoint - TimeSpan.FromSeconds(lowerLimit) < _maximumError) if (midpoint - TimeSpan.FromSeconds(lowerLimit) < _maximumError)
{ {
lowerLimit = Math.Max(lowerLimit - (0.5 * searchDistance), minimumCreditsDuration); lowerLimit = Math.Max(lowerLimit - (0.5 * searchDistance), _minimumCreditsDuration);
// Reset end for a new search with the increased duration // Reset end for a new search with the increased duration
end = TimeSpan.FromSeconds(lowerLimit); end = TimeSpan.FromSeconds(lowerLimit);
@ -190,7 +190,7 @@ public class BlackFrameAnalyzer : IMediaFileAnalyzer
if (TimeSpan.FromSeconds(upperLimit) - midpoint < _maximumError) if (TimeSpan.FromSeconds(upperLimit) - midpoint < _maximumError)
{ {
upperLimit = Math.Min(upperLimit + (0.5 * searchDistance), maximumCreditsDuration); upperLimit = Math.Min(upperLimit + (0.5 * searchDistance), _maximumCreditsDuration);
// Reset start for a new search with the increased duration // Reset start for a new search with the increased duration
start = TimeSpan.FromSeconds(upperLimit); start = TimeSpan.FromSeconds(upperLimit);

View File

@ -22,15 +22,15 @@ public class ChromaprintAnalyzer : IMediaFileAnalyzer
/// </summary> /// </summary>
private const double SamplesToSeconds = 0.1238; private const double SamplesToSeconds = 0.1238;
private int minimumIntroDuration; private readonly int _minimumIntroDuration;
private int maximumDifferences; private readonly int _maximumDifferences;
private int invertedIndexShift; private readonly int _invertedIndexShift;
private double maximumTimeSkip; private readonly double _maximumTimeSkip;
private ILogger<ChromaprintAnalyzer> _logger; private readonly ILogger<ChromaprintAnalyzer> _logger;
private AnalysisMode _analysisMode; private AnalysisMode _analysisMode;
@ -41,10 +41,10 @@ public class ChromaprintAnalyzer : IMediaFileAnalyzer
public ChromaprintAnalyzer(ILogger<ChromaprintAnalyzer> logger) public ChromaprintAnalyzer(ILogger<ChromaprintAnalyzer> logger)
{ {
var config = Plugin.Instance?.Configuration ?? new PluginConfiguration(); var config = Plugin.Instance?.Configuration ?? new PluginConfiguration();
maximumDifferences = config.MaximumFingerprintPointDifferences; _maximumDifferences = config.MaximumFingerprintPointDifferences;
invertedIndexShift = config.InvertedIndexShift; _invertedIndexShift = config.InvertedIndexShift;
maximumTimeSkip = config.MaximumTimeSkip; _maximumTimeSkip = config.MaximumTimeSkip;
minimumIntroDuration = config.MinimumIntroDuration; _minimumIntroDuration = config.MinimumIntroDuration;
_logger = logger; _logger = logger;
} }
@ -87,7 +87,7 @@ public class ChromaprintAnalyzer : IMediaFileAnalyzer
.Where((episode, index) => Math.Abs(index - indexInAnalysisQueue) <= 1 && index != indexInAnalysisQueue)); .Where((episode, index) => Math.Abs(index - indexInAnalysisQueue) <= 1 && index != indexInAnalysisQueue));
} }
seasonIntros = episodesWithFingerprint.Where(e => e.State.IsAnalyzed(mode)).ToDictionary(e => e.EpisodeId, e => Plugin.Instance!.GetIntroByMode(e.EpisodeId, mode)); seasonIntros = episodesWithFingerprint.Where(e => e.State.IsAnalyzed(mode)).ToDictionary(e => e.EpisodeId, e => Plugin.GetIntroByMode(e.EpisodeId, mode));
// Compute fingerprints for all episodes in the season // Compute fingerprints for all episodes in the season
foreach (var episode in episodesWithFingerprint) foreach (var episode in episodesWithFingerprint)
@ -113,7 +113,7 @@ public class ChromaprintAnalyzer : IMediaFileAnalyzer
WarningManager.SetFlag(PluginWarning.InvalidChromaprintFingerprint); WarningManager.SetFlag(PluginWarning.InvalidChromaprintFingerprint);
// Fallback to an empty fingerprint on any error // Fallback to an empty fingerprint on any error
fingerprintCache[episode.EpisodeId] = Array.Empty<uint>(); fingerprintCache[episode.EpisodeId] = [];
} }
} }
@ -203,7 +203,7 @@ public class ChromaprintAnalyzer : IMediaFileAnalyzer
// Adjust all introduction times. // Adjust all introduction times.
var analyzerHelper = new AnalyzerHelper(_logger); var analyzerHelper = new AnalyzerHelper(_logger);
seasonIntros = analyzerHelper.AdjustIntroTimes(analysisQueue, seasonIntros, this._analysisMode); seasonIntros = analyzerHelper.AdjustIntroTimes(analysisQueue, seasonIntros, _analysisMode);
Plugin.Instance!.UpdateTimestamps(seasonIntros, _analysisMode); Plugin.Instance!.UpdateTimestamps(seasonIntros, _analysisMode);
@ -307,7 +307,7 @@ public class ChromaprintAnalyzer : IMediaFileAnalyzer
{ {
var originalPoint = kvp.Key; var originalPoint = kvp.Key;
for (var i = -1 * invertedIndexShift; i <= invertedIndexShift; i++) for (var i = -1 * _invertedIndexShift; i <= _invertedIndexShift; i++)
{ {
var modifiedPoint = (uint)(originalPoint + i); var modifiedPoint = (uint)(originalPoint + i);
@ -372,7 +372,7 @@ public class ChromaprintAnalyzer : IMediaFileAnalyzer
var diff = lhs[lhsPosition] ^ rhs[rhsPosition]; var diff = lhs[lhsPosition] ^ rhs[rhsPosition];
// If the difference between the samples is small, flag both times as similar. // If the difference between the samples is small, flag both times as similar.
if (CountBits(diff) > maximumDifferences) if (CountBits(diff) > _maximumDifferences)
{ {
continue; continue;
} }
@ -389,14 +389,14 @@ public class ChromaprintAnalyzer : IMediaFileAnalyzer
rhsTimes.Add(double.MaxValue); rhsTimes.Add(double.MaxValue);
// Now that both fingerprints have been compared at this shift, see if there's a contiguous time range. // Now that both fingerprints have been compared at this shift, see if there's a contiguous time range.
var lContiguous = TimeRangeHelpers.FindContiguous(lhsTimes.ToArray(), maximumTimeSkip); var lContiguous = TimeRangeHelpers.FindContiguous(lhsTimes.ToArray(), _maximumTimeSkip);
if (lContiguous is null || lContiguous.Duration < minimumIntroDuration) if (lContiguous is null || lContiguous.Duration < _minimumIntroDuration)
{ {
return (new TimeRange(), new TimeRange()); return (new TimeRange(), new TimeRange());
} }
// Since LHS had a contiguous time range, RHS must have one also. // Since LHS had a contiguous time range, RHS must have one also.
var rContiguous = TimeRangeHelpers.FindContiguous(rhsTimes.ToArray(), maximumTimeSkip)!; var rContiguous = TimeRangeHelpers.FindContiguous(rhsTimes.ToArray(), _maximumTimeSkip)!;
return (lContiguous, rContiguous); return (lContiguous, rContiguous);
} }

View File

@ -10,7 +10,7 @@ namespace ConfusedPolarBear.Plugin.IntroSkipper.Analyzers;
/// </summary> /// </summary>
public class SegmentAnalyzer : IMediaFileAnalyzer public class SegmentAnalyzer : IMediaFileAnalyzer
{ {
private ILogger<SegmentAnalyzer> _logger; private readonly ILogger<SegmentAnalyzer> _logger;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="SegmentAnalyzer"/> class. /// Initializes a new instance of the <see cref="SegmentAnalyzer"/> class.

View File

@ -71,7 +71,7 @@ public class PluginConfiguration : BasePluginConfiguration
/// By default, EDL files are only written for a season if the season had at least one newly analyzed episode. /// By default, EDL files are only written for a season if the season had at least one newly analyzed episode.
/// If this is set, all EDL files will be regenerated and overwrite any existing EDL file. /// If this is set, all EDL files will be regenerated and overwrite any existing EDL file.
/// </summary> /// </summary>
public bool RegenerateEdlFiles { get; set; } = false; public bool RegenerateEdlFiles { get; set; }
// ===== Custom analysis settings ===== // ===== Custom analysis settings =====
@ -172,7 +172,7 @@ public class PluginConfiguration : BasePluginConfiguration
/// <summary> /// <summary>
/// Gets or sets the amount of credit at start to play (in seconds). /// Gets or sets the amount of credit at start to play (in seconds).
/// </summary> /// </summary>
public int SecondsOfCreditsStartToPlay { get; set; } = 0; public int SecondsOfCreditsStartToPlay { get; set; }
// ===== Internal algorithm settings ===== // ===== Internal algorithm settings =====
@ -228,7 +228,7 @@ public class PluginConfiguration : BasePluginConfiguration
/// <summary> /// <summary>
/// Gets or sets the number of threads for an ffmpeg process. /// Gets or sets the number of threads for an ffmpeg process.
/// </summary> /// </summary>
public int ProcessThreads { get; set; } = 0; public int ProcessThreads { get; set; }
/// <summary> /// <summary>
/// Gets or sets the relative priority for an ffmpeg process. /// Gets or sets the relative priority for an ffmpeg process.

View File

@ -3,33 +3,26 @@ namespace ConfusedPolarBear.Plugin.IntroSkipper.Configuration;
/// <summary> /// <summary>
/// User interface configuration. /// User interface configuration.
/// </summary> /// </summary>
public class UserInterfaceConfiguration /// <remarks>
/// Initializes a new instance of the <see cref="UserInterfaceConfiguration"/> class.
/// </remarks>
/// <param name="visible">Skip button visibility.</param>
/// <param name="introText">Skip button intro text.</param>
/// <param name="creditsText">Skip button end credits text.</param>
public class UserInterfaceConfiguration(bool visible, string introText, string creditsText)
{ {
/// <summary>
/// Initializes a new instance of the <see cref="UserInterfaceConfiguration"/> class.
/// </summary>
/// <param name="visible">Skip button visibility.</param>
/// <param name="introText">Skip button intro text.</param>
/// <param name="creditsText">Skip button end credits text.</param>
public UserInterfaceConfiguration(bool visible, string introText, string creditsText)
{
SkipButtonVisible = visible;
SkipButtonIntroText = introText;
SkipButtonEndCreditsText = creditsText;
}
/// <summary> /// <summary>
/// Gets or sets a value indicating whether to show the skip intro button. /// Gets or sets a value indicating whether to show the skip intro button.
/// </summary> /// </summary>
public bool SkipButtonVisible { get; set; } public bool SkipButtonVisible { get; set; } = visible;
/// <summary> /// <summary>
/// Gets or sets the text to display in the skip intro button in introduction mode. /// Gets or sets the text to display in the skip intro button in introduction mode.
/// </summary> /// </summary>
public string SkipButtonIntroText { get; set; } public string SkipButtonIntroText { get; set; } = introText;
/// <summary> /// <summary>
/// Gets or sets the text to display in the skip intro button in end credits mode. /// Gets or sets the text to display in the skip intro button in end credits mode.
/// </summary> /// </summary>
public string SkipButtonEndCreditsText { get; set; } public string SkipButtonEndCreditsText { get; set; } = creditsText;
} }

View File

@ -146,16 +146,16 @@ public class SkipIntroController : ControllerBase
/// <param name="id">Unique identifier of this episode.</param> /// <param name="id">Unique identifier of this episode.</param>
/// <param name="mode">Mode.</param> /// <param name="mode">Mode.</param>
/// <returns>Intro object if the provided item has an intro, null otherwise.</returns> /// <returns>Intro object if the provided item has an intro, null otherwise.</returns>
private Intro? GetIntro(Guid id, AnalysisMode mode) private static Intro? GetIntro(Guid id, AnalysisMode mode)
{ {
try try
{ {
var timestamp = Plugin.Instance!.GetIntroByMode(id, mode); var timestamp = Plugin.GetIntroByMode(id, mode);
// Operate on a copy to avoid mutating the original Intro object stored in the dictionary. // Operate on a copy to avoid mutating the original Intro object stored in the dictionary.
var segment = new Intro(timestamp); var segment = new Intro(timestamp);
var config = Plugin.Instance.Configuration; var config = Plugin.Instance!.Configuration;
segment.IntroEnd -= config.RemainingSecondsOfIntro; segment.IntroEnd -= config.RemainingSecondsOfIntro;
if (config.PersistSkipButton) if (config.PersistSkipButton)
{ {
@ -219,7 +219,7 @@ public class SkipIntroController : ControllerBase
public ActionResult<List<IntroWithMetadata>> GetAllTimestamps( public ActionResult<List<IntroWithMetadata>> GetAllTimestamps(
[FromQuery] AnalysisMode mode = AnalysisMode.Introduction) [FromQuery] AnalysisMode mode = AnalysisMode.Introduction)
{ {
List<IntroWithMetadata> intros = new(); List<IntroWithMetadata> intros = [];
var timestamps = mode == AnalysisMode.Introduction ? var timestamps = mode == AnalysisMode.Introduction ?
Plugin.Instance!.Intros : Plugin.Instance!.Intros :

View File

@ -135,7 +135,7 @@ public class TroubleshootingController : ControllerBase
return bundle.ToString().TrimEnd('\n'); return bundle.ToString().TrimEnd('\n');
} }
private string GetHumanReadableSize(long bytes) private static string GetHumanReadableSize(long bytes)
{ {
string[] sizes = ["Bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"]; string[] sizes = ["Bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
double len = bytes; double len = bytes;
@ -144,7 +144,7 @@ public class TroubleshootingController : ControllerBase
while (len >= 1024 && order < sizes.Length - 1) while (len >= 1024 && order < sizes.Length - 1)
{ {
order++; order++;
len = len / 1024; len /= 1024;
} }
return $"{len:0.##} {sizes[order]}"; return $"{len:0.##} {sizes[order]}";

View File

@ -178,7 +178,7 @@ public class VisualizationController : ControllerBase
return NoContent(); return NoContent();
} }
private string GetSeasonName(QueuedEpisode episode) private static string GetSeasonName(QueuedEpisode episode)
{ {
return "Season " + episode.SeasonNumber.ToString(CultureInfo.InvariantCulture); return "Season " + episode.SeasonNumber.ToString(CultureInfo.InvariantCulture);
} }
@ -190,7 +190,7 @@ public class VisualizationController : ControllerBase
/// <param name="season">Season name.</param> /// <param name="season">Season name.</param>
/// <param name="episodes">Episodes.</param> /// <param name="episodes">Episodes.</param>
/// <returns>Boolean indicating if the requested season was found.</returns> /// <returns>Boolean indicating if the requested season was found.</returns>
private bool LookupSeasonByName(string series, string season, out List<QueuedEpisode> episodes) private static bool LookupSeasonByName(string series, string season, out List<QueuedEpisode> episodes)
{ {
foreach (var queuedEpisodes in Plugin.Instance!.QueuedMediaItems) foreach (var queuedEpisodes in Plugin.Instance!.QueuedMediaItems)
{ {
@ -209,7 +209,7 @@ public class VisualizationController : ControllerBase
return true; return true;
} }
episodes = new List<QueuedEpisode>(); episodes = [];
return false; return false;
} }
} }

View File

@ -3,26 +3,20 @@ namespace ConfusedPolarBear.Plugin.IntroSkipper.Data;
/// <summary> /// <summary>
/// A frame of video that partially (or entirely) consists of black pixels. /// A frame of video that partially (or entirely) consists of black pixels.
/// </summary> /// </summary>
public class BlackFrame /// <remarks>
/// Initializes a new instance of the <see cref="BlackFrame"/> class.
/// </remarks>
/// <param name="percent">Percentage of the frame that is black.</param>
/// <param name="time">Time this frame appears at.</param>
public class BlackFrame(int percent, double time)
{ {
/// <summary>
/// Initializes a new instance of the <see cref="BlackFrame"/> class.
/// </summary>
/// <param name="percent">Percentage of the frame that is black.</param>
/// <param name="time">Time this frame appears at.</param>
public BlackFrame(int percent, double time)
{
Percentage = percent;
Time = time;
}
/// <summary> /// <summary>
/// Gets or sets the percentage of the frame that is black. /// Gets or sets the percentage of the frame that is black.
/// </summary> /// </summary>
public int Percentage { get; set; } public int Percentage { get; set; } = percent;
/// <summary> /// <summary>
/// Gets or sets the time (in seconds) this frame appeared at. /// Gets or sets the time (in seconds) this frame appeared at.
/// </summary> /// </summary>
public double Time { get; set; } public double Time { get; set; } = time;
} }

View File

@ -5,26 +5,20 @@ namespace ConfusedPolarBear.Plugin.IntroSkipper.Data;
/// <summary> /// <summary>
/// Episode name and internal ID as returned by the visualization controller. /// Episode name and internal ID as returned by the visualization controller.
/// </summary> /// </summary>
public class EpisodeVisualization /// <remarks>
/// Initializes a new instance of the <see cref="EpisodeVisualization"/> class.
/// </remarks>
/// <param name="id">Episode id.</param>
/// <param name="name">Episode name.</param>
public class EpisodeVisualization(Guid id, string name)
{ {
/// <summary>
/// Initializes a new instance of the <see cref="EpisodeVisualization"/> class.
/// </summary>
/// <param name="id">Episode id.</param>
/// <param name="name">Episode name.</param>
public EpisodeVisualization(Guid id, string name)
{
Id = id;
Name = name;
}
/// <summary> /// <summary>
/// Gets the id. /// Gets the id.
/// </summary> /// </summary>
public Guid Id { get; private set; } public Guid Id { get; private set; } = id;
/// <summary> /// <summary>
/// Gets the name. /// Gets the name.
/// </summary> /// </summary>
public string Name { get; private set; } = string.Empty; public string Name { get; private set; } = name;
} }

View File

@ -40,7 +40,7 @@ public class QueuedEpisode
/// <summary> /// <summary>
/// Gets or sets a value indicating whether an episode is Anime. /// Gets or sets a value indicating whether an episode is Anime.
/// </summary> /// </summary>
public bool IsAnime { get; set; } = false; public bool IsAnime { get; set; }
/// <summary> /// <summary>
/// Gets or sets the timestamp (in seconds) to stop searching for an introduction at. /// Gets or sets the timestamp (in seconds) to stop searching for an introduction at.

View File

@ -61,7 +61,7 @@ public class TimeRange : IComparable
/// <returns>int.</returns> /// <returns>int.</returns>
public int CompareTo(object? obj) public int CompareTo(object? obj)
{ {
if (!(obj is TimeRange tr)) if (obj is not TimeRange tr)
{ {
throw new ArgumentException("obj must be a TimeRange"); throw new ArgumentException("obj must be a TimeRange");
} }

View File

@ -2,7 +2,6 @@
using System.Collections.Generic; using System.Collections.Generic;
namespace ConfusedPolarBear.Plugin.IntroSkipper.Data; namespace ConfusedPolarBear.Plugin.IntroSkipper.Data;
#pragma warning restore CA1036
/// <summary> /// <summary>
/// Time range helpers. /// Time range helpers.

View File

@ -5,7 +5,7 @@ namespace ConfusedPolarBear.Plugin.IntroSkipper.Data;
/// </summary> /// </summary>
public static class WarningManager public static class WarningManager
{ {
private static PluginWarning warnings; private static PluginWarning _warnings;
/// <summary> /// <summary>
/// Set warning. /// Set warning.
@ -13,7 +13,7 @@ public static class WarningManager
/// <param name="warning">Warning.</param> /// <param name="warning">Warning.</param>
public static void SetFlag(PluginWarning warning) public static void SetFlag(PluginWarning warning)
{ {
warnings |= warning; _warnings |= warning;
} }
/// <summary> /// <summary>
@ -21,7 +21,7 @@ public static class WarningManager
/// </summary> /// </summary>
public static void Clear() public static void Clear()
{ {
warnings = PluginWarning.None; _warnings = PluginWarning.None;
} }
/// <summary> /// <summary>
@ -30,7 +30,7 @@ public static class WarningManager
/// <returns>Warnings.</returns> /// <returns>Warnings.</returns>
public static string GetWarnings() public static string GetWarnings()
{ {
return warnings.ToString(); return _warnings.ToString();
} }
/// <summary> /// <summary>
@ -40,6 +40,6 @@ public static class WarningManager
/// <returns>True if the flag is set, otherwise false.</returns> /// <returns>True if the flag is set, otherwise false.</returns>
public static bool HasFlag(PluginWarning warning) public static bool HasFlag(PluginWarning warning)
{ {
return (warnings & warning) == warning; return (_warnings & warning) == warning;
} }
} }

View File

@ -1,6 +1,5 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using ConfusedPolarBear.Plugin.IntroSkipper.Data; using ConfusedPolarBear.Plugin.IntroSkipper.Data;
@ -19,38 +18,30 @@ namespace ConfusedPolarBear.Plugin.IntroSkipper;
/// </summary> /// </summary>
public class Entrypoint : IHostedService, IDisposable public class Entrypoint : IHostedService, IDisposable
{ {
private readonly IUserManager _userManager;
private readonly IUserViewManager _userViewManager;
private readonly ITaskManager _taskManager; private readonly ITaskManager _taskManager;
private readonly ILibraryManager _libraryManager; private readonly ILibraryManager _libraryManager;
private readonly ILogger<Entrypoint> _logger; private readonly ILogger<Entrypoint> _logger;
private readonly ILoggerFactory _loggerFactory; private readonly ILoggerFactory _loggerFactory;
private Timer _queueTimer; private readonly HashSet<Guid> _seasonsToAnalyze = [];
private readonly Timer _queueTimer;
private static readonly ManualResetEventSlim _autoTaskCompletEvent = new(false);
private bool _disposed; private bool _disposed;
private bool _analyzeAgain; private bool _analyzeAgain;
private HashSet<Guid> _seasonsToAnalyze = new HashSet<Guid>();
private static CancellationTokenSource? _cancellationTokenSource; private static CancellationTokenSource? _cancellationTokenSource;
private static ManualResetEventSlim _autoTaskCompletEvent = new ManualResetEventSlim(false);
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="Entrypoint"/> class. /// Initializes a new instance of the <see cref="Entrypoint"/> class.
/// </summary> /// </summary>
/// <param name="userManager">User manager.</param>
/// <param name="userViewManager">User view manager.</param>
/// <param name="libraryManager">Library manager.</param> /// <param name="libraryManager">Library manager.</param>
/// <param name="taskManager">Task manager.</param> /// <param name="taskManager">Task manager.</param>
/// <param name="logger">Logger.</param> /// <param name="logger">Logger.</param>
/// <param name="loggerFactory">Logger factory.</param> /// <param name="loggerFactory">Logger factory.</param>
public Entrypoint( public Entrypoint(
IUserManager userManager,
IUserViewManager userViewManager,
ILibraryManager libraryManager, ILibraryManager libraryManager,
ITaskManager taskManager, ITaskManager taskManager,
ILogger<Entrypoint> logger, ILogger<Entrypoint> logger,
ILoggerFactory loggerFactory) ILoggerFactory loggerFactory)
{ {
_userManager = userManager;
_userViewManager = userViewManager;
_libraryManager = libraryManager; _libraryManager = libraryManager;
_taskManager = taskManager; _taskManager = taskManager;
_logger = logger; _logger = logger;

View File

@ -14,25 +14,24 @@ namespace ConfusedPolarBear.Plugin.IntroSkipper;
/// <summary> /// <summary>
/// Wrapper for libchromaprint and the silencedetect filter. /// Wrapper for libchromaprint and the silencedetect filter.
/// </summary> /// </summary>
public static class FFmpegWrapper public static partial class FFmpegWrapper
{ {
/// <summary> /// <summary>
/// Used with FFmpeg's silencedetect filter to extract the start and end times of silence. /// Used with FFmpeg's silencedetect filter to extract the start and end times of silence.
/// </summary> /// </summary>
private static readonly Regex SilenceDetectionExpression = new( private static readonly Regex _silenceDetectionExpression = SilenceRegex();
"silence_(?<type>start|end): (?<time>[0-9\\.]+)");
/// <summary> /// <summary>
/// Used with FFmpeg's blackframe filter to extract the time and percentage of black pixels. /// Used with FFmpeg's blackframe filter to extract the time and percentage of black pixels.
/// </summary> /// </summary>
private static readonly Regex BlackFrameRegex = new("(pblack|t):[0-9.]+"); private static readonly Regex _blackFrameRegex = BlackFrameRegex();
/// <summary> /// <summary>
/// Gets or sets the logger. /// Gets or sets the logger.
/// </summary> /// </summary>
public static ILogger? Logger { get; set; } public static ILogger? Logger { get; set; }
private static Dictionary<string, string> ChromaprintLogs { get; set; } = new(); private static Dictionary<string, string> ChromaprintLogs { get; set; } = [];
private static ConcurrentDictionary<(Guid Id, AnalysisMode Mode), Dictionary<uint, int>> InvertedIndexCache { get; set; } = new(); private static ConcurrentDictionary<(Guid Id, AnalysisMode Mode), Dictionary<uint, int>> InvertedIndexCache { get; set; } = new();
@ -205,7 +204,7 @@ public static class FFmpegWrapper
* [silencedetect @ 0x000000000000] silence_end: 56.123 | silence_duration: 43.783 * [silencedetect @ 0x000000000000] silence_end: 56.123 | silence_duration: 43.783
*/ */
var raw = Encoding.UTF8.GetString(GetOutput(args, cacheKey, true)); var raw = Encoding.UTF8.GetString(GetOutput(args, cacheKey, true));
foreach (Match match in SilenceDetectionExpression.Matches(raw)) foreach (Match match in _silenceDetectionExpression.Matches(raw))
{ {
var isStart = match.Groups["type"].Value == "start"; var isStart = match.Groups["type"].Value == "start";
var time = Convert.ToDouble(match.Groups["time"].Value, CultureInfo.InvariantCulture); var time = Convert.ToDouble(match.Groups["time"].Value, CultureInfo.InvariantCulture);
@ -267,7 +266,7 @@ public static class FFmpegWrapper
// In our case, the metadata contained something that matched the regex. // In our case, the metadata contained something that matched the regex.
if (line.StartsWith("[Parsed_blackframe_", StringComparison.OrdinalIgnoreCase)) if (line.StartsWith("[Parsed_blackframe_", StringComparison.OrdinalIgnoreCase))
{ {
var matches = BlackFrameRegex.Matches(line); var matches = _blackFrameRegex.Matches(line);
if (matches.Count != 2) if (matches.Count != 2)
{ {
continue; continue;
@ -422,47 +421,43 @@ public static class FFmpegWrapper
RedirectStandardError = stderr RedirectStandardError = stderr
}; };
using (var ffmpeg = new Process { StartInfo = info }) using var ffmpeg = new Process { StartInfo = info };
Logger?.LogDebug("Starting ffmpeg with the following arguments: {Arguments}", ffmpeg.StartInfo.Arguments);
ffmpeg.Start();
try
{ {
Logger?.LogDebug("Starting ffmpeg with the following arguments: {Arguments}", ffmpeg.StartInfo.Arguments); ffmpeg.PriorityClass = Plugin.Instance?.Configuration.ProcessPriority ?? ProcessPriorityClass.BelowNormal;
}
catch (Exception e)
{
Logger?.LogDebug("ffmpeg priority could not be modified. {Message}", e.Message);
}
ffmpeg.Start(); using var ms = new MemoryStream();
var buf = new byte[4096];
int bytesRead;
try using (var streamReader = stderr ? ffmpeg.StandardError : ffmpeg.StandardOutput)
{
while ((bytesRead = streamReader.BaseStream.Read(buf, 0, buf.Length)) > 0)
{ {
ffmpeg.PriorityClass = Plugin.Instance?.Configuration.ProcessPriority ?? ProcessPriorityClass.BelowNormal; ms.Write(buf, 0, bytesRead);
}
catch (Exception e)
{
Logger?.LogDebug("ffmpeg priority could not be modified. {Message}", e.Message);
}
using (var ms = new MemoryStream())
{
var buf = new byte[4096];
int bytesRead;
using (var streamReader = stderr ? ffmpeg.StandardError : ffmpeg.StandardOutput)
{
while ((bytesRead = streamReader.BaseStream.Read(buf, 0, buf.Length)) > 0)
{
ms.Write(buf, 0, bytesRead);
}
}
ffmpeg.WaitForExit(timeout);
var output = ms.ToArray();
// If caching is enabled, cache the output of this command.
if (cacheOutput)
{
File.WriteAllBytes(cacheFilename, output);
}
return output;
} }
} }
ffmpeg.WaitForExit(timeout);
var output = ms.ToArray();
// If caching is enabled, cache the output of this command.
if (cacheOutput)
{
File.WriteAllBytes(cacheFilename, output);
}
return output;
} }
/// <summary> /// <summary>
@ -696,4 +691,10 @@ public static class FFmpegWrapper
return formatted; return formatted;
} }
[GeneratedRegex("silence_(?<type>start|end): (?<time>[0-9\\.]+)")]
private static partial Regex SilenceRegex();
[GeneratedRegex("(pblack|t):[0-9.]+")]
private static partial Regex BlackFrameRegex();
} }

View File

@ -21,15 +21,15 @@ 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 class Plugin : BasePlugin<PluginConfiguration>, IHasWebPages public partial 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();
private ILibraryManager _libraryManager; private readonly ILibraryManager _libraryManager;
private IItemRepository _itemRepository; private readonly IItemRepository _itemRepository;
private ILogger<Plugin> _logger; private readonly ILogger<Plugin> _logger;
private string _introPath; private readonly string _introPath;
private string _creditsPath; private readonly string _creditsPath;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="Plugin"/> class. /// Initializes a new instance of the <see cref="Plugin"/> class.
@ -201,7 +201,7 @@ public class Plugin : BasePlugin<PluginConfiguration>, IHasWebPages
/// <param name="mode">Mode.</param> /// <param name="mode">Mode.</param>
public void SaveTimestamps(AnalysisMode mode) public void SaveTimestamps(AnalysisMode mode)
{ {
List<Intro> introList = new List<Intro>(); List<Intro> introList = [];
var filePath = mode == AnalysisMode.Introduction var filePath = mode == AnalysisMode.Introduction
? _introPath ? _introPath
: _creditsPath; : _creditsPath;
@ -256,11 +256,11 @@ public class Plugin : BasePlugin<PluginConfiguration>, IHasWebPages
/// <inheritdoc /> /// <inheritdoc />
public IEnumerable<PluginPageInfo> GetPages() public IEnumerable<PluginPageInfo> GetPages()
{ {
return new[] return
{ [
new PluginPageInfo new PluginPageInfo
{ {
Name = this.Name, Name = Name,
EmbeddedResourcePath = GetType().Namespace + ".Configuration.configPage.html" EmbeddedResourcePath = GetType().Namespace + ".Configuration.configPage.html"
}, },
new PluginPageInfo new PluginPageInfo
@ -273,7 +273,7 @@ public class Plugin : BasePlugin<PluginConfiguration>, IHasWebPages
Name = "skip-intro-button.js", Name = "skip-intro-button.js",
EmbeddedResourcePath = GetType().Namespace + ".Configuration.inject.js" EmbeddedResourcePath = GetType().Namespace + ".Configuration.inject.js"
} }
}; ];
} }
/// <summary> /// <summary>
@ -311,7 +311,7 @@ public class Plugin : BasePlugin<PluginConfiguration>, IHasWebPages
/// <param name="id">Item id.</param> /// <param name="id">Item id.</param>
/// <param name="mode">Mode.</param> /// <param name="mode">Mode.</param>
/// <returns>Intro.</returns> /// <returns>Intro.</returns>
internal Intro GetIntroByMode(Guid id, AnalysisMode mode) internal static Intro GetIntroByMode(Guid id, AnalysisMode mode)
{ {
return mode == AnalysisMode.Introduction return mode == AnalysisMode.Introduction
? Instance!.Intros[id] ? Instance!.Intros[id]
@ -353,7 +353,7 @@ public class Plugin : BasePlugin<PluginConfiguration>, IHasWebPages
{ {
// Handle the case where the item is not found // Handle the case where the item is not found
_logger.LogWarning("Item with ID {Id} not found.", id); _logger.LogWarning("Item with ID {Id} not found.", id);
return new List<ChapterInfo>(); return [];
} }
return _itemRepository.GetChapters(item); return _itemRepository.GetChapters(item);
@ -447,7 +447,7 @@ public 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 = new Regex("</head>", RegexOptions.IgnoreCase); Regex headEnd = HeadRegex();
contents = headEnd.Replace(contents, scriptTag + "</head>", 1); contents = headEnd.Replace(contents, scriptTag + "</head>", 1);
// Write the modified file contents // Write the modified file contents
@ -455,4 +455,7 @@ public 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();
} }

View File

@ -15,28 +15,18 @@ namespace ConfusedPolarBear.Plugin.IntroSkipper;
/// <summary> /// <summary>
/// Manages enqueuing library items for analysis. /// Manages enqueuing library items for analysis.
/// </summary> /// </summary>
public class QueueManager /// <remarks>
/// Initializes a new instance of the <see cref="QueueManager"/> class.
/// </remarks>
/// <param name="logger">Logger.</param>
/// <param name="libraryManager">Library manager.</param>
public class QueueManager(ILogger<QueueManager> logger, ILibraryManager libraryManager)
{ {
private ILibraryManager _libraryManager; private readonly ILibraryManager _libraryManager = libraryManager;
private ILogger<QueueManager> _logger; private readonly ILogger<QueueManager> _logger = logger;
private readonly Dictionary<Guid, List<QueuedEpisode>> _queuedEpisodes = [];
private double analysisPercent; private double _analysisPercent;
private List<string> selectedLibraries; private List<string> _selectedLibraries = [];
private Dictionary<Guid, List<QueuedEpisode>> _queuedEpisodes;
/// <summary>
/// Initializes a new instance of the <see cref="QueueManager"/> class.
/// </summary>
/// <param name="logger">Logger.</param>
/// <param name="libraryManager">Library manager.</param>
public QueueManager(ILogger<QueueManager> logger, ILibraryManager libraryManager)
{
_logger = logger;
_libraryManager = libraryManager;
selectedLibraries = new();
_queuedEpisodes = new();
}
/// <summary> /// <summary>
/// Gets all media items on the server. /// Gets all media items on the server.
@ -52,7 +42,7 @@ public class QueueManager
foreach (var folder in _libraryManager.GetVirtualFolders()) foreach (var folder in _libraryManager.GetVirtualFolders())
{ {
// If libraries have been selected for analysis, ensure this library was selected. // If libraries have been selected for analysis, ensure this library was selected.
if (selectedLibraries.Count > 0 && !selectedLibraries.Contains(folder.Name)) if (_selectedLibraries.Count > 0 && !_selectedLibraries.Contains(folder.Name))
{ {
_logger.LogDebug("Not analyzing library \"{Name}\": not selected by user", folder.Name); _logger.LogDebug("Not analyzing library \"{Name}\": not selected by user", folder.Name);
continue; continue;
@ -100,17 +90,15 @@ public class QueueManager
var config = Plugin.Instance!.Configuration; var config = Plugin.Instance!.Configuration;
// Store the analysis percent // Store the analysis percent
analysisPercent = Convert.ToDouble(config.AnalysisPercent) / 100; _analysisPercent = Convert.ToDouble(config.AnalysisPercent) / 100;
// Get the list of library names which have been selected for analysis, ignoring whitespace and empty entries. // Get the list of library names which have been selected for analysis, ignoring whitespace and empty entries.
selectedLibraries = config.SelectedLibraries _selectedLibraries = [.. config.SelectedLibraries.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)];
.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)
.ToList();
// If any libraries have been selected for analysis, log their names. // If any libraries have been selected for analysis, log their names.
if (selectedLibraries.Count > 0) if (_selectedLibraries.Count > 0)
{ {
_logger.LogInformation("Limiting analysis to the following libraries: {Selected}", selectedLibraries); _logger.LogInformation("Limiting analysis to the following libraries: {Selected}", _selectedLibraries);
} }
else else
{ {
@ -136,7 +124,7 @@ public class QueueManager
{ {
// Order by series name, season, and then episode number so that status updates are logged in order // Order by series name, season, and then episode number so that status updates are logged in order
ParentId = id, ParentId = id,
OrderBy = new[] { (ItemSortBy.SeriesSortName, SortOrder.Ascending), (ItemSortBy.ParentIndexNumber, SortOrder.Ascending), (ItemSortBy.IndexNumber, SortOrder.Ascending), }, OrderBy = [(ItemSortBy.SeriesSortName, SortOrder.Ascending), (ItemSortBy.ParentIndexNumber, SortOrder.Ascending), (ItemSortBy.IndexNumber, SortOrder.Ascending),],
IncludeItemTypes = [BaseItemKind.Episode], IncludeItemTypes = [BaseItemKind.Episode],
Recursive = true, Recursive = true,
IsVirtualItem = false IsVirtualItem = false
@ -184,7 +172,7 @@ public class QueueManager
// Allocate a new list for each new season // Allocate a new list for each new season
if (!_queuedEpisodes.TryGetValue(episode.SeasonId, out var seasonEpisodes)) if (!_queuedEpisodes.TryGetValue(episode.SeasonId, out var seasonEpisodes))
{ {
seasonEpisodes = new List<QueuedEpisode>(); seasonEpisodes = [];
_queuedEpisodes[episode.SeasonId] = seasonEpisodes; _queuedEpisodes[episode.SeasonId] = seasonEpisodes;
} }
@ -202,7 +190,7 @@ public class QueueManager
// X and Y default to 25% and 10 minutes. // X and Y default to 25% and 10 minutes.
var duration = TimeSpan.FromTicks(episode.RunTimeTicks ?? 0).TotalSeconds; var duration = TimeSpan.FromTicks(episode.RunTimeTicks ?? 0).TotalSeconds;
var fingerprintDuration = Math.Min( var fingerprintDuration = Math.Min(
duration >= 5 * 60 ? duration * analysisPercent : duration, duration >= 5 * 60 ? duration * _analysisPercent : duration,
60 * pluginInstance.Configuration.AnalysisLengthLimit); 60 * pluginInstance.Configuration.AnalysisLengthLimit);
// Queue the episode for analysis // Queue the episode for analysis

View File

@ -134,7 +134,7 @@ public class BaseItemAnalyzerTask
first.SeasonNumber); first.SeasonNumber);
Interlocked.Add(ref totalProcessed, episodeCount * modeCount); // Update total Processed directly Interlocked.Add(ref totalProcessed, episodeCount * modeCount); // Update total Processed directly
progress.Report((totalProcessed * 100) / totalQueued); progress.Report(totalProcessed * 100 / totalQueued);
return; return;
} }
@ -142,7 +142,7 @@ public class BaseItemAnalyzerTask
if (modeCount != requiredModeCount) if (modeCount != requiredModeCount)
{ {
Interlocked.Add(ref totalProcessed, episodeCount); Interlocked.Add(ref totalProcessed, episodeCount);
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
} }
try try
@ -159,7 +159,7 @@ public class BaseItemAnalyzerTask
writeEdl = analyzed > 0 || Plugin.Instance.Configuration.RegenerateEdlFiles; writeEdl = analyzed > 0 || Plugin.Instance.Configuration.RegenerateEdlFiles;
progress.Report((totalProcessed * 100) / totalQueued); progress.Report(totalProcessed * 100 / totalQueued);
} }
} }
catch (FingerprintException ex) catch (FingerprintException ex)
@ -219,9 +219,10 @@ public class BaseItemAnalyzerTask
first.SeriesName, first.SeriesName,
first.SeasonNumber); first.SeasonNumber);
var analyzers = new Collection<IMediaFileAnalyzer>(); var analyzers = new Collection<IMediaFileAnalyzer>
{
analyzers.Add(new ChapterAnalyzer(_loggerFactory.CreateLogger<ChapterAnalyzer>())); new ChapterAnalyzer(_loggerFactory.CreateLogger<ChapterAnalyzer>())
};
if (first.IsAnime) if (first.IsAnime)
{ {
if (Plugin.Instance!.Configuration.UseChromaprint) if (Plugin.Instance!.Configuration.UseChromaprint)

View File

@ -112,6 +112,6 @@ public class CleanCacheTask : IScheduledTask
/// <returns>Task triggers.</returns> /// <returns>Task triggers.</returns>
public IEnumerable<TaskTriggerInfo> GetDefaultTriggers() public IEnumerable<TaskTriggerInfo> GetDefaultTriggers()
{ {
return Array.Empty<TaskTriggerInfo>(); return [];
} }
} }

View File

@ -102,6 +102,6 @@ public class DetectCreditsTask : IScheduledTask
/// <returns>Task triggers.</returns> /// <returns>Task triggers.</returns>
public IEnumerable<TaskTriggerInfo> GetDefaultTriggers() public IEnumerable<TaskTriggerInfo> GetDefaultTriggers()
{ {
return Array.Empty<TaskTriggerInfo>(); return [];
} }
} }

View File

@ -101,13 +101,13 @@ public class DetectIntrosCreditsTask : IScheduledTask
/// <returns>Task triggers.</returns> /// <returns>Task triggers.</returns>
public IEnumerable<TaskTriggerInfo> GetDefaultTriggers() public IEnumerable<TaskTriggerInfo> GetDefaultTriggers()
{ {
return new[] return
{ [
new TaskTriggerInfo new TaskTriggerInfo
{ {
Type = TaskTriggerInfo.TriggerDaily, Type = TaskTriggerInfo.TriggerDaily,
TimeOfDayTicks = TimeSpan.FromHours(0).Ticks TimeOfDayTicks = TimeSpan.FromHours(0).Ticks
} }
}; ];
} }
} }

View File

@ -101,6 +101,6 @@ public class DetectIntrosTask : IScheduledTask
/// <returns>Task triggers.</returns> /// <returns>Task triggers.</returns>
public IEnumerable<TaskTriggerInfo> GetDefaultTriggers() public IEnumerable<TaskTriggerInfo> GetDefaultTriggers()
{ {
return Array.Empty<TaskTriggerInfo>(); return [];
} }
} }

View File

@ -5,9 +5,9 @@ namespace ConfusedPolarBear.Plugin.IntroSkipper.ScheduledTasks;
internal sealed class ScheduledTaskSemaphore : IDisposable internal sealed class ScheduledTaskSemaphore : IDisposable
{ {
private static readonly SemaphoreSlim _semaphore = new SemaphoreSlim(1, 1); private static readonly SemaphoreSlim _semaphore = new(1, 1);
private static bool _isHeld = false; private static bool _isHeld;
private ScheduledTaskSemaphore() private ScheduledTaskSemaphore()
{ {