diff --git a/ConfusedPolarBear.Plugin.IntroSkipper.Tests/TestAudioFingerprinting.cs b/ConfusedPolarBear.Plugin.IntroSkipper.Tests/TestAudioFingerprinting.cs
index a940067..c0da162 100644
--- a/ConfusedPolarBear.Plugin.IntroSkipper.Tests/TestAudioFingerprinting.cs
+++ b/ConfusedPolarBear.Plugin.IntroSkipper.Tests/TestAudioFingerprinting.cs
@@ -108,6 +108,29 @@ public class TestAudioFingerprinting
Assert.Equal(22.912, rhs.IntroEnd);
}
+ ///
+ /// Test that the silencedetect wrapper is working.
+ ///
+ [FactSkipFFmpegTests]
+ public void TestSilenceDetection()
+ {
+ var clip = queueEpisode("audio/big_buck_bunny_clip.mp3");
+
+ var expected = new TimeRange[]
+ {
+ new TimeRange(44.6310, 44.8072),
+ new TimeRange(53.5905, 53.8070),
+ new TimeRange(53.8458, 54.2024),
+ new TimeRange(54.2611, 54.5935),
+ new TimeRange(54.7098, 54.9293),
+ new TimeRange(54.9294, 55.2590),
+ };
+
+ var actual = FFmpegWrapper.DetectSilence(clip, 60);
+
+ Assert.Equal(expected, actual);
+ }
+
private QueuedEpisode queueEpisode(string path)
{
return new QueuedEpisode()
diff --git a/ConfusedPolarBear.Plugin.IntroSkipper.Tests/TestContiguous.cs b/ConfusedPolarBear.Plugin.IntroSkipper.Tests/TestContiguous.cs
index 286aa0c..50121f6 100644
--- a/ConfusedPolarBear.Plugin.IntroSkipper.Tests/TestContiguous.cs
+++ b/ConfusedPolarBear.Plugin.IntroSkipper.Tests/TestContiguous.cs
@@ -71,4 +71,22 @@ public class TestTimeRanges
Assert.Equal(expected, actual);
}
+
+ ///
+ /// Tests that TimeRange intersections are detected correctly.
+ /// Tests each time range against a range of 5 to 10 seconds.
+ ///
+ [Theory]
+ [InlineData(1, 4, false)] // too early
+ [InlineData(4, 6, true)] // intersects on the left
+ [InlineData(7, 8, true)] // in the middle
+ [InlineData(9, 12, true)] // intersects on the right
+ [InlineData(13, 15, false)] // too late
+ public void TestTimeRangeIntersection(int start, int end, bool expected)
+ {
+ var large = new TimeRange(5, 10);
+ var testRange = new TimeRange(start, end);
+
+ Assert.Equal(expected, large.Intersects(testRange));
+ }
}
diff --git a/ConfusedPolarBear.Plugin.IntroSkipper/AutoSkip.cs b/ConfusedPolarBear.Plugin.IntroSkipper/AutoSkip.cs
index 3846bb9..1cac686 100644
--- a/ConfusedPolarBear.Plugin.IntroSkipper/AutoSkip.cs
+++ b/ConfusedPolarBear.Plugin.IntroSkipper/AutoSkip.cs
@@ -169,6 +169,8 @@ public class AutoSkip : IServerEntryPoint
// Send the seek command
_logger.LogDebug("Sending seek command to {Session}", deviceId);
+ var introEnd = (long)intro.IntroEnd - Plugin.Instance!.Configuration.AmountOfIntroToPlay;
+
_sessionManager.SendPlaystateCommand(
session.Id,
session.Id,
@@ -176,7 +178,7 @@ public class AutoSkip : IServerEntryPoint
{
Command = PlaystateCommand.Seek,
ControllingUserId = session.UserId.ToString("N"),
- SeekPositionTicks = (long)intro.IntroEnd * TimeSpan.TicksPerSecond,
+ SeekPositionTicks = introEnd * TimeSpan.TicksPerSecond,
},
CancellationToken.None);
diff --git a/ConfusedPolarBear.Plugin.IntroSkipper/Configuration/PluginConfiguration.cs b/ConfusedPolarBear.Plugin.IntroSkipper/Configuration/PluginConfiguration.cs
index 741c561..d98554c 100644
--- a/ConfusedPolarBear.Plugin.IntroSkipper/Configuration/PluginConfiguration.cs
+++ b/ConfusedPolarBear.Plugin.IntroSkipper/Configuration/PluginConfiguration.cs
@@ -62,6 +62,17 @@ public class PluginConfiguration : BasePluginConfiguration
///
public int MinimumIntroDuration { get; set; } = 15;
+ ///
+ /// Gets or sets the maximum amount of noise (in dB) that is considered silent.
+ /// Lowering this number will increase the filter's sensitivity to noise.
+ ///
+ public int SilenceDetectionMaximumNoise { get; set; } = -50;
+
+ ///
+ /// Gets or sets the minimum duration of audio (in seconds) that is considered silent.
+ ///
+ public double SilenceDetectionMinimumDuration { get; set; } = 0.50;
+
// ===== Playback settings =====
///
@@ -83,5 +94,5 @@ public class PluginConfiguration : BasePluginConfiguration
/// Gets or sets the amount of intro to play (in seconds).
/// TODO: rename.
///
- public int AmountOfIntroToPlay { get; set; } = 5;
+ public int AmountOfIntroToPlay { get; set; } = 2;
}
diff --git a/ConfusedPolarBear.Plugin.IntroSkipper/Data/TimeRange.cs b/ConfusedPolarBear.Plugin.IntroSkipper/Data/TimeRange.cs
index 7e7b51b..451df2f 100644
--- a/ConfusedPolarBear.Plugin.IntroSkipper/Data/TimeRange.cs
+++ b/ConfusedPolarBear.Plugin.IntroSkipper/Data/TimeRange.cs
@@ -69,6 +69,18 @@ public class TimeRange : IComparable
return tr.Duration.CompareTo(Duration);
}
+
+ ///
+ /// Tests if this TimeRange object intersects the provided TimeRange.
+ ///
+ /// Second TimeRange object to test.
+ /// true if tr intersects the current TimeRange, false otherwise.
+ public bool Intersects(TimeRange tr)
+ {
+ return
+ (Start < tr.Start && tr.Start < End) ||
+ (Start < tr.End && tr.End < End);
+ }
}
#pragma warning restore CA1036
diff --git a/ConfusedPolarBear.Plugin.IntroSkipper/FFmpegWrapper.cs b/ConfusedPolarBear.Plugin.IntroSkipper/FFmpegWrapper.cs
index bda56ef..7852b97 100644
--- a/ConfusedPolarBear.Plugin.IntroSkipper/FFmpegWrapper.cs
+++ b/ConfusedPolarBear.Plugin.IntroSkipper/FFmpegWrapper.cs
@@ -4,6 +4,7 @@ using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Text;
+using System.Text.RegularExpressions;
using Microsoft.Extensions.Logging;
namespace ConfusedPolarBear.Plugin.IntroSkipper;
@@ -13,6 +14,16 @@ namespace ConfusedPolarBear.Plugin.IntroSkipper;
///
public static class FFmpegWrapper
{
+ // FFmpeg logs lines similar to the following:
+ // [silencedetect @ 0x000000000000] silence_start: 12.34
+ // [silencedetect @ 0x000000000000] silence_end: 56.123 | silence_duration: 43.783
+
+ ///
+ /// Used with FFmpeg's silencedetect filter to extract the start and end times of silence.
+ ///
+ private static readonly Regex SilenceDetectionExpression = new(
+ "silence_(?start|end): (?