From f68fd9d06af04919c3dd28c93510d5f76853eb0a Mon Sep 17 00:00:00 2001
From: rlauu <46294892+rlauu@users.noreply.github.com>
Date: Wed, 6 Nov 2024 10:30:00 +0100
Subject: [PATCH] Fix bug where timestamps were reset to zero.
---
IntroSkipper/Analyzers/AnalyzerHelper.cs | 17 ++++-----
IntroSkipper/Analyzers/BlackFrameAnalyzer.cs | 14 ++++----
IntroSkipper/Analyzers/ChapterAnalyzer.cs | 13 +++----
IntroSkipper/Analyzers/ChromaprintAnalyzer.cs | 4 +--
.../Controllers/SkipIntroController.cs | 8 ++---
IntroSkipper/Data/QueuedEpisode.cs | 8 ++---
IntroSkipper/Data/Segment.cs | 6 ++--
IntroSkipper/Manager/QueueManager.cs | 8 +++--
IntroSkipper/Plugin.cs | 35 ++++++++++---------
.../ScheduledTasks/BaseItemAnalyzerTask.cs | 13 +++----
10 files changed, 63 insertions(+), 63 deletions(-)
diff --git a/IntroSkipper/Analyzers/AnalyzerHelper.cs b/IntroSkipper/Analyzers/AnalyzerHelper.cs
index b572676..70133a9 100644
--- a/IntroSkipper/Analyzers/AnalyzerHelper.cs
+++ b/IntroSkipper/Analyzers/AnalyzerHelper.cs
@@ -36,20 +36,21 @@ namespace IntroSkipper.Analyzers
/// Original introductions.
/// Analysis mode.
/// Modified Intro Timestamps.
- public Dictionary AdjustIntroTimes(
+ public IReadOnlyList AdjustIntroTimes(
IReadOnlyList episodes,
- IReadOnlyDictionary originalIntros,
+ IReadOnlyList originalIntros,
AnalysisMode mode)
{
- return episodes
- .Where(episode => originalIntros.TryGetValue(episode.EpisodeId, out var _))
- .ToDictionary(
- episode => episode.EpisodeId,
- episode => AdjustIntroForEpisode(episode, originalIntros[episode.EpisodeId], mode));
+ return originalIntros.Select(i => AdjustIntroForEpisode(episodes.FirstOrDefault(e => originalIntros.Any(i => i.EpisodeId == e.EpisodeId)), i, mode)).ToList();
}
- private Segment AdjustIntroForEpisode(QueuedEpisode episode, Segment originalIntro, AnalysisMode mode)
+ private Segment AdjustIntroForEpisode(QueuedEpisode? episode, Segment originalIntro, AnalysisMode mode)
{
+ if (episode is null)
+ {
+ return new Segment(originalIntro.EpisodeId);
+ }
+
_logger.LogTrace("{Name} original intro: {Start} - {End}", episode.Name, originalIntro.Start, originalIntro.End);
var adjustedIntro = new Segment(originalIntro);
diff --git a/IntroSkipper/Analyzers/BlackFrameAnalyzer.cs b/IntroSkipper/Analyzers/BlackFrameAnalyzer.cs
index f5ea273..ae11304 100644
--- a/IntroSkipper/Analyzers/BlackFrameAnalyzer.cs
+++ b/IntroSkipper/Analyzers/BlackFrameAnalyzer.cs
@@ -37,9 +37,7 @@ public class BlackFrameAnalyzer(ILogger logger) : IMediaFile
throw new NotImplementedException("mode must equal Credits");
}
- var creditTimes = new Dictionary();
-
- var episodeAnalysisQueue = new List(analysisQueue);
+ var creditTimes = new List();
bool isFirstEpisode = true;
@@ -47,7 +45,7 @@ public class BlackFrameAnalyzer(ILogger logger) : IMediaFile
var searchDistance = 2 * _minimumCreditsDuration;
- foreach (var episode in episodeAnalysisQueue.Where(e => !e.GetAnalyzed(mode)))
+ foreach (var episode in analysisQueue.Where(e => !e.GetAnalyzed(mode)))
{
if (cancellationToken.IsCancellationRequested)
{
@@ -116,16 +114,16 @@ public class BlackFrameAnalyzer(ILogger logger) : IMediaFile
searchStart = episode.Duration - credit.Start + (0.5 * searchDistance);
- creditTimes.Add(episode.EpisodeId, credit);
+ creditTimes.Add(credit);
episode.SetAnalyzed(mode, true);
}
var analyzerHelper = new AnalyzerHelper(_logger);
- creditTimes = analyzerHelper.AdjustIntroTimes(analysisQueue, creditTimes, mode);
+ var adjustedCreditTimes = analyzerHelper.AdjustIntroTimes(analysisQueue, creditTimes, mode);
- await Plugin.Instance!.UpdateTimestamps(creditTimes, mode).ConfigureAwait(false);
+ await Plugin.Instance!.UpdateTimestamps(adjustedCreditTimes, mode).ConfigureAwait(false);
- return episodeAnalysisQueue;
+ return analysisQueue;
}
///
diff --git a/IntroSkipper/Analyzers/ChapterAnalyzer.cs b/IntroSkipper/Analyzers/ChapterAnalyzer.cs
index efb272e..653c42c 100644
--- a/IntroSkipper/Analyzers/ChapterAnalyzer.cs
+++ b/IntroSkipper/Analyzers/ChapterAnalyzer.cs
@@ -24,7 +24,7 @@ namespace IntroSkipper.Analyzers;
/// Logger.
public class ChapterAnalyzer(ILogger logger) : IMediaFileAnalyzer
{
- private ILogger _logger = logger;
+ private readonly ILogger _logger = logger;
///
public async Task> AnalyzeMediaFiles(
@@ -32,10 +32,7 @@ public class ChapterAnalyzer(ILogger logger) : IMediaFileAnalyz
AnalysisMode mode,
CancellationToken cancellationToken)
{
- var skippableRanges = new Dictionary();
-
- // Episode analysis queue.
- var episodeAnalysisQueue = new List(analysisQueue);
+ var skippableRanges = new List();
var expression = mode == AnalysisMode.Introduction ?
Plugin.Instance!.Configuration.ChapterAnalyzerIntroductionPattern :
@@ -46,7 +43,7 @@ public class ChapterAnalyzer(ILogger logger) : IMediaFileAnalyz
return analysisQueue;
}
- foreach (var episode in episodeAnalysisQueue.Where(e => !e.GetAnalyzed(mode)))
+ foreach (var episode in analysisQueue.Where(e => !e.GetAnalyzed(mode)))
{
if (cancellationToken.IsCancellationRequested)
{
@@ -64,13 +61,13 @@ public class ChapterAnalyzer(ILogger logger) : IMediaFileAnalyz
continue;
}
- skippableRanges.Add(episode.EpisodeId, skipRange);
+ skippableRanges.Add(skipRange);
episode.SetAnalyzed(mode, true);
}
await Plugin.Instance.UpdateTimestamps(skippableRanges, mode).ConfigureAwait(false);
- return episodeAnalysisQueue;
+ return analysisQueue;
}
///
diff --git a/IntroSkipper/Analyzers/ChromaprintAnalyzer.cs b/IntroSkipper/Analyzers/ChromaprintAnalyzer.cs
index e6159a6..95a75d2 100644
--- a/IntroSkipper/Analyzers/ChromaprintAnalyzer.cs
+++ b/IntroSkipper/Analyzers/ChromaprintAnalyzer.cs
@@ -208,9 +208,9 @@ public class ChromaprintAnalyzer : IMediaFileAnalyzer
// Adjust all introduction times.
var analyzerHelper = new AnalyzerHelper(_logger);
- seasonIntros = analyzerHelper.AdjustIntroTimes(analysisQueue, seasonIntros, _analysisMode);
+ var adjustedSeasonIntros = analyzerHelper.AdjustIntroTimes(analysisQueue, [.. seasonIntros.Values], _analysisMode);
- await Plugin.Instance!.UpdateTimestamps(seasonIntros, _analysisMode).ConfigureAwait(false);
+ await Plugin.Instance!.UpdateTimestamps(adjustedSeasonIntros, _analysisMode).ConfigureAwait(false);
return episodeAnalysisQueue;
}
diff --git a/IntroSkipper/Controllers/SkipIntroController.cs b/IntroSkipper/Controllers/SkipIntroController.cs
index 5911fe6..fdc807a 100644
--- a/IntroSkipper/Controllers/SkipIntroController.cs
+++ b/IntroSkipper/Controllers/SkipIntroController.cs
@@ -75,14 +75,14 @@ public class SkipIntroController(MediaSegmentUpdateManager mediaSegmentUpdateMan
if (timestamps?.Introduction.End > 0.0)
{
- var tr = new TimeRange(timestamps.Introduction.Start, timestamps.Introduction.End);
- await Plugin.Instance!.UpdateTimestamps(new Dictionary { [id] = new Segment(id, tr) }, AnalysisMode.Introduction).ConfigureAwait(false);
+ var seg = new Segment(id, new TimeRange(timestamps.Introduction.Start, timestamps.Introduction.End));
+ await Plugin.Instance!.UpdateTimestamps([seg], AnalysisMode.Introduction).ConfigureAwait(false);
}
if (timestamps?.Credits.End > 0.0)
{
- var tr = new TimeRange(timestamps.Credits.Start, timestamps.Credits.End);
- await Plugin.Instance!.UpdateTimestamps(new Dictionary { [id] = new Segment(id, tr) }, AnalysisMode.Credits).ConfigureAwait(false);
+ var seg = new Segment(id, new TimeRange(timestamps.Credits.Start, timestamps.Credits.End));
+ await Plugin.Instance!.UpdateTimestamps([seg], AnalysisMode.Credits).ConfigureAwait(false);
}
if (Plugin.Instance.Configuration.UpdateMediaSegments)
diff --git a/IntroSkipper/Data/QueuedEpisode.cs b/IntroSkipper/Data/QueuedEpisode.cs
index 85a3ce1..2f684ea 100644
--- a/IntroSkipper/Data/QueuedEpisode.cs
+++ b/IntroSkipper/Data/QueuedEpisode.cs
@@ -11,7 +11,7 @@ namespace IntroSkipper.Data;
///
public class QueuedEpisode
{
- private readonly Dictionary _isAnalyzed = [];
+ private readonly bool[] _isAnalyzed = new bool[2];
///
/// Gets or sets the series name.
@@ -36,7 +36,7 @@ public class QueuedEpisode
///
/// Gets a value indicating whether this media has been already analyzed.
///
- public IReadOnlyDictionary IsAnalyzed => _isAnalyzed;
+ public IReadOnlyList IsAnalyzed => _isAnalyzed;
///
/// Gets or sets the full path to episode.
@@ -80,7 +80,7 @@ public class QueuedEpisode
/// Value to set.
public void SetAnalyzed(AnalysisMode mode, bool value)
{
- _isAnalyzed[mode] = value;
+ _isAnalyzed[(int)mode] = value;
}
///
@@ -90,6 +90,6 @@ public class QueuedEpisode
/// Value of the analyzed mode.
public bool GetAnalyzed(AnalysisMode mode)
{
- return _isAnalyzed.TryGetValue(mode, out var value) && value;
+ return _isAnalyzed[(int)mode];
}
}
diff --git a/IntroSkipper/Data/Segment.cs b/IntroSkipper/Data/Segment.cs
index c170045..6710357 100644
--- a/IntroSkipper/Data/Segment.cs
+++ b/IntroSkipper/Data/Segment.cs
@@ -33,8 +33,8 @@ public class Segment
public Segment(Guid episode)
{
EpisodeId = episode;
- Start = 0;
- End = 0;
+ Start = 0.0;
+ End = 0.0;
}
///
@@ -88,7 +88,7 @@ public class Segment
/// Gets a value indicating whether this introduction is valid or not.
/// Invalid results must not be returned through the API.
///
- public bool Valid => End > 0;
+ public bool Valid => End > 0.0;
///
/// Gets the duration of this intro.
diff --git a/IntroSkipper/Manager/QueueManager.cs b/IntroSkipper/Manager/QueueManager.cs
index a4c6e9b..066512c 100644
--- a/IntroSkipper/Manager/QueueManager.cs
+++ b/IntroSkipper/Manager/QueueManager.cs
@@ -292,7 +292,7 @@ namespace IntroSkipper.Manager
VerifyQueue(IReadOnlyList candidates, IReadOnlyCollection modes)
{
var verified = new List();
- var reqModes = new HashSet(modes); // Start with all modes and remove completed ones
+ var reqModes = new HashSet();
foreach (var candidate in candidates)
{
@@ -315,8 +315,10 @@ namespace IntroSkipper.Manager
{
candidate.SetAnalyzed(mode, true);
}
-
- reqModes.Remove(mode);
+ }
+ else
+ {
+ reqModes.Add(mode);
}
}
}
diff --git a/IntroSkipper/Plugin.cs b/IntroSkipper/Plugin.cs
index 17b030c..335ce23 100644
--- a/IntroSkipper/Plugin.cs
+++ b/IntroSkipper/Plugin.cs
@@ -301,36 +301,37 @@ public class Plugin : BasePlugin, IHasWebPages
return _itemRepository.GetChapters(item);
}
- internal async Task UpdateTimestamps(IReadOnlyDictionary newTimestamps, AnalysisMode mode)
+ internal async Task UpdateTimestamps(IReadOnlyList newTimestamps, AnalysisMode mode)
{
+ if (newTimestamps.Count == 0)
+ {
+ return;
+ }
+
+ _logger.LogDebug("Starting UpdateTimestamps with {Count} segments for mode {Mode}", newTimestamps.Count, mode);
+
using var db = new IntroSkipperDbContext(_dbPath);
- // Get all existing segments in a single query
- var existingSegments = db.DbSegment
- .Where(s => newTimestamps.Keys.Contains(s.ItemId) && s.Type == mode)
- .ToDictionary(s => s.ItemId);
+ var segments = newTimestamps.Select(s => new DbSegment(s, mode)).ToList();
- // Batch updates and inserts
- var segmentsToAdd = new List();
+ var newItemIds = segments.Select(s => s.ItemId).ToHashSet();
+ var existingIds = db.DbSegment
+ .Where(s => s.Type == mode && newItemIds.Contains(s.ItemId))
+ .Select(s => s.ItemId)
+ .ToHashSet();
- foreach (var (itemId, segment) in newTimestamps)
+ foreach (var segment in segments)
{
- var dbSegment = new DbSegment(segment, mode);
- if (existingSegments.TryGetValue(itemId, out var existing))
+ if (existingIds.Contains(segment.ItemId))
{
- db.Entry(existing).CurrentValues.SetValues(dbSegment);
+ db.DbSegment.Update(segment);
}
else
{
- segmentsToAdd.Add(dbSegment);
+ db.DbSegment.Add(segment);
}
}
- if (segmentsToAdd.Count > 0)
- {
- await db.DbSegment.AddRangeAsync(segmentsToAdd).ConfigureAwait(false);
- }
-
await db.SaveChangesAsync().ConfigureAwait(false);
}
diff --git a/IntroSkipper/ScheduledTasks/BaseItemAnalyzerTask.cs b/IntroSkipper/ScheduledTasks/BaseItemAnalyzerTask.cs
index 02bf299..1998a15 100644
--- a/IntroSkipper/ScheduledTasks/BaseItemAnalyzerTask.cs
+++ b/IntroSkipper/ScheduledTasks/BaseItemAnalyzerTask.cs
@@ -205,22 +205,22 @@ public class BaseItemAnalyzerTask
var analyzers = new Collection();
- if (action == AnalyzerAction.Chapter || action == AnalyzerAction.Default)
+ if (action is AnalyzerAction.Chapter or AnalyzerAction.Default)
{
analyzers.Add(new ChapterAnalyzer(_loggerFactory.CreateLogger()));
}
- if (first.IsAnime && !first.IsMovie && (action == AnalyzerAction.Chromaprint || action == AnalyzerAction.Default))
+ if (first.IsAnime && !first.IsMovie && action is AnalyzerAction.Chromaprint or AnalyzerAction.Default)
{
analyzers.Add(new ChromaprintAnalyzer(_loggerFactory.CreateLogger()));
}
- if (mode == AnalysisMode.Credits && (action == AnalyzerAction.BlackFrame || action == AnalyzerAction.Default))
+ if (mode is AnalysisMode.Credits && action is AnalyzerAction.BlackFrame or AnalyzerAction.Default)
{
analyzers.Add(new BlackFrameAnalyzer(_loggerFactory.CreateLogger()));
}
- if (!first.IsAnime && !first.IsMovie && (action == AnalyzerAction.Chromaprint || action == AnalyzerAction.Default))
+ if (!first.IsAnime && !first.IsMovie && action is AnalyzerAction.Chromaprint or AnalyzerAction.Default)
{
analyzers.Add(new ChromaprintAnalyzer(_loggerFactory.CreateLogger()));
}
@@ -234,8 +234,9 @@ public class BaseItemAnalyzerTask
}
// Add items without intros/credits to blacklist.
- var blacklisted = items.Where(e => !e.GetAnalyzed(mode)).ToList();
- await Plugin.Instance!.UpdateTimestamps(blacklisted.ToDictionary(e => e.EpisodeId, e => new Segment(e.EpisodeId)), mode).ConfigureAwait(false);
+ var blacklisted = new List(items.Where(e => !e.GetAnalyzed(mode)).Select(e => new Segment(e.EpisodeId)));
+ _logger.LogDebug("Blacklisting {Count} items for mode {Mode}", blacklisted.Count, mode);
+ await Plugin.Instance!.UpdateTimestamps(blacklisted, mode).ConfigureAwait(false);
totalItems -= blacklisted.Count;
return totalItems;