Unit test time ranges and audio fingerprinting
This commit is contained in:
parent
61932fcf89
commit
928f467871
@ -0,0 +1,27 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net6.0</TargetFramework>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
|
||||||
|
<IsPackable>false</IsPackable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" />
|
||||||
|
<PackageReference Include="xunit" Version="2.4.1" />
|
||||||
|
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
|
||||||
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
|
<PrivateAssets>all</PrivateAssets>
|
||||||
|
</PackageReference>
|
||||||
|
<PackageReference Include="coverlet.collector" Version="3.1.0">
|
||||||
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
|
<PrivateAssets>all</PrivateAssets>
|
||||||
|
</PackageReference>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\ConfusedPolarBear.Plugin.IntroSkipper\ConfusedPolarBear.Plugin.IntroSkipper.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
@ -0,0 +1,74 @@
|
|||||||
|
using Xunit;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
|
namespace ConfusedPolarBear.Plugin.IntroSkipper.Tests;
|
||||||
|
|
||||||
|
public class TestFPCalc
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void TestInstallationCheck()
|
||||||
|
{
|
||||||
|
Assert.True(FPCalc.CheckFPCalcInstalled());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void TestFingerprinting()
|
||||||
|
{
|
||||||
|
// Generated with `fpcalc -raw audio/big_buck_bunny_intro.mp3`
|
||||||
|
var expected = new uint[]{
|
||||||
|
3269995649, 3261610160, 3257403872, 1109989680, 1109993760, 1110010656, 1110142768, 1110175504,
|
||||||
|
1110109952, 1126874880, 2788611, 2787586, 6981634, 15304754, 28891170, 43579426, 43542561,
|
||||||
|
47737888, 41608640, 40559296, 36352644, 53117572, 2851460, 1076465548, 1080662428, 1080662492,
|
||||||
|
1089182044, 1148041501, 1148037422, 3291343918, 3290980398, 3429367854, 3437756714, 3433698090,
|
||||||
|
3433706282, 3366600490, 3366464314, 2296916250, 3362269210, 3362265115, 3362266441, 3370784472,
|
||||||
|
3366605480, 1218990776, 1223217816, 1231602328, 1260950200, 1245491640, 169845176, 1510908120,
|
||||||
|
1510911000, 2114365528, 2114370008, 1996929688, 1996921480, 1897171592, 1884588680, 1347470984,
|
||||||
|
1343427226, 1345467054, 1349657318, 1348673570, 1356869666, 1356865570, 295837698, 60957698,
|
||||||
|
44194818, 48416770, 40011778, 36944210, 303147954, 369146786, 1463847842, 1434488738, 1417709474,
|
||||||
|
1417713570, 3699441634, 3712167202, 3741460534, 2585144342, 2597725238, 2596200487, 2595926077,
|
||||||
|
2595984141, 2594734600, 2594736648, 2598931176, 2586348264, 2586348264, 2586561257, 2586451659,
|
||||||
|
2603225802, 2603225930, 2573860970, 2561151018, 3634901034, 3634896954, 3651674122, 3416793162,
|
||||||
|
3416816715, 3404331257, 3395844345, 3395836155, 3408464089, 3374975369, 1282036360, 1290457736,
|
||||||
|
1290400440, 1290314408, 1281925800, 1277727404, 1277792932, 1278785460, 1561962388, 1426698196,
|
||||||
|
3607924711, 4131892839, 4140215815, 4292259591, 3218515717, 3209938229, 3171964197, 3171956013,
|
||||||
|
4229117295, 4229312879, 4242407935, 4238016959, 4239987133, 4239990013, 3703060732, 1547188252,
|
||||||
|
1278748677, 1278748935, 1144662786, 1148854786, 1090388802, 1090388962, 1086260130, 1085940098,
|
||||||
|
1102709122, 45811586, 44634002, 44596656, 44592544, 1122527648, 1109944736, 1109977504, 1111030243,
|
||||||
|
1111017762, 1109969186, 1126721826, 1101556002, 1084844322, 1084979506, 1084914450, 1084914449,
|
||||||
|
1084873520, 3228093296, 3224996817, 3225062275, 3241840002, 3346701698, 3349843394, 3349782306,
|
||||||
|
3349719842, 3353914146, 3328748322, 3328747810, 3328809266, 3471476754, 3472530451, 3472473123,
|
||||||
|
3472417825, 3395841056, 3458735136, 3341420624, 1076496560, 1076501168, 1076501136, 1076497024
|
||||||
|
};
|
||||||
|
|
||||||
|
var actual = FPCalc.Fingerprint(queueEpisode("audio/big_buck_bunny_intro.mp3"));
|
||||||
|
|
||||||
|
Assert.Equal(expected, actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void TestIntroDetection()
|
||||||
|
{
|
||||||
|
var logger = new Logger<FingerprinterTask>(new LoggerFactory());
|
||||||
|
var task = new FingerprinterTask(logger);
|
||||||
|
|
||||||
|
var lhs = queueEpisode("audio/big_buck_bunny_intro.mp3");
|
||||||
|
var rhs = queueEpisode("audio/big_buck_bunny_clip.mp3");
|
||||||
|
|
||||||
|
var result = task.FingerprintEpisodes(lhs, rhs);
|
||||||
|
var actual = FingerprinterTask.LastIntro;
|
||||||
|
|
||||||
|
Assert.True(result);
|
||||||
|
Assert.True(actual.Valid);
|
||||||
|
Assert.Equal(5.12, actual.IntroStart);
|
||||||
|
Assert.Equal(22.912, actual.IntroEnd);
|
||||||
|
}
|
||||||
|
|
||||||
|
private QueuedEpisode queueEpisode(string path)
|
||||||
|
{
|
||||||
|
return new QueuedEpisode()
|
||||||
|
{
|
||||||
|
Path = "../../../" + path,
|
||||||
|
FingerprintDuration = 60
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,36 @@
|
|||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace ConfusedPolarBear.Plugin.IntroSkipper.Tests;
|
||||||
|
|
||||||
|
public class TestTimeRanges
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void TestSmallRange()
|
||||||
|
{
|
||||||
|
var times = new double[]{
|
||||||
|
1, 1.5, 2, 2.5, 3, 3.5, 4,
|
||||||
|
100, 100.5, 101, 101.5
|
||||||
|
};
|
||||||
|
|
||||||
|
var expected = new TimeRange(1, 4);
|
||||||
|
var actual = TimeRangeHelpers.FindContiguous(times, 3.25);
|
||||||
|
|
||||||
|
Assert.Equal(expected, actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void TestLargeRange()
|
||||||
|
{
|
||||||
|
var times = new double[]{
|
||||||
|
1, 1.5, 2,
|
||||||
|
2.8, 2.9, 2.995, 3.0, 3.01, 3.02, 3.4, 3.45, 3.48, 3.7, 3.77, 3.78, 3.781, 3.782, 3.789, 3.85,
|
||||||
|
4.5, 5.3122, 5.3123, 5.3124, 5.3125, 5.3126, 5.3127, 5.3128,
|
||||||
|
55, 55.5, 55.6, 55.7
|
||||||
|
};
|
||||||
|
|
||||||
|
var expected = new TimeRange(1, 5.3128);
|
||||||
|
var actual = TimeRangeHelpers.FindContiguous(times, 3.25);
|
||||||
|
|
||||||
|
Assert.Equal(expected, actual);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
The audio used in the fingerprinting unit tests is from Big Buck Bunny, attributed below.
|
||||||
|
|
||||||
|
Both big_buck_bunny_intro.mp3 and big_buck_bunny_clip.mp3 are derived from Big Buck Bunny, (c) copyright 2008, Blender Foundation / www.bigbuckbunny.org. They are used under the Creative Commons Attribution 3.0 and the original source can be found at https://www.youtube.com/watch?v=YE7VzlLtp-4.
|
||||||
|
|
||||||
|
Both files have been downmixed to two audio channels.
|
||||||
|
big_buck_bunny_intro.mp3 is from 5 to 30 seconds.
|
||||||
|
big_buck_bunny_clip.mp3 is from 0 to 60 seconds.
|
Binary file not shown.
Binary file not shown.
@ -1,6 +1,9 @@
|
|||||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
|
#
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConfusedPolarBear.Plugin.IntroSkipper", "ConfusedPolarBear.Plugin.IntroSkipper\ConfusedPolarBear.Plugin.IntroSkipper.csproj", "{D921B930-CF91-406F-ACBC-08914DCD0D34}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConfusedPolarBear.Plugin.IntroSkipper", "ConfusedPolarBear.Plugin.IntroSkipper\ConfusedPolarBear.Plugin.IntroSkipper.csproj", "{D921B930-CF91-406F-ACBC-08914DCD0D34}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConfusedPolarBear.Plugin.IntroSkipper.Tests", "ConfusedPolarBear.Plugin.IntroSkipper.Tests\ConfusedPolarBear.Plugin.IntroSkipper.Tests.csproj", "{9E30DA42-983E-46E0-A3BF-A2BA56FE9718}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
@ -11,5 +14,9 @@ Global
|
|||||||
{D921B930-CF91-406F-ACBC-08914DCD0D34}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{D921B930-CF91-406F-ACBC-08914DCD0D34}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{D921B930-CF91-406F-ACBC-08914DCD0D34}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{D921B930-CF91-406F-ACBC-08914DCD0D34}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{D921B930-CF91-406F-ACBC-08914DCD0D34}.Release|Any CPU.Build.0 = Release|Any CPU
|
{D921B930-CF91-406F-ACBC-08914DCD0D34}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{9E30DA42-983E-46E0-A3BF-A2BA56FE9718}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{9E30DA42-983E-46E0-A3BF-A2BA56FE9718}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{9E30DA42-983E-46E0-A3BF-A2BA56FE9718}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{9E30DA42-983E-46E0-A3BF-A2BA56FE9718}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
EndGlobal
|
EndGlobal
|
||||||
|
@ -115,7 +115,7 @@ public static class FPCalc {
|
|||||||
fingerprint = new List<uint>().AsReadOnly();
|
fingerprint = new List<uint>().AsReadOnly();
|
||||||
|
|
||||||
// If fingerprint caching isn't enabled, don't try to load anything.
|
// If fingerprint caching isn't enabled, don't try to load anything.
|
||||||
if (!Plugin.Instance!.Configuration.CacheFingerprints)
|
if (!(Plugin.Instance?.Configuration.CacheFingerprints ?? false))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -151,7 +151,7 @@ public static class FPCalc {
|
|||||||
private static void cacheFingerprint(QueuedEpisode episode, List<uint> fingerprint)
|
private static void cacheFingerprint(QueuedEpisode episode, List<uint> fingerprint)
|
||||||
{
|
{
|
||||||
// Bail out if caching isn't enabled.
|
// Bail out if caching isn't enabled.
|
||||||
if (!Plugin.Instance!.Configuration.CacheFingerprints)
|
if (!(Plugin.Instance?.Configuration.CacheFingerprints ?? false))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -34,6 +34,11 @@ public class FingerprinterTask : IScheduledTask {
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private const double SAMPLES_TO_SECONDS = 0.128;
|
private const double SAMPLES_TO_SECONDS = 0.128;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the last detected intro sequence. Only populated when a unit test is running.
|
||||||
|
/// </summary>
|
||||||
|
public static Intro LastIntro { get; private set; } = new Intro();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Constructor.
|
/// Constructor.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -173,7 +178,7 @@ public class FingerprinterTask : IScheduledTask {
|
|||||||
/// <param name="lhsEpisode">First episode to analyze.</param>
|
/// <param name="lhsEpisode">First episode to analyze.</param>
|
||||||
/// <param name="rhsEpisode">Second episode to analyze.</param>
|
/// <param name="rhsEpisode">Second episode to analyze.</param>
|
||||||
/// <returns>true if an intro was found in both episodes, otherwise false.</returns>
|
/// <returns>true if an intro was found in both episodes, otherwise false.</returns>
|
||||||
private bool FingerprintEpisodes(QueuedEpisode lhsEpisode, QueuedEpisode rhsEpisode)
|
public bool FingerprintEpisodes(QueuedEpisode lhsEpisode, QueuedEpisode rhsEpisode)
|
||||||
{
|
{
|
||||||
var lhs = FPCalc.Fingerprint(lhsEpisode);
|
var lhs = FPCalc.Fingerprint(lhsEpisode);
|
||||||
var rhs = FPCalc.Fingerprint(rhsEpisode);
|
var rhs = FPCalc.Fingerprint(rhsEpisode);
|
||||||
@ -353,13 +358,21 @@ public class FingerprinterTask : IScheduledTask {
|
|||||||
|
|
||||||
private static void storeIntro(Guid episode, double introStart, double introEnd)
|
private static void storeIntro(Guid episode, double introStart, double introEnd)
|
||||||
{
|
{
|
||||||
Plugin.Instance!.Intros[episode] = new Intro()
|
var intro = new Intro()
|
||||||
{
|
{
|
||||||
EpisodeId = episode,
|
EpisodeId = episode,
|
||||||
Valid = introEnd > 0,
|
Valid = introEnd > 0,
|
||||||
IntroStart = introStart,
|
IntroStart = introStart,
|
||||||
IntroEnd = introEnd
|
IntroEnd = introEnd
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (Plugin.Instance is null)
|
||||||
|
{
|
||||||
|
LastIntro = intro;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Plugin.Instance.Intros[episode] = intro;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int countBits(uint number) {
|
private static int countBits(uint number) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user