Fix bug where timestamps were reset to zero.
This commit is contained in:
parent
327fe99de0
commit
f68fd9d06a
@ -36,20 +36,21 @@ namespace IntroSkipper.Analyzers
|
||||
/// <param name="originalIntros">Original introductions.</param>
|
||||
/// <param name="mode">Analysis mode.</param>
|
||||
/// <returns>Modified Intro Timestamps.</returns>
|
||||
public Dictionary<Guid, Segment> AdjustIntroTimes(
|
||||
public IReadOnlyList<Segment> AdjustIntroTimes(
|
||||
IReadOnlyList<QueuedEpisode> episodes,
|
||||
IReadOnlyDictionary<Guid, Segment> originalIntros,
|
||||
IReadOnlyList<Segment> 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);
|
||||
|
@ -37,9 +37,7 @@ public class BlackFrameAnalyzer(ILogger<BlackFrameAnalyzer> logger) : IMediaFile
|
||||
throw new NotImplementedException("mode must equal Credits");
|
||||
}
|
||||
|
||||
var creditTimes = new Dictionary<Guid, Segment>();
|
||||
|
||||
var episodeAnalysisQueue = new List<QueuedEpisode>(analysisQueue);
|
||||
var creditTimes = new List<Segment>();
|
||||
|
||||
bool isFirstEpisode = true;
|
||||
|
||||
@ -47,7 +45,7 @@ public class BlackFrameAnalyzer(ILogger<BlackFrameAnalyzer> 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<BlackFrameAnalyzer> 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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -24,7 +24,7 @@ namespace IntroSkipper.Analyzers;
|
||||
/// <param name="logger">Logger.</param>
|
||||
public class ChapterAnalyzer(ILogger<ChapterAnalyzer> logger) : IMediaFileAnalyzer
|
||||
{
|
||||
private ILogger<ChapterAnalyzer> _logger = logger;
|
||||
private readonly ILogger<ChapterAnalyzer> _logger = logger;
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<IReadOnlyList<QueuedEpisode>> AnalyzeMediaFiles(
|
||||
@ -32,10 +32,7 @@ public class ChapterAnalyzer(ILogger<ChapterAnalyzer> logger) : IMediaFileAnalyz
|
||||
AnalysisMode mode,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var skippableRanges = new Dictionary<Guid, Segment>();
|
||||
|
||||
// Episode analysis queue.
|
||||
var episodeAnalysisQueue = new List<QueuedEpisode>(analysisQueue);
|
||||
var skippableRanges = new List<Segment>();
|
||||
|
||||
var expression = mode == AnalysisMode.Introduction ?
|
||||
Plugin.Instance!.Configuration.ChapterAnalyzerIntroductionPattern :
|
||||
@ -46,7 +43,7 @@ public class ChapterAnalyzer(ILogger<ChapterAnalyzer> 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<ChapterAnalyzer> 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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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<Guid, Segment> { [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<Guid, Segment> { [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)
|
||||
|
@ -11,7 +11,7 @@ namespace IntroSkipper.Data;
|
||||
/// </summary>
|
||||
public class QueuedEpisode
|
||||
{
|
||||
private readonly Dictionary<AnalysisMode, bool> _isAnalyzed = [];
|
||||
private readonly bool[] _isAnalyzed = new bool[2];
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the series name.
|
||||
@ -36,7 +36,7 @@ public class QueuedEpisode
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this media has been already analyzed.
|
||||
/// </summary>
|
||||
public IReadOnlyDictionary<AnalysisMode, bool> IsAnalyzed => _isAnalyzed;
|
||||
public IReadOnlyList<bool> IsAnalyzed => _isAnalyzed;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the full path to episode.
|
||||
@ -80,7 +80,7 @@ public class QueuedEpisode
|
||||
/// <param name="value">Value to set.</param>
|
||||
public void SetAnalyzed(AnalysisMode mode, bool value)
|
||||
{
|
||||
_isAnalyzed[mode] = value;
|
||||
_isAnalyzed[(int)mode] = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -90,6 +90,6 @@ public class QueuedEpisode
|
||||
/// <returns>Value of the analyzed mode.</returns>
|
||||
public bool GetAnalyzed(AnalysisMode mode)
|
||||
{
|
||||
return _isAnalyzed.TryGetValue(mode, out var value) && value;
|
||||
return _isAnalyzed[(int)mode];
|
||||
}
|
||||
}
|
||||
|
@ -33,8 +33,8 @@ public class Segment
|
||||
public Segment(Guid episode)
|
||||
{
|
||||
EpisodeId = episode;
|
||||
Start = 0;
|
||||
End = 0;
|
||||
Start = 0.0;
|
||||
End = 0.0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -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.
|
||||
/// </summary>
|
||||
public bool Valid => End > 0;
|
||||
public bool Valid => End > 0.0;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the duration of this intro.
|
||||
|
@ -292,7 +292,7 @@ namespace IntroSkipper.Manager
|
||||
VerifyQueue(IReadOnlyList<QueuedEpisode> candidates, IReadOnlyCollection<AnalysisMode> modes)
|
||||
{
|
||||
var verified = new List<QueuedEpisode>();
|
||||
var reqModes = new HashSet<AnalysisMode>(modes); // Start with all modes and remove completed ones
|
||||
var reqModes = new HashSet<AnalysisMode>();
|
||||
|
||||
foreach (var candidate in candidates)
|
||||
{
|
||||
@ -315,8 +315,10 @@ namespace IntroSkipper.Manager
|
||||
{
|
||||
candidate.SetAnalyzed(mode, true);
|
||||
}
|
||||
|
||||
reqModes.Remove(mode);
|
||||
}
|
||||
else
|
||||
{
|
||||
reqModes.Add(mode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -301,36 +301,37 @@ public class Plugin : BasePlugin<PluginConfiguration>, IHasWebPages
|
||||
return _itemRepository.GetChapters(item);
|
||||
}
|
||||
|
||||
internal async Task UpdateTimestamps(IReadOnlyDictionary<Guid, Segment> newTimestamps, AnalysisMode mode)
|
||||
internal async Task UpdateTimestamps(IReadOnlyList<Segment> 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<DbSegment>();
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -205,22 +205,22 @@ public class BaseItemAnalyzerTask
|
||||
|
||||
var analyzers = new Collection<IMediaFileAnalyzer>();
|
||||
|
||||
if (action == AnalyzerAction.Chapter || action == AnalyzerAction.Default)
|
||||
if (action is AnalyzerAction.Chapter or AnalyzerAction.Default)
|
||||
{
|
||||
analyzers.Add(new ChapterAnalyzer(_loggerFactory.CreateLogger<ChapterAnalyzer>()));
|
||||
}
|
||||
|
||||
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<ChromaprintAnalyzer>()));
|
||||
}
|
||||
|
||||
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<BlackFrameAnalyzer>()));
|
||||
}
|
||||
|
||||
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<ChromaprintAnalyzer>()));
|
||||
}
|
||||
@ -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<Segment>(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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user