diff --git a/ConfusedPolarBear.Plugin.IntroSkipper/FFmpegWrapper.cs b/ConfusedPolarBear.Plugin.IntroSkipper/FFmpegWrapper.cs index fdae152..66fc68b 100644 --- a/ConfusedPolarBear.Plugin.IntroSkipper/FFmpegWrapper.cs +++ b/ConfusedPolarBear.Plugin.IntroSkipper/FFmpegWrapper.cs @@ -98,38 +98,6 @@ public static bool CheckFFmpegVersion() } } - /// - /// Run an FFmpeg command with the provided arguments and validate that the output contains - /// the provided string. - /// - /// Arguments to pass to FFmpeg. - /// String that the output must contain. Case insensitive. - /// Support bundle key to store FFmpeg's output under. - /// Error message to log if this requirement is not met. - /// true on success, false on error. - private static bool CheckFFmpegRequirement( - string arguments, - string mustContain, - string bundleName, - string errorMessage) - { - Logger?.LogDebug("Checking FFmpeg requirement {Arguments}", arguments); - - var output = Encoding.UTF8.GetString(GetOutput(arguments, string.Empty, false, 2000)); - Logger?.LogTrace("Output of ffmpeg {Arguments}: {Output}", arguments, output); - ChromaprintLogs[bundleName] = output; - - if (!output.Contains(mustContain, StringComparison.OrdinalIgnoreCase)) - { - Logger?.LogError("{ErrorMessage}", errorMessage); - return false; - } - - Logger?.LogDebug("FFmpeg requirement {Arguments} met", arguments); - - return true; - } - /// /// Fingerprint a queued episode. /// @@ -158,58 +126,6 @@ public static uint[] Fingerprint(QueuedEpisode episode, AnalysisMode mode) return Fingerprint(episode, mode, start, end); } - /// - /// Fingerprint a queued episode. - /// - /// Queued episode to fingerprint. - /// Portion of media file to fingerprint. - /// Time (in seconds) relative to the start of the file to start fingerprinting from. - /// Time (in seconds) relative to the start of the file to stop fingerprinting at. - /// Numerical fingerprint points. - private static uint[] Fingerprint(QueuedEpisode episode, AnalysisMode mode, int start, int end) - { - // Try to load this episode from cache before running ffmpeg. - if (LoadCachedFingerprint(episode, mode, out uint[] cachedFingerprint)) - { - Logger?.LogTrace("Fingerprint cache hit on {File}", episode.Path); - return cachedFingerprint; - } - - Logger?.LogDebug( - "Fingerprinting [{Start}, {End}] from \"{File}\" (id {Id})", - start, - end, - episode.Path, - episode.EpisodeId); - - var args = string.Format( - CultureInfo.InvariantCulture, - "-ss {0} -i \"{1}\" -to {2} -ac 2 -f chromaprint -fp_format raw -", - start, - episode.Path, - end - start); - - // Returns all fingerprint points as raw 32 bit unsigned integers (little endian). - var rawPoints = GetOutput(args, string.Empty); - if (rawPoints.Length == 0 || rawPoints.Length % 4 != 0) - { - Logger?.LogWarning("Chromaprint returned {Count} points for \"{Path}\"", rawPoints.Length, episode.Path); - throw new FingerprintException("chromaprint output for \"" + episode.Path + "\" was malformed"); - } - - var results = new List(); - for (var i = 0; i < rawPoints.Length; i += 4) - { - var rawPoint = rawPoints.Slice(i, 4); - results.Add(BitConverter.ToUInt32(rawPoint)); - } - - // Try to cache this fingerprint. - CacheFingerprint(episode, mode, results); - - return results.ToArray(); - } - /// /// Transforms a Chromaprint into an inverted index of fingerprint points to the last index it appeared at. /// @@ -295,6 +211,75 @@ public static TimeRange[] DetectSilence(QueuedEpisode episode, int limit) return silenceRanges.ToArray(); } + /// + /// Gets Chromaprint debugging logs. + /// + /// Markdown formatted logs. + public static string GetChromaprintLogs() + { + // Print the FFmpeg detection status at the top. + // Format: "* FFmpeg: `error`" + // Append two newlines to separate the bulleted list from the logs + var logs = string.Format( + CultureInfo.InvariantCulture, + "* FFmpeg: `{0}`\n\n", + ChromaprintLogs["error"]); + + // Always include ffmpeg version information + logs += FormatFFmpegLog("version"); + + // Don't print feature detection logs if the plugin started up okay + if (ChromaprintLogs["error"] == "okay") + { + return logs; + } + + // Print all remaining logs + foreach (var kvp in ChromaprintLogs) + { + if (kvp.Key == "error" || kvp.Key == "version") + { + continue; + } + + logs += FormatFFmpegLog(kvp.Key); + } + + return logs; + } + + /// + /// Run an FFmpeg command with the provided arguments and validate that the output contains + /// the provided string. + /// + /// Arguments to pass to FFmpeg. + /// String that the output must contain. Case insensitive. + /// Support bundle key to store FFmpeg's output under. + /// Error message to log if this requirement is not met. + /// true on success, false on error. + private static bool CheckFFmpegRequirement( + string arguments, + string mustContain, + string bundleName, + string errorMessage) + { + Logger?.LogDebug("Checking FFmpeg requirement {Arguments}", arguments); + + var output = Encoding.UTF8.GetString(GetOutput(arguments, string.Empty, false, 2000)); + Logger?.LogTrace("Output of ffmpeg {Arguments}: {Output}", arguments, output); + ChromaprintLogs[bundleName] = output; + + if (!output.Contains(mustContain, StringComparison.OrdinalIgnoreCase)) + { + Logger?.LogError("{ErrorMessage}", errorMessage); + return false; + } + + Logger?.LogDebug("FFmpeg requirement {Arguments} met", arguments); + + return true; + } + /// /// Runs ffmpeg and returns standard output (or error). /// If caching is enabled, will use cacheFilename to cache the output of this command. @@ -392,6 +377,58 @@ private static ReadOnlySpan GetOutput( } } + /// + /// Fingerprint a queued episode. + /// + /// Queued episode to fingerprint. + /// Portion of media file to fingerprint. + /// Time (in seconds) relative to the start of the file to start fingerprinting from. + /// Time (in seconds) relative to the start of the file to stop fingerprinting at. + /// Numerical fingerprint points. + private static uint[] Fingerprint(QueuedEpisode episode, AnalysisMode mode, int start, int end) + { + // Try to load this episode from cache before running ffmpeg. + if (LoadCachedFingerprint(episode, mode, out uint[] cachedFingerprint)) + { + Logger?.LogTrace("Fingerprint cache hit on {File}", episode.Path); + return cachedFingerprint; + } + + Logger?.LogDebug( + "Fingerprinting [{Start}, {End}] from \"{File}\" (id {Id})", + start, + end, + episode.Path, + episode.EpisodeId); + + var args = string.Format( + CultureInfo.InvariantCulture, + "-ss {0} -i \"{1}\" -to {2} -ac 2 -f chromaprint -fp_format raw -", + start, + episode.Path, + end - start); + + // Returns all fingerprint points as raw 32 bit unsigned integers (little endian). + var rawPoints = GetOutput(args, string.Empty); + if (rawPoints.Length == 0 || rawPoints.Length % 4 != 0) + { + Logger?.LogWarning("Chromaprint returned {Count} points for \"{Path}\"", rawPoints.Length, episode.Path); + throw new FingerprintException("chromaprint output for \"" + episode.Path + "\" was malformed"); + } + + var results = new List(); + for (var i = 0; i < rawPoints.Length; i += 4) + { + var rawPoint = rawPoints.Slice(i, 4); + results.Add(BitConverter.ToUInt32(rawPoint)); + } + + // Try to cache this fingerprint. + CacheFingerprint(episode, mode, results); + + return results.ToArray(); + } + /// /// Tries to load an episode's fingerprint from cache. If caching is not enabled, calling this function is a no-op. /// This function was created before the unified caching mechanism was introduced (in v0.1.7). @@ -507,43 +544,6 @@ private static string GetFingerprintCachePath(QueuedEpisode episode, AnalysisMod } } - /// - /// Gets Chromaprint debugging logs. - /// - /// Markdown formatted logs. - public static string GetChromaprintLogs() - { - // Print the FFmpeg detection status at the top. - // Format: "* FFmpeg: `error`" - // Append two newlines to separate the bulleted list from the logs - var logs = string.Format( - CultureInfo.InvariantCulture, - "* FFmpeg: `{0}`\n\n", - ChromaprintLogs["error"]); - - // Always include ffmpeg version information - logs += FormatFFmpegLog("version"); - - // Don't print feature detection logs if the plugin started up okay - if (ChromaprintLogs["error"] == "okay") - { - return logs; - } - - // Print all remaining logs - foreach (var kvp in ChromaprintLogs) - { - if (kvp.Key == "error" || kvp.Key == "version") - { - continue; - } - - logs += FormatFFmpegLog(kvp.Key); - } - - return logs; - } - private static string FormatFFmpegLog(string key) { /* Format: