diff --git a/ConfusedPolarBear.Plugin.IntroSkipper.Tests/TestAudioFingerprinting.cs b/ConfusedPolarBear.Plugin.IntroSkipper.Tests/TestAudioFingerprinting.cs index eecf7ec..a2ec132 100644 --- a/ConfusedPolarBear.Plugin.IntroSkipper.Tests/TestAudioFingerprinting.cs +++ b/ConfusedPolarBear.Plugin.IntroSkipper.Tests/TestAudioFingerprinting.cs @@ -70,11 +70,11 @@ public class TestFPCalc Assert.True(lhs.Valid); Assert.Equal(0, lhs.IntroStart); - Assert.Equal(17.792, lhs.IntroEnd); + Assert.Equal(17.92, lhs.IntroEnd); Assert.True(rhs.Valid); - Assert.Equal(5.12, rhs.IntroStart); - Assert.Equal(22.912, rhs.IntroEnd); + Assert.Equal(0, rhs.IntroStart); + Assert.Equal(22.784, rhs.IntroEnd); } private QueuedEpisode queueEpisode(string path) diff --git a/ConfusedPolarBear.Plugin.IntroSkipper.Tests/TestContiguous.cs b/ConfusedPolarBear.Plugin.IntroSkipper.Tests/TestContiguous.cs index c27076a..286aa0c 100644 --- a/ConfusedPolarBear.Plugin.IntroSkipper.Tests/TestContiguous.cs +++ b/ConfusedPolarBear.Plugin.IntroSkipper.Tests/TestContiguous.cs @@ -13,7 +13,7 @@ public class TestTimeRanges }; var expected = new TimeRange(1, 4); - var actual = TimeRangeHelpers.FindContiguous(times, 3.25); + var actual = TimeRangeHelpers.FindContiguous(times, 2); Assert.Equal(expected, actual); } @@ -29,7 +29,45 @@ public class TestTimeRanges }; var expected = new TimeRange(1, 5.3128); - var actual = TimeRangeHelpers.FindContiguous(times, 3.25); + var actual = TimeRangeHelpers.FindContiguous(times, 2); + + Assert.Equal(expected, actual); + } + + [Fact] + public void TestFuturama() + { + // These timestamps were manually extracted from Futurama S01E04 and S01E05. + var times = new double[]{ + 2.176, 8.32, 10.112, 11.264, 13.696, 16, 16.128, 16.64, 16.768, 16.896, 17.024, 17.152, 17.28, + 17.408, 17.536, 17.664, 17.792, 17.92, 18.048, 18.176, 18.304, 18.432, 18.56, 18.688, 18.816, + 18.944, 19.072, 19.2, 19.328, 19.456, 19.584, 19.712, 19.84, 19.968, 20.096, 20.224, 20.352, + 20.48, 20.608, 20.736, 20.864, 20.992, 21.12, 21.248, 21.376, 21.504, 21.632, 21.76, 21.888, + 22.016, 22.144, 22.272, 22.4, 22.528, 22.656, 22.784, 22.912, 23.04, 23.168, 23.296, 23.424, + 23.552, 23.68, 23.808, 23.936, 24.064, 24.192, 24.32, 24.448, 24.576, 24.704, 24.832, 24.96, + 25.088, 25.216, 25.344, 25.472, 25.6, 25.728, 25.856, 25.984, 26.112, 26.24, 26.368, 26.496, + 26.624, 26.752, 26.88, 27.008, 27.136, 27.264, 27.392, 27.52, 27.648, 27.776, 27.904, 28.032, + 28.16, 28.288, 28.416, 28.544, 28.672, 28.8, 28.928, 29.056, 29.184, 29.312, 29.44, 29.568, + 29.696, 29.824, 29.952, 30.08, 30.208, 30.336, 30.464, 30.592, 30.72, 30.848, 30.976, 31.104, + 31.232, 31.36, 31.488, 31.616, 31.744, 31.872, 32, 32.128, 32.256, 32.384, 32.512, 32.64, + 32.768, 32.896, 33.024, 33.152, 33.28, 33.408, 33.536, 33.664, 33.792, 33.92, 34.048, 34.176, + 34.304, 34.432, 34.56, 34.688, 34.816, 34.944, 35.072, 35.2, 35.328, 35.456, 35.584, 35.712, + 35.84, 35.968, 36.096, 36.224, 36.352, 36.48, 36.608, 36.736, 36.864, 36.992, 37.12, 37.248, + 37.376, 37.504, 37.632, 37.76, 37.888, 38.016, 38.144, 38.272, 38.4, 38.528, 38.656, 38.784, + 38.912, 39.04, 39.168, 39.296, 39.424, 39.552, 39.68, 39.808, 39.936, 40.064, 40.192, 40.32, + 40.448, 40.576, 40.704, 40.832, 40.96, 41.088, 41.216, 41.344, 41.472, 41.6, 41.728, 41.856, + 41.984, 42.112, 42.24, 42.368, 42.496, 42.624, 42.752, 42.88, 43.008, 43.136, 43.264, 43.392, + 43.52, 43.648, 43.776, 43.904, 44.032, 44.16, 44.288, 44.416, 44.544, 44.672, 44.8, 44.928, + 45.056, 45.184, 57.344, 62.976, 68.864, 74.368, 81.92, 82.048, 86.528, 100.864, 102.656, + 102.784, 102.912, 103.808, 110.976, 116.864, 125.696, 128.384, 133.248, 133.376, 136.064, + 136.704, 142.976, 150.272, 152.064, 164.864, 164.992, 166.144, 166.272, 175.488, 190.08, + 191.872, 192, 193.28, 193.536, 213.376, 213.504, 225.664, 225.792, 243.2, 243.84, 256, + 264.448, 264.576, 264.704, 269.568, 274.816, 274.944, 276.096, 283.264, 294.784, 294.912, + 295.04, 295.168, 313.984, 325.504, 333.568, 335.872, 336.384 + }; + + var expected = new TimeRange(16, 45.184); + var actual = TimeRangeHelpers.FindContiguous(times, 2); Assert.Equal(expected, actual); } diff --git a/ConfusedPolarBear.Plugin.IntroSkipper/Configuration/configPage.html b/ConfusedPolarBear.Plugin.IntroSkipper/Configuration/configPage.html index b70fba7..9dcbcef 100644 --- a/ConfusedPolarBear.Plugin.IntroSkipper/Configuration/configPage.html +++ b/ConfusedPolarBear.Plugin.IntroSkipper/Configuration/configPage.html @@ -49,9 +49,45 @@ -
-

Troubleshooter

-

Compare the audio fingerprint of two episodes.

+
+ Fingerprint Visualizer + +

+ Interactively compare the audio fingerprints of two episodes.
+ The blue and red bar to the right of the fingerprint diff turns blue + when the corresponding fingerprint points are at least 75% similar. +

+ + + + + + + + + + + + + + + + + + + + + + + +
KeyFunction
Up arrow + Shift the left episode up by 0.128 seconds. + Holding control will shift the episode by 10 seconds. +
Down arrow + Shift the left episode down by 0.128 seconds. + Holding control will shift the episode by 10 seconds. +
Right arrowAdvance to the next pair of episodes.
Left arrowGo back to the previous pair of episodes.
+
@@ -61,21 +97,23 @@
+ Shift amount:

-
+ diff --git a/ConfusedPolarBear.Plugin.IntroSkipper/Controllers/TroubleshooterController.cs b/ConfusedPolarBear.Plugin.IntroSkipper/Controllers/TroubleshooterController.cs index 492691e..fb65bcd 100644 --- a/ConfusedPolarBear.Plugin.IntroSkipper/Controllers/TroubleshooterController.cs +++ b/ConfusedPolarBear.Plugin.IntroSkipper/Controllers/TroubleshooterController.cs @@ -11,7 +11,7 @@ namespace ConfusedPolarBear.Plugin.IntroSkipper.Controllers; /// /// Intro skipper troubleshooting controller. Allows browsing fingerprints on a per episode basis. /// -[Authorize] +[Authorize(Policy = "RequiresElevation")] [ApiController] [Produces(MediaTypeNames.Application.Json)] [Route("Intros")] diff --git a/ConfusedPolarBear.Plugin.IntroSkipper/Data/TimeRange.cs b/ConfusedPolarBear.Plugin.IntroSkipper/Data/TimeRange.cs index 10f3ae1..fb9ef1d 100644 --- a/ConfusedPolarBear.Plugin.IntroSkipper/Data/TimeRange.cs +++ b/ConfusedPolarBear.Plugin.IntroSkipper/Data/TimeRange.cs @@ -3,6 +3,9 @@ using System.Collections.Generic; namespace ConfusedPolarBear.Plugin.IntroSkipper; +// Supress CA1036: Override methods on comparable types. +#pragma warning disable CA1036 + /// /// Range of contiguous time. /// @@ -54,98 +57,23 @@ public class TimeRange : IComparable public double Duration => End - Start; /// - /// Comparison operator. + /// Compare TimeRange durations. /// - /// Left TimeRange. - /// Right TimeRange. - public static bool operator ==(TimeRange left, TimeRange right) - { - return left.Equals(right); - } - - /// - /// Comparison operator. - /// - /// Left TimeRange. - /// Right TimeRange. - public static bool operator !=(TimeRange left, TimeRange right) - { - return !left.Equals(right); - } - - /// - /// Comparison operator. - /// - /// Left TimeRange. - /// Right TimeRange. - public static bool operator <=(TimeRange left, TimeRange right) - { - return left.CompareTo(right) <= 0; - } - - /// - /// Comparison operator. - /// - /// Left TimeRange. - /// Right TimeRange. - public static bool operator <(TimeRange left, TimeRange right) - { - return left.CompareTo(right) < 0; - } - - /// - /// Comparison operator. - /// - /// Left TimeRange. - /// Right TimeRange. - public static bool operator >=(TimeRange left, TimeRange right) - { - return left.CompareTo(right) >= 0; - } - - /// - /// Comparison operator. - /// - /// Left TimeRange. - /// Right TimeRange. - public static bool operator >(TimeRange left, TimeRange right) - { - return left.CompareTo(right) > 0; - } - - /// - /// Compares this TimeRange to another TimeRange. - /// - /// Other object to compare against. - /// A signed integer that indicates whether this instance precedes, follows, or appears in the same position in the sort order as the obj parameter. + /// Object to compare with. + /// int. public int CompareTo(object? obj) { - if (obj is not TimeRange tr) + if (!(obj is TimeRange tr)) { - return 0; + throw new ArgumentException("obj must be a TimeRange"); } - return this.Duration.CompareTo(tr.Duration); - } - - /// - public override bool Equals(object? obj) - { - if (obj is null || obj is not TimeRange tr) - { - return false; - } - - return this.Start == tr.Start && this.Duration == tr.Duration; - } - - /// - public override int GetHashCode() - { - return this.Start.GetHashCode() + this.Duration.GetHashCode(); + return tr.Duration.CompareTo(Duration); } } +#pragma warning restore CA1036 + /// /// Time range helpers. /// @@ -167,7 +95,7 @@ public static class TimeRangeHelpers Array.Sort(times); var ranges = new List(); - var currentRange = new TimeRange(times[0], 0); + var currentRange = new TimeRange(times[0], times[0]); // For all provided timestamps, check if it is contiguous with its neighbor. for (var i = 0; i < times.Length - 1; i++) @@ -182,7 +110,7 @@ public static class TimeRangeHelpers } ranges.Add(new TimeRange(currentRange)); - currentRange.Start = next; + currentRange = new TimeRange(next, next); } // Find and return the longest contiguous range. diff --git a/ConfusedPolarBear.Plugin.IntroSkipper/ScheduledTasks/FingerprinterTask.cs b/ConfusedPolarBear.Plugin.IntroSkipper/ScheduledTasks/FingerprinterTask.cs index 9d6cc2c..43072c9 100644 --- a/ConfusedPolarBear.Plugin.IntroSkipper/ScheduledTasks/FingerprinterTask.cs +++ b/ConfusedPolarBear.Plugin.IntroSkipper/ScheduledTasks/FingerprinterTask.cs @@ -21,13 +21,14 @@ public class FingerprinterTask : IScheduledTask /// /// Maximum number of bits (out of 32 total) that can be different between segments before they are considered dissimilar. + /// 8 bits means the audio must be at least 75% similar (1 - 8 / 32). /// - private const double MaximumDifferences = 3; + private const double MaximumDifferences = 8; /// /// Maximum time (in seconds) permitted between timestamps before they are considered non-contiguous. /// - private const double MaximumDistance = 3.25; + private const double MaximumDistance = 2.5; /// /// Seconds of audio in one fingerprint point. This value is defined by the Chromaprint library and should not be changed. @@ -192,7 +193,7 @@ public class FingerprinterTask : IScheduledTask var rhs = episodes[i + 1]; // TODO: make configurable - if (!everFoundIntro && failures >= 6) + if (!everFoundIntro && failures >= 20) { _logger.LogWarning( "Failed to find an introduction in {Series} season {Season}", @@ -312,7 +313,7 @@ public class FingerprinterTask : IScheduledTask // If no valid ranges were found, re-analyze the episodes considering all possible shifts. if (lhsRanges.Count == 0) { - _logger.LogDebug("quick scan unsuccessful, falling back to full scan"); + _logger.LogDebug("quick scan unsuccessful, falling back to full scan (±{Limit})", limit); (lhsContiguous, rhsContiguous) = ShiftEpisodes(lhsPoints, rhsPoints, -1 * limit, limit); lhsRanges.AddRange(lhsContiguous);