Cache the fingerprints for a season temporarily

This commit is contained in:
ConfusedPolarBear 2022-05-17 01:33:49 -05:00
parent cf1878382b
commit 05c5526bca

View File

@ -46,6 +46,14 @@ public class FingerprinterTask : IScheduledTask
private readonly ILogger<FingerprinterTask> _logger; private readonly ILogger<FingerprinterTask> _logger;
private readonly object _fingerprintCacheLock = new object();
/// <summary>
/// Temporary fingerprint cache to speed up reanalysis.
/// Fingerprints are removed from this after a season is analyzed.
/// </summary>
private Dictionary<Guid, ReadOnlyCollection<uint>> _fingerprintCache;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="FingerprinterTask"/> class. /// Initializes a new instance of the <see cref="FingerprinterTask"/> class.
/// </summary> /// </summary>
@ -53,6 +61,7 @@ public class FingerprinterTask : IScheduledTask
public FingerprinterTask(ILogger<FingerprinterTask> logger) public FingerprinterTask(ILogger<FingerprinterTask> logger)
{ {
_logger = logger; _logger = logger;
_fingerprintCache = new Dictionary<Guid, ReadOnlyCollection<uint>>();
} }
/// <summary> /// <summary>
@ -88,9 +97,38 @@ public class FingerprinterTask : IScheduledTask
Parallel.ForEach(queue, (season) => Parallel.ForEach(queue, (season) =>
{ {
AnalyzeSeason(season, cancellationToken); var first = season.Value[0];
try
{
AnalyzeSeason(season, cancellationToken);
}
catch (FingerprintException ex)
{
_logger.LogWarning(
"Unable to analyze {Series} season {Season}: unable to fingerprint: {Ex}",
first.SeriesName,
first.SeasonNumber,
ex);
}
catch (KeyNotFoundException ex)
{
_logger.LogWarning(
"Unable to analyze {Series} season {Season}: cache miss: {Ex}",
first.SeriesName,
first.SeasonNumber,
ex);
}
// Clear this season's episodes from the temporary fingerprint cache.
lock (_fingerprintCacheLock)
{
foreach (var ep in season.Value)
{
_fingerprintCache.Remove(ep.EpisodeId);
}
}
// TODO: report progress on a per episode basis
totalProcessed += season.Value.Count; totalProcessed += season.Value.Count;
progress.Report((totalProcessed * 100) / Plugin.Instance!.TotalQueued); progress.Report((totalProcessed * 100) / Plugin.Instance!.TotalQueued);
}); });
@ -159,12 +197,6 @@ public class FingerprinterTask : IScheduledTask
lhs.EpisodeId, lhs.EpisodeId,
rhs.EpisodeId); rhs.EpisodeId);
/*
TODO: bring back
totalProcessed += 2;
progress.Report((totalProcessed * 100) / Plugin.Instance!.TotalQueued);
*/
continue; continue;
} }
@ -189,14 +221,6 @@ public class FingerprinterTask : IScheduledTask
{ {
_logger.LogError("Caught fingerprint error: {Ex}", ex); _logger.LogError("Caught fingerprint error: {Ex}", ex);
} }
finally
{
/*
TODO: bring back
totalProcessed += 2;
progress.Report((totalProcessed * 100) / Plugin.Instance!.TotalQueued);
*/
}
} }
Plugin.Instance!.SaveTimestamps(); Plugin.Instance!.SaveTimestamps();
@ -221,6 +245,13 @@ public class FingerprinterTask : IScheduledTask
var lhsFingerprint = FPCalc.Fingerprint(lhsEpisode); var lhsFingerprint = FPCalc.Fingerprint(lhsEpisode);
var rhsFingerprint = FPCalc.Fingerprint(rhsEpisode); var rhsFingerprint = FPCalc.Fingerprint(rhsEpisode);
// Cache the fingerprints for quicker recall in the second pass (if one is needed).
lock (_fingerprintCacheLock)
{
_fingerprintCache[lhsEpisode.EpisodeId] = lhsFingerprint;
_fingerprintCache[rhsEpisode.EpisodeId] = rhsFingerprint;
}
return FingerprintEpisodes( return FingerprintEpisodes(
lhsEpisode.EpisodeId, lhsEpisode.EpisodeId,
lhsFingerprint, lhsFingerprint,
@ -501,7 +532,6 @@ public class FingerprinterTask : IScheduledTask
_logger.LogInformation("Reanalyzing {Count} episodes", totalCount - maxBucket.Count); _logger.LogInformation("Reanalyzing {Count} episodes", totalCount - maxBucket.Count);
// TODO: pick two episodes at random // TODO: pick two episodes at random
// Cache the fingerprint of the first episode in the max bucket to save CPU cycles
var lhs = episodes.Find(x => x.EpisodeId == maxBucket.Episodes[1]); var lhs = episodes.Find(x => x.EpisodeId == maxBucket.Episodes[1]);
if (lhs is null) if (lhs is null)
{ {
@ -509,17 +539,7 @@ public class FingerprinterTask : IScheduledTask
return; return;
} }
ReadOnlyCollection<uint> lhsFingerprint; var lhsFingerprint = _fingerprintCache[lhs.EpisodeId];
try
{
lhsFingerprint = FPCalc.Fingerprint(lhs);
}
catch (FingerprintException ex)
{
_logger.LogWarning("Skipping reanalysis of {Show} season {Season}: {Exception}", lhs.SeriesName, lhs.SeasonNumber, ex);
return;
}
var lhsDuration = GetIntroDuration(lhs.EpisodeId); var lhsDuration = GetIntroDuration(lhs.EpisodeId);
var (lowTargetDuration, highTargetDuration) = ( var (lowTargetDuration, highTargetDuration) = (
lhsDuration - ReanalysisTolerance, lhsDuration - ReanalysisTolerance,
@ -561,7 +581,7 @@ public class FingerprinterTask : IScheduledTask
lhs.EpisodeId, lhs.EpisodeId,
lhsFingerprint, lhsFingerprint,
episode.EpisodeId, episode.EpisodeId,
FPCalc.Fingerprint(episode)); _fingerprintCache[episode.EpisodeId]);
// Ensure that the new intro duration is within the targeted bucket and longer than what was found previously. // Ensure that the new intro duration is within the targeted bucket and longer than what was found previously.
var newDuration = Math.Round(newRhs.IntroEnd - newRhs.IntroStart, 2); var newDuration = Math.Round(newRhs.IntroEnd - newRhs.IntroStart, 2);