apply auto-fixes from VS Code (#283)
Co-authored-by: rlauu <46294892+rlauu@users.noreply.github.com>
This commit is contained in:
parent
d428efb1f2
commit
60c735282e
@ -14,7 +14,7 @@ namespace ConfusedPolarBear.Plugin.IntroSkipper;
|
||||
public class AnalyzerHelper
|
||||
{
|
||||
private readonly ILogger _logger;
|
||||
private readonly double silenceDetectionMinimumDuration;
|
||||
private readonly double _silenceDetectionMinimumDuration;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AnalyzerHelper"/> class.
|
||||
@ -23,7 +23,7 @@ public class AnalyzerHelper
|
||||
public AnalyzerHelper(ILogger logger)
|
||||
{
|
||||
var config = Plugin.Instance?.Configuration ?? new PluginConfiguration();
|
||||
silenceDetectionMinimumDuration = config.SilenceDetectionMinimumDuration;
|
||||
_silenceDetectionMinimumDuration = config.SilenceDetectionMinimumDuration;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
@ -126,7 +126,7 @@ public class AnalyzerHelper
|
||||
private bool IsValidSilenceForIntroAdjustment(TimeRange silenceRange, TimeRange originalIntroEnd, Intro adjustedIntro)
|
||||
{
|
||||
return originalIntroEnd.Intersects(silenceRange) &&
|
||||
silenceRange.Duration >= silenceDetectionMinimumDuration &&
|
||||
silenceRange.Duration >= _silenceDetectionMinimumDuration &&
|
||||
silenceRange.Start >= adjustedIntro.IntroStart;
|
||||
}
|
||||
}
|
||||
|
@ -19,11 +19,11 @@ public class BlackFrameAnalyzer : IMediaFileAnalyzer
|
||||
|
||||
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>
|
||||
/// Initializes a new instance of the <see cref="BlackFrameAnalyzer"/> class.
|
||||
@ -32,9 +32,9 @@ public class BlackFrameAnalyzer : IMediaFileAnalyzer
|
||||
public BlackFrameAnalyzer(ILogger<BlackFrameAnalyzer> logger)
|
||||
{
|
||||
var config = Plugin.Instance?.Configuration ?? new PluginConfiguration();
|
||||
minimumCreditsDuration = config.MinimumCreditsDuration;
|
||||
maximumCreditsDuration = 2 * config.MaximumCreditsDuration;
|
||||
blackFrameMinimumPercentage = config.BlackFrameMinimumPercentage;
|
||||
_minimumCreditsDuration = config.MinimumCreditsDuration;
|
||||
_maximumCreditsDuration = 2 * config.MaximumCreditsDuration;
|
||||
_blackFrameMinimumPercentage = config.BlackFrameMinimumPercentage;
|
||||
|
||||
_logger = logger;
|
||||
}
|
||||
@ -56,9 +56,9 @@ public class BlackFrameAnalyzer : IMediaFileAnalyzer
|
||||
|
||||
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)))
|
||||
{
|
||||
@ -73,7 +73,7 @@ public class BlackFrameAnalyzer : IMediaFileAnalyzer
|
||||
var scanTime = episode.Duration - searchStart;
|
||||
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
|
||||
{
|
||||
@ -82,16 +82,16 @@ public class BlackFrameAnalyzer : IMediaFileAnalyzer
|
||||
scanTime = episode.Duration - searchStart;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
if (searchStart == minimumCreditsDuration) // Skip if no black frames were found
|
||||
if (searchStart == _minimumCreditsDuration) // Skip if no black frames were found
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@ -103,12 +103,12 @@ public class BlackFrameAnalyzer : IMediaFileAnalyzer
|
||||
episode,
|
||||
searchStart,
|
||||
searchDistance,
|
||||
blackFrameMinimumPercentage);
|
||||
_blackFrameMinimumPercentage);
|
||||
|
||||
if (credit is null)
|
||||
{
|
||||
// If no credits were found, reset the first-episode search logic for the next episode in the sequence.
|
||||
searchStart = minimumCreditsDuration;
|
||||
searchStart = _minimumCreditsDuration;
|
||||
isFirstEpisode = true;
|
||||
continue;
|
||||
}
|
||||
@ -139,7 +139,7 @@ public class BlackFrameAnalyzer : IMediaFileAnalyzer
|
||||
{
|
||||
// Start by analyzing the last N minutes of the file.
|
||||
var upperLimit = searchStart;
|
||||
var lowerLimit = Math.Max(searchStart - searchDistance, minimumCreditsDuration);
|
||||
var lowerLimit = Math.Max(searchStart - searchDistance, _minimumCreditsDuration);
|
||||
var start = TimeSpan.FromSeconds(upperLimit);
|
||||
var end = TimeSpan.FromSeconds(lowerLimit);
|
||||
var firstFrameTime = 0.0;
|
||||
@ -176,7 +176,7 @@ public class BlackFrameAnalyzer : IMediaFileAnalyzer
|
||||
|
||||
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
|
||||
end = TimeSpan.FromSeconds(lowerLimit);
|
||||
@ -190,7 +190,7 @@ public class BlackFrameAnalyzer : IMediaFileAnalyzer
|
||||
|
||||
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
|
||||
start = TimeSpan.FromSeconds(upperLimit);
|
||||
|
@ -22,15 +22,15 @@ public class ChromaprintAnalyzer : IMediaFileAnalyzer
|
||||
/// </summary>
|
||||
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;
|
||||
|
||||
@ -41,10 +41,10 @@ public class ChromaprintAnalyzer : IMediaFileAnalyzer
|
||||
public ChromaprintAnalyzer(ILogger<ChromaprintAnalyzer> logger)
|
||||
{
|
||||
var config = Plugin.Instance?.Configuration ?? new PluginConfiguration();
|
||||
maximumDifferences = config.MaximumFingerprintPointDifferences;
|
||||
invertedIndexShift = config.InvertedIndexShift;
|
||||
maximumTimeSkip = config.MaximumTimeSkip;
|
||||
minimumIntroDuration = config.MinimumIntroDuration;
|
||||
_maximumDifferences = config.MaximumFingerprintPointDifferences;
|
||||
_invertedIndexShift = config.InvertedIndexShift;
|
||||
_maximumTimeSkip = config.MaximumTimeSkip;
|
||||
_minimumIntroDuration = config.MinimumIntroDuration;
|
||||
|
||||
_logger = logger;
|
||||
}
|
||||
@ -87,7 +87,7 @@ public class ChromaprintAnalyzer : IMediaFileAnalyzer
|
||||
.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
|
||||
foreach (var episode in episodesWithFingerprint)
|
||||
@ -113,7 +113,7 @@ public class ChromaprintAnalyzer : IMediaFileAnalyzer
|
||||
WarningManager.SetFlag(PluginWarning.InvalidChromaprintFingerprint);
|
||||
|
||||
// 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.
|
||||
var analyzerHelper = new AnalyzerHelper(_logger);
|
||||
seasonIntros = analyzerHelper.AdjustIntroTimes(analysisQueue, seasonIntros, this._analysisMode);
|
||||
seasonIntros = analyzerHelper.AdjustIntroTimes(analysisQueue, seasonIntros, _analysisMode);
|
||||
|
||||
Plugin.Instance!.UpdateTimestamps(seasonIntros, _analysisMode);
|
||||
|
||||
@ -307,7 +307,7 @@ public class ChromaprintAnalyzer : IMediaFileAnalyzer
|
||||
{
|
||||
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);
|
||||
|
||||
@ -372,7 +372,7 @@ public class ChromaprintAnalyzer : IMediaFileAnalyzer
|
||||
var diff = lhs[lhsPosition] ^ rhs[rhsPosition];
|
||||
|
||||
// If the difference between the samples is small, flag both times as similar.
|
||||
if (CountBits(diff) > maximumDifferences)
|
||||
if (CountBits(diff) > _maximumDifferences)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@ -389,14 +389,14 @@ public class ChromaprintAnalyzer : IMediaFileAnalyzer
|
||||
rhsTimes.Add(double.MaxValue);
|
||||
|
||||
// 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);
|
||||
if (lContiguous is null || lContiguous.Duration < minimumIntroDuration)
|
||||
var lContiguous = TimeRangeHelpers.FindContiguous(lhsTimes.ToArray(), _maximumTimeSkip);
|
||||
if (lContiguous is null || lContiguous.Duration < _minimumIntroDuration)
|
||||
{
|
||||
return (new TimeRange(), new TimeRange());
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
|
@ -10,7 +10,7 @@ namespace ConfusedPolarBear.Plugin.IntroSkipper.Analyzers;
|
||||
/// </summary>
|
||||
public class SegmentAnalyzer : IMediaFileAnalyzer
|
||||
{
|
||||
private ILogger<SegmentAnalyzer> _logger;
|
||||
private readonly ILogger<SegmentAnalyzer> _logger;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="SegmentAnalyzer"/> class.
|
||||
|
@ -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.
|
||||
/// If this is set, all EDL files will be regenerated and overwrite any existing EDL file.
|
||||
/// </summary>
|
||||
public bool RegenerateEdlFiles { get; set; } = false;
|
||||
public bool RegenerateEdlFiles { get; set; }
|
||||
|
||||
// ===== Custom analysis settings =====
|
||||
|
||||
@ -172,7 +172,7 @@ public class PluginConfiguration : BasePluginConfiguration
|
||||
/// <summary>
|
||||
/// Gets or sets the amount of credit at start to play (in seconds).
|
||||
/// </summary>
|
||||
public int SecondsOfCreditsStartToPlay { get; set; } = 0;
|
||||
public int SecondsOfCreditsStartToPlay { get; set; }
|
||||
|
||||
// ===== Internal algorithm settings =====
|
||||
|
||||
@ -228,7 +228,7 @@ public class PluginConfiguration : BasePluginConfiguration
|
||||
/// <summary>
|
||||
/// Gets or sets the number of threads for an ffmpeg process.
|
||||
/// </summary>
|
||||
public int ProcessThreads { get; set; } = 0;
|
||||
public int ProcessThreads { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the relative priority for an ffmpeg process.
|
||||
|
@ -3,33 +3,26 @@ namespace ConfusedPolarBear.Plugin.IntroSkipper.Configuration;
|
||||
/// <summary>
|
||||
/// User interface configuration.
|
||||
/// </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>
|
||||
/// Gets or sets a value indicating whether to show the skip intro button.
|
||||
/// </summary>
|
||||
public bool SkipButtonVisible { get; set; }
|
||||
public bool SkipButtonVisible { get; set; } = visible;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the text to display in the skip intro button in introduction mode.
|
||||
/// </summary>
|
||||
public string SkipButtonIntroText { get; set; }
|
||||
public string SkipButtonIntroText { get; set; } = introText;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the text to display in the skip intro button in end credits mode.
|
||||
/// </summary>
|
||||
public string SkipButtonEndCreditsText { get; set; }
|
||||
public string SkipButtonEndCreditsText { get; set; } = creditsText;
|
||||
}
|
||||
|
@ -146,16 +146,16 @@ public class SkipIntroController : ControllerBase
|
||||
/// <param name="id">Unique identifier of this episode.</param>
|
||||
/// <param name="mode">Mode.</param>
|
||||
/// <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
|
||||
{
|
||||
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.
|
||||
var segment = new Intro(timestamp);
|
||||
|
||||
var config = Plugin.Instance.Configuration;
|
||||
var config = Plugin.Instance!.Configuration;
|
||||
segment.IntroEnd -= config.RemainingSecondsOfIntro;
|
||||
if (config.PersistSkipButton)
|
||||
{
|
||||
@ -219,7 +219,7 @@ public class SkipIntroController : ControllerBase
|
||||
public ActionResult<List<IntroWithMetadata>> GetAllTimestamps(
|
||||
[FromQuery] AnalysisMode mode = AnalysisMode.Introduction)
|
||||
{
|
||||
List<IntroWithMetadata> intros = new();
|
||||
List<IntroWithMetadata> intros = [];
|
||||
|
||||
var timestamps = mode == AnalysisMode.Introduction ?
|
||||
Plugin.Instance!.Intros :
|
||||
|
@ -135,7 +135,7 @@ public class TroubleshootingController : ControllerBase
|
||||
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"];
|
||||
double len = bytes;
|
||||
@ -144,7 +144,7 @@ public class TroubleshootingController : ControllerBase
|
||||
while (len >= 1024 && order < sizes.Length - 1)
|
||||
{
|
||||
order++;
|
||||
len = len / 1024;
|
||||
len /= 1024;
|
||||
}
|
||||
|
||||
return $"{len:0.##} {sizes[order]}";
|
||||
|
@ -178,7 +178,7 @@ public class VisualizationController : ControllerBase
|
||||
return NoContent();
|
||||
}
|
||||
|
||||
private string GetSeasonName(QueuedEpisode episode)
|
||||
private static string GetSeasonName(QueuedEpisode episode)
|
||||
{
|
||||
return "Season " + episode.SeasonNumber.ToString(CultureInfo.InvariantCulture);
|
||||
}
|
||||
@ -190,7 +190,7 @@ public class VisualizationController : ControllerBase
|
||||
/// <param name="season">Season name.</param>
|
||||
/// <param name="episodes">Episodes.</param>
|
||||
/// <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)
|
||||
{
|
||||
@ -209,7 +209,7 @@ public class VisualizationController : ControllerBase
|
||||
return true;
|
||||
}
|
||||
|
||||
episodes = new List<QueuedEpisode>();
|
||||
episodes = [];
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -3,26 +3,20 @@ namespace ConfusedPolarBear.Plugin.IntroSkipper.Data;
|
||||
/// <summary>
|
||||
/// A frame of video that partially (or entirely) consists of black pixels.
|
||||
/// </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>
|
||||
/// Gets or sets the percentage of the frame that is black.
|
||||
/// </summary>
|
||||
public int Percentage { get; set; }
|
||||
public int Percentage { get; set; } = percent;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the time (in seconds) this frame appeared at.
|
||||
/// </summary>
|
||||
public double Time { get; set; }
|
||||
public double Time { get; set; } = time;
|
||||
}
|
||||
|
@ -5,26 +5,20 @@ namespace ConfusedPolarBear.Plugin.IntroSkipper.Data;
|
||||
/// <summary>
|
||||
/// Episode name and internal ID as returned by the visualization controller.
|
||||
/// </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>
|
||||
/// Gets the id.
|
||||
/// </summary>
|
||||
public Guid Id { get; private set; }
|
||||
public Guid Id { get; private set; } = id;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name.
|
||||
/// </summary>
|
||||
public string Name { get; private set; } = string.Empty;
|
||||
public string Name { get; private set; } = name;
|
||||
}
|
||||
|
@ -40,7 +40,7 @@ public class QueuedEpisode
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether an episode is Anime.
|
||||
/// </summary>
|
||||
public bool IsAnime { get; set; } = false;
|
||||
public bool IsAnime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the timestamp (in seconds) to stop searching for an introduction at.
|
||||
|
@ -61,7 +61,7 @@ public class TimeRange : IComparable
|
||||
/// <returns>int.</returns>
|
||||
public int CompareTo(object? obj)
|
||||
{
|
||||
if (!(obj is TimeRange tr))
|
||||
if (obj is not TimeRange tr)
|
||||
{
|
||||
throw new ArgumentException("obj must be a TimeRange");
|
||||
}
|
||||
|
@ -2,7 +2,6 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace ConfusedPolarBear.Plugin.IntroSkipper.Data;
|
||||
#pragma warning restore CA1036
|
||||
|
||||
/// <summary>
|
||||
/// Time range helpers.
|
||||
|
@ -5,7 +5,7 @@ namespace ConfusedPolarBear.Plugin.IntroSkipper.Data;
|
||||
/// </summary>
|
||||
public static class WarningManager
|
||||
{
|
||||
private static PluginWarning warnings;
|
||||
private static PluginWarning _warnings;
|
||||
|
||||
/// <summary>
|
||||
/// Set warning.
|
||||
@ -13,7 +13,7 @@ public static class WarningManager
|
||||
/// <param name="warning">Warning.</param>
|
||||
public static void SetFlag(PluginWarning warning)
|
||||
{
|
||||
warnings |= warning;
|
||||
_warnings |= warning;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -21,7 +21,7 @@ public static class WarningManager
|
||||
/// </summary>
|
||||
public static void Clear()
|
||||
{
|
||||
warnings = PluginWarning.None;
|
||||
_warnings = PluginWarning.None;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -30,7 +30,7 @@ public static class WarningManager
|
||||
/// <returns>Warnings.</returns>
|
||||
public static string GetWarnings()
|
||||
{
|
||||
return warnings.ToString();
|
||||
return _warnings.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -40,6 +40,6 @@ public static class WarningManager
|
||||
/// <returns>True if the flag is set, otherwise false.</returns>
|
||||
public static bool HasFlag(PluginWarning warning)
|
||||
{
|
||||
return (warnings & warning) == warning;
|
||||
return (_warnings & warning) == warning;
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using ConfusedPolarBear.Plugin.IntroSkipper.Data;
|
||||
@ -19,38 +18,30 @@ namespace ConfusedPolarBear.Plugin.IntroSkipper;
|
||||
/// </summary>
|
||||
public class Entrypoint : IHostedService, IDisposable
|
||||
{
|
||||
private readonly IUserManager _userManager;
|
||||
private readonly IUserViewManager _userViewManager;
|
||||
private readonly ITaskManager _taskManager;
|
||||
private readonly ILibraryManager _libraryManager;
|
||||
private readonly ILogger<Entrypoint> _logger;
|
||||
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 _analyzeAgain;
|
||||
private HashSet<Guid> _seasonsToAnalyze = new HashSet<Guid>();
|
||||
private static CancellationTokenSource? _cancellationTokenSource;
|
||||
private static ManualResetEventSlim _autoTaskCompletEvent = new ManualResetEventSlim(false);
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Entrypoint"/> class.
|
||||
/// </summary>
|
||||
/// <param name="userManager">User manager.</param>
|
||||
/// <param name="userViewManager">User view manager.</param>
|
||||
/// <param name="libraryManager">Library manager.</param>
|
||||
/// <param name="taskManager">Task manager.</param>
|
||||
/// <param name="logger">Logger.</param>
|
||||
/// <param name="loggerFactory">Logger factory.</param>
|
||||
public Entrypoint(
|
||||
IUserManager userManager,
|
||||
IUserViewManager userViewManager,
|
||||
ILibraryManager libraryManager,
|
||||
ITaskManager taskManager,
|
||||
ILogger<Entrypoint> logger,
|
||||
ILoggerFactory loggerFactory)
|
||||
{
|
||||
_userManager = userManager;
|
||||
_userViewManager = userViewManager;
|
||||
_libraryManager = libraryManager;
|
||||
_taskManager = taskManager;
|
||||
_logger = logger;
|
||||
|
@ -14,25 +14,24 @@ namespace ConfusedPolarBear.Plugin.IntroSkipper;
|
||||
/// <summary>
|
||||
/// Wrapper for libchromaprint and the silencedetect filter.
|
||||
/// </summary>
|
||||
public static class FFmpegWrapper
|
||||
public static partial class FFmpegWrapper
|
||||
{
|
||||
/// <summary>
|
||||
/// Used with FFmpeg's silencedetect filter to extract the start and end times of silence.
|
||||
/// </summary>
|
||||
private static readonly Regex SilenceDetectionExpression = new(
|
||||
"silence_(?<type>start|end): (?<time>[0-9\\.]+)");
|
||||
private static readonly Regex _silenceDetectionExpression = SilenceRegex();
|
||||
|
||||
/// <summary>
|
||||
/// Used with FFmpeg's blackframe filter to extract the time and percentage of black pixels.
|
||||
/// </summary>
|
||||
private static readonly Regex BlackFrameRegex = new("(pblack|t):[0-9.]+");
|
||||
private static readonly Regex _blackFrameRegex = BlackFrameRegex();
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the logger.
|
||||
/// </summary>
|
||||
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();
|
||||
|
||||
@ -205,7 +204,7 @@ public static class FFmpegWrapper
|
||||
* [silencedetect @ 0x000000000000] silence_end: 56.123 | silence_duration: 43.783
|
||||
*/
|
||||
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 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.
|
||||
if (line.StartsWith("[Parsed_blackframe_", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
var matches = BlackFrameRegex.Matches(line);
|
||||
var matches = _blackFrameRegex.Matches(line);
|
||||
if (matches.Count != 2)
|
||||
{
|
||||
continue;
|
||||
@ -422,47 +421,43 @@ public static class FFmpegWrapper
|
||||
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;
|
||||
}
|
||||
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;
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -696,4 +691,10 @@ public static class FFmpegWrapper
|
||||
|
||||
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();
|
||||
}
|
||||
|
@ -21,15 +21,15 @@ namespace ConfusedPolarBear.Plugin.IntroSkipper;
|
||||
/// <summary>
|
||||
/// Intro skipper plugin. Uses audio analysis to find common sequences of audio shared between episodes.
|
||||
/// </summary>
|
||||
public class Plugin : BasePlugin<PluginConfiguration>, IHasWebPages
|
||||
public partial class Plugin : BasePlugin<PluginConfiguration>, IHasWebPages
|
||||
{
|
||||
private readonly object _serializationLock = new();
|
||||
private readonly object _introsLock = new();
|
||||
private ILibraryManager _libraryManager;
|
||||
private IItemRepository _itemRepository;
|
||||
private ILogger<Plugin> _logger;
|
||||
private string _introPath;
|
||||
private string _creditsPath;
|
||||
private readonly ILibraryManager _libraryManager;
|
||||
private readonly IItemRepository _itemRepository;
|
||||
private readonly ILogger<Plugin> _logger;
|
||||
private readonly string _introPath;
|
||||
private readonly string _creditsPath;
|
||||
|
||||
/// <summary>
|
||||
/// 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>
|
||||
public void SaveTimestamps(AnalysisMode mode)
|
||||
{
|
||||
List<Intro> introList = new List<Intro>();
|
||||
List<Intro> introList = [];
|
||||
var filePath = mode == AnalysisMode.Introduction
|
||||
? _introPath
|
||||
: _creditsPath;
|
||||
@ -256,11 +256,11 @@ public class Plugin : BasePlugin<PluginConfiguration>, IHasWebPages
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<PluginPageInfo> GetPages()
|
||||
{
|
||||
return new[]
|
||||
{
|
||||
return
|
||||
[
|
||||
new PluginPageInfo
|
||||
{
|
||||
Name = this.Name,
|
||||
Name = Name,
|
||||
EmbeddedResourcePath = GetType().Namespace + ".Configuration.configPage.html"
|
||||
},
|
||||
new PluginPageInfo
|
||||
@ -273,7 +273,7 @@ public class Plugin : BasePlugin<PluginConfiguration>, IHasWebPages
|
||||
Name = "skip-intro-button.js",
|
||||
EmbeddedResourcePath = GetType().Namespace + ".Configuration.inject.js"
|
||||
}
|
||||
};
|
||||
];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -311,7 +311,7 @@ public class Plugin : BasePlugin<PluginConfiguration>, IHasWebPages
|
||||
/// <param name="id">Item id.</param>
|
||||
/// <param name="mode">Mode.</param>
|
||||
/// <returns>Intro.</returns>
|
||||
internal Intro GetIntroByMode(Guid id, AnalysisMode mode)
|
||||
internal static Intro GetIntroByMode(Guid id, AnalysisMode mode)
|
||||
{
|
||||
return mode == AnalysisMode.Introduction
|
||||
? Instance!.Intros[id]
|
||||
@ -353,7 +353,7 @@ public class Plugin : BasePlugin<PluginConfiguration>, IHasWebPages
|
||||
{
|
||||
// Handle the case where the item is not found
|
||||
_logger.LogWarning("Item with ID {Id} not found.", id);
|
||||
return new List<ChapterInfo>();
|
||||
return [];
|
||||
}
|
||||
|
||||
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.
|
||||
// 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);
|
||||
|
||||
// Write the modified file contents
|
||||
@ -455,4 +455,7 @@ public class Plugin : BasePlugin<PluginConfiguration>, IHasWebPages
|
||||
|
||||
_logger.LogInformation("Skip intro button successfully added");
|
||||
}
|
||||
|
||||
[GeneratedRegex("</head>", RegexOptions.IgnoreCase)]
|
||||
private static partial Regex HeadRegex();
|
||||
}
|
||||
|
@ -15,28 +15,18 @@ namespace ConfusedPolarBear.Plugin.IntroSkipper;
|
||||
/// <summary>
|
||||
/// Manages enqueuing library items for analysis.
|
||||
/// </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 ILogger<QueueManager> _logger;
|
||||
|
||||
private double analysisPercent;
|
||||
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();
|
||||
}
|
||||
private readonly ILibraryManager _libraryManager = libraryManager;
|
||||
private readonly ILogger<QueueManager> _logger = logger;
|
||||
private readonly Dictionary<Guid, List<QueuedEpisode>> _queuedEpisodes = [];
|
||||
private double _analysisPercent;
|
||||
private List<string> _selectedLibraries = [];
|
||||
|
||||
/// <summary>
|
||||
/// Gets all media items on the server.
|
||||
@ -52,7 +42,7 @@ public class QueueManager
|
||||
foreach (var folder in _libraryManager.GetVirtualFolders())
|
||||
{
|
||||
// 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);
|
||||
continue;
|
||||
@ -100,17 +90,15 @@ public class QueueManager
|
||||
var config = Plugin.Instance!.Configuration;
|
||||
|
||||
// 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.
|
||||
selectedLibraries = config.SelectedLibraries
|
||||
.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)
|
||||
.ToList();
|
||||
_selectedLibraries = [.. config.SelectedLibraries.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)];
|
||||
|
||||
// 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
|
||||
{
|
||||
@ -136,7 +124,7 @@ public class QueueManager
|
||||
{
|
||||
// Order by series name, season, and then episode number so that status updates are logged in order
|
||||
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],
|
||||
Recursive = true,
|
||||
IsVirtualItem = false
|
||||
@ -184,7 +172,7 @@ public class QueueManager
|
||||
// Allocate a new list for each new season
|
||||
if (!_queuedEpisodes.TryGetValue(episode.SeasonId, out var seasonEpisodes))
|
||||
{
|
||||
seasonEpisodes = new List<QueuedEpisode>();
|
||||
seasonEpisodes = [];
|
||||
_queuedEpisodes[episode.SeasonId] = seasonEpisodes;
|
||||
}
|
||||
|
||||
@ -202,7 +190,7 @@ public class QueueManager
|
||||
// X and Y default to 25% and 10 minutes.
|
||||
var duration = TimeSpan.FromTicks(episode.RunTimeTicks ?? 0).TotalSeconds;
|
||||
var fingerprintDuration = Math.Min(
|
||||
duration >= 5 * 60 ? duration * analysisPercent : duration,
|
||||
duration >= 5 * 60 ? duration * _analysisPercent : duration,
|
||||
60 * pluginInstance.Configuration.AnalysisLengthLimit);
|
||||
|
||||
// Queue the episode for analysis
|
||||
|
@ -134,7 +134,7 @@ public class BaseItemAnalyzerTask
|
||||
first.SeasonNumber);
|
||||
|
||||
Interlocked.Add(ref totalProcessed, episodeCount * modeCount); // Update total Processed directly
|
||||
progress.Report((totalProcessed * 100) / totalQueued);
|
||||
progress.Report(totalProcessed * 100 / totalQueued);
|
||||
|
||||
return;
|
||||
}
|
||||
@ -142,7 +142,7 @@ public class BaseItemAnalyzerTask
|
||||
if (modeCount != requiredModeCount)
|
||||
{
|
||||
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
|
||||
@ -159,7 +159,7 @@ public class BaseItemAnalyzerTask
|
||||
|
||||
writeEdl = analyzed > 0 || Plugin.Instance.Configuration.RegenerateEdlFiles;
|
||||
|
||||
progress.Report((totalProcessed * 100) / totalQueued);
|
||||
progress.Report(totalProcessed * 100 / totalQueued);
|
||||
}
|
||||
}
|
||||
catch (FingerprintException ex)
|
||||
@ -219,9 +219,10 @@ public class BaseItemAnalyzerTask
|
||||
first.SeriesName,
|
||||
first.SeasonNumber);
|
||||
|
||||
var analyzers = new Collection<IMediaFileAnalyzer>();
|
||||
|
||||
analyzers.Add(new ChapterAnalyzer(_loggerFactory.CreateLogger<ChapterAnalyzer>()));
|
||||
var analyzers = new Collection<IMediaFileAnalyzer>
|
||||
{
|
||||
new ChapterAnalyzer(_loggerFactory.CreateLogger<ChapterAnalyzer>())
|
||||
};
|
||||
if (first.IsAnime)
|
||||
{
|
||||
if (Plugin.Instance!.Configuration.UseChromaprint)
|
||||
|
@ -112,6 +112,6 @@ public class CleanCacheTask : IScheduledTask
|
||||
/// <returns>Task triggers.</returns>
|
||||
public IEnumerable<TaskTriggerInfo> GetDefaultTriggers()
|
||||
{
|
||||
return Array.Empty<TaskTriggerInfo>();
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
@ -102,6 +102,6 @@ public class DetectCreditsTask : IScheduledTask
|
||||
/// <returns>Task triggers.</returns>
|
||||
public IEnumerable<TaskTriggerInfo> GetDefaultTriggers()
|
||||
{
|
||||
return Array.Empty<TaskTriggerInfo>();
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
@ -101,13 +101,13 @@ public class DetectIntrosCreditsTask : IScheduledTask
|
||||
/// <returns>Task triggers.</returns>
|
||||
public IEnumerable<TaskTriggerInfo> GetDefaultTriggers()
|
||||
{
|
||||
return new[]
|
||||
{
|
||||
return
|
||||
[
|
||||
new TaskTriggerInfo
|
||||
{
|
||||
Type = TaskTriggerInfo.TriggerDaily,
|
||||
TimeOfDayTicks = TimeSpan.FromHours(0).Ticks
|
||||
}
|
||||
};
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -101,6 +101,6 @@ public class DetectIntrosTask : IScheduledTask
|
||||
/// <returns>Task triggers.</returns>
|
||||
public IEnumerable<TaskTriggerInfo> GetDefaultTriggers()
|
||||
{
|
||||
return Array.Empty<TaskTriggerInfo>();
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
@ -5,9 +5,9 @@ namespace ConfusedPolarBear.Plugin.IntroSkipper.ScheduledTasks;
|
||||
|
||||
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()
|
||||
{
|
||||
|
Loading…
x
Reference in New Issue
Block a user