Increase accuracy

This commit is contained in:
ConfusedPolarBear 2022-05-19 00:59:58 -05:00
parent 9c4b62ff5e
commit b9612f83c1

View File

@ -244,17 +244,19 @@ public class FingerprinterTask : IScheduledTask
{ {
Plugin.Instance!.Intros[intro.Key] = intro.Value; Plugin.Instance!.Intros[intro.Key] = intro.Value;
} }
}
// Only run the second pass if the user hasn't requested cancellation and we found an intro
if (!cancellationToken.IsCancellationRequested && everFoundIntro)
{
// Run a second pass over this season to remove outliers and fix episodes that failed in the first pass.
RunSecondPass(season.Value);
}
lock (_introsLock)
{
Plugin.Instance!.SaveTimestamps(); Plugin.Instance!.SaveTimestamps();
} }
if (cancellationToken.IsCancellationRequested || !everFoundIntro)
{
return;
}
// Reanalyze this season to check for (and hopefully correct) outliers and failed episodes.
CheckSeason(season.Value);
} }
/// <summary> /// <summary>
@ -478,7 +480,7 @@ public class FingerprinterTask : IScheduledTask
/// Looks for and fixes intro durations that were either not found or are statistical outliers. /// Looks for and fixes intro durations that were either not found or are statistical outliers.
/// </summary> /// </summary>
/// <param name="episodes">List of episodes that was just analyzed.</param> /// <param name="episodes">List of episodes that was just analyzed.</param>
private void CheckSeason(List<QueuedEpisode> episodes) private void RunSecondPass(List<QueuedEpisode> episodes)
{ {
var intros = Plugin.Instance!.Intros; var intros = Plugin.Instance!.Intros;
@ -539,9 +541,8 @@ public class FingerprinterTask : IScheduledTask
} }
// Ensure that the most frequently seen bucket has a majority // Ensure that the most frequently seen bucket has a majority
// TODO: change to debug
percentValid = (maxBucket.Count * 100) / validCount; percentValid = (maxBucket.Count * 100) / validCount;
_logger.LogInformation( _logger.LogDebug(
"Intro duration {Duration} appeared {Frequency} times ({Percent}%)", "Intro duration {Duration} appeared {Frequency} times ({Percent}%)",
maxDuration, maxDuration,
maxBucket.Count, maxBucket.Count,
@ -552,22 +553,29 @@ public class FingerprinterTask : IScheduledTask
return; return;
} }
_logger.LogInformation("Reanalyzing {Count} episodes", totalCount - maxBucket.Count); _logger.LogDebug("Second pass is processing {Count} episodes", totalCount - maxBucket.Count);
// TODO: pick two episodes at random // Calculate a range of intro durations that are most likely to be correct.
var lhs = episodes.Find(x => x.EpisodeId == maxBucket.Episodes[1]); var maxEpisode = episodes.Find(x => x.EpisodeId == maxBucket.Episodes[0]);
if (lhs is null) if (maxEpisode is null)
{ {
_logger.LogError("Reanalysis failed to get episode from bucket"); _logger.LogError("Second pass failed to get episode from bucket");
return; return;
} }
var lhsFingerprint = _fingerprintCache[lhs.EpisodeId]; var lhsDuration = GetIntroDuration(maxEpisode.EpisodeId);
var lhsDuration = GetIntroDuration(lhs.EpisodeId);
var (lowTargetDuration, highTargetDuration) = ( var (lowTargetDuration, highTargetDuration) = (
lhsDuration - ReanalysisTolerance, lhsDuration - ReanalysisTolerance,
lhsDuration + ReanalysisTolerance); lhsDuration + ReanalysisTolerance);
// TODO: add limit and make it customizable
var count = maxBucket.Episodes.Count - 1;
var goodFingerprints = new List<ReadOnlyCollection<uint>>();
foreach (var id in maxBucket.Episodes)
{
goodFingerprints.Add(_fingerprintCache[id]);
}
foreach (var episode in episodes) foreach (var episode in episodes)
{ {
// Don't reanalyze episodes from the max bucket // Don't reanalyze episodes from the max bucket
@ -584,7 +592,7 @@ public class FingerprinterTask : IScheduledTask
// If the episode's intro duration is close enough to the targeted bucket, leave it alone. // If the episode's intro duration is close enough to the targeted bucket, leave it alone.
if (Math.Abs(lhsDuration - oldDuration) <= ReanalysisTolerance) if (Math.Abs(lhsDuration - oldDuration) <= ReanalysisTolerance)
{ {
_logger.LogInformation( _logger.LogDebug(
"Not reanalyzing episode {Path} (intro is {Initial}, target is {Max})", "Not reanalyzing episode {Path} (intro is {Initial}, target is {Max})",
shortPath, shortPath,
Math.Round(oldDuration, 2), Math.Round(oldDuration, 2),
@ -600,8 +608,10 @@ public class FingerprinterTask : IScheduledTask
maxDuration); maxDuration);
// Analyze the episode again, ignoring whatever is returned for the known good episode. // Analyze the episode again, ignoring whatever is returned for the known good episode.
foreach (var lhsFingerprint in goodFingerprints)
{
var (_, newRhs) = FingerprintEpisodes( var (_, newRhs) = FingerprintEpisodes(
lhs.EpisodeId, maxEpisode.EpisodeId,
lhsFingerprint, lhsFingerprint,
episode.EpisodeId, episode.EpisodeId,
_fingerprintCache[episode.EpisodeId]); _fingerprintCache[episode.EpisodeId]);
@ -610,7 +620,7 @@ public class FingerprinterTask : IScheduledTask
var newDuration = Math.Round(newRhs.IntroEnd - newRhs.IntroStart, 2); var newDuration = Math.Round(newRhs.IntroEnd - newRhs.IntroStart, 2);
if (newDuration < oldDuration || newDuration < lowTargetDuration || newDuration > highTargetDuration) if (newDuration < oldDuration || newDuration < lowTargetDuration || newDuration > highTargetDuration)
{ {
_logger.LogInformation( _logger.LogDebug(
"Ignoring reanalysis for {Path} (was {Initial}, now is {New})", "Ignoring reanalysis for {Path} (was {Initial}, now is {New})",
shortPath, shortPath,
oldDuration, oldDuration,
@ -630,6 +640,9 @@ public class FingerprinterTask : IScheduledTask
{ {
Plugin.Instance!.Intros[episode.EpisodeId] = newRhs; Plugin.Instance!.Intros[episode.EpisodeId] = newRhs;
} }
break;
}
} }
} }