From 69ddd7a20de629518f4170e8bf28c7f3ba567a74 Mon Sep 17 00:00:00 2001
From: ConfusedPolarBear <33811686+ConfusedPolarBear@users.noreply.github.com>
Date: Thu, 25 Aug 2022 00:39:20 -0500
Subject: [PATCH] Add support bundle
Bundles include the following information:
- Jellyfin version
- Plugin version
- Chromaprint detection status and logs
---
.../Chromaprint.cs | 69 ++++++++++++++++---
.../Configuration/configPage.html | 41 +++++++++++
.../Controllers/TroubleshootingController.cs | 65 +++++++++++++++++
3 files changed, 166 insertions(+), 9 deletions(-)
create mode 100644 ConfusedPolarBear.Plugin.IntroSkipper/Controllers/TroubleshootingController.cs
diff --git a/ConfusedPolarBear.Plugin.IntroSkipper/Chromaprint.cs b/ConfusedPolarBear.Plugin.IntroSkipper/Chromaprint.cs
index c969f61..68fd809 100644
--- a/ConfusedPolarBear.Plugin.IntroSkipper/Chromaprint.cs
+++ b/ConfusedPolarBear.Plugin.IntroSkipper/Chromaprint.cs
@@ -13,13 +13,13 @@ namespace ConfusedPolarBear.Plugin.IntroSkipper;
///
public static class Chromaprint
{
- private static bool _loggedVersionInformation;
-
///
/// Gets or sets the logger.
///
public static ILogger? Logger { get; set; }
+ private static Dictionary ChromaprintLogs { get; set; } = new();
+
///
/// Check that the installed version of ffmpeg supports chromaprint.
///
@@ -28,44 +28,47 @@ public static class Chromaprint
{
try
{
- // Log the output of "ffmpeg -version" at the first call to this function
- if (!_loggedVersionInformation)
- {
- _loggedVersionInformation = true;
- var version = Encoding.UTF8.GetString(GetOutput("-version", 2000));
- Logger?.LogDebug("ffmpeg version information: {Version}", version);
- }
+ // Log the output of "ffmpeg -version".
+ ChromaprintLogs["version"] = Encoding.UTF8.GetString(GetOutput("-version", 2000));
+ Logger?.LogDebug("ffmpeg version information: {Version}", ChromaprintLogs["version"]);
// First, validate that the installed version of ffmpeg supports chromaprint at all.
var muxers = Encoding.UTF8.GetString(GetOutput("-muxers", 2000));
+ ChromaprintLogs["muxer list"] = muxers;
Logger?.LogTrace("ffmpeg muxers: {Muxers}", muxers);
if (!muxers.Contains("chromaprint", StringComparison.OrdinalIgnoreCase))
{
+ ChromaprintLogs["error"] = "muxer_not_supported";
Logger?.LogError("The installed version of ffmpeg does not support chromaprint");
return false;
}
// Second, validate that ffmpeg understands the "-fp_format raw" option.
var muxerHelp = Encoding.UTF8.GetString(GetOutput("-h muxer=chromaprint", 2000));
+ ChromaprintLogs["muxer options"] = muxerHelp;
Logger?.LogTrace("ffmpeg chromaprint help: {MuxerHelp}", muxerHelp);
if (!muxerHelp.Contains("-fp_format", StringComparison.OrdinalIgnoreCase))
{
+ ChromaprintLogs["error"] = "fp_format_not_supported";
Logger?.LogError("The installed version of ffmpeg does not support the -fp_format flag");
return false;
}
else if (!muxerHelp.Contains("binary raw fingerprint", StringComparison.OrdinalIgnoreCase))
{
+ ChromaprintLogs["error"] = "fp_format_raw_not_supported";
Logger?.LogError("The installed version of ffmpeg does not support raw binary fingerprints");
return false;
}
Logger?.LogDebug("Installed version of ffmpeg meets fingerprinting requirements");
+ ChromaprintLogs["error"] = "okay";
return true;
}
catch
{
+ ChromaprintLogs["error"] = "unknown_error";
return false;
}
}
@@ -273,4 +276,52 @@ public static class Chromaprint
{
return Path.Join(Plugin.Instance!.FingerprintCachePath, episode.EpisodeId.ToString("N"));
}
+
+ ///
+ /// Gets Chromaprint debugging logs.
+ ///
+ /// Markdown formatted logs.
+ public static string GetChromaprintLogs()
+ {
+ var logs = new StringBuilder(1024);
+
+ // Print the Chromaprint detection status at the top.
+ // Format: "* Chromaprint: `error`"
+ logs.Append("* Chromaprint: `");
+ logs.Append(ChromaprintLogs["error"]);
+ logs.Append("`\n\n"); // Use two newlines to separate the bulleted list from the logs
+
+ // Print all remaining logs
+ foreach (var kvp in ChromaprintLogs)
+ {
+ var name = kvp.Key;
+ var contents = kvp.Value;
+
+ if (string.Equals(name, "error", StringComparison.OrdinalIgnoreCase))
+ {
+ continue;
+ }
+
+ /* Format:
+ * FFmpeg NAME:
+ * ```
+ * LOGS
+ * ```
+ */
+ logs.Append("FFmpeg ");
+ logs.Append(name);
+ logs.Append(":\n```\n");
+ logs.Append(contents);
+
+ // ensure the closing triple backtick is on a separate line
+ if (!contents.EndsWith('\n'))
+ {
+ logs.Append('\n');
+ }
+
+ logs.Append("```\n\n");
+ }
+
+ return logs.ToString();
+ }
}
diff --git a/ConfusedPolarBear.Plugin.IntroSkipper/Configuration/configPage.html b/ConfusedPolarBear.Plugin.IntroSkipper/Configuration/configPage.html
index ec3401a..a385500 100644
--- a/ConfusedPolarBear.Plugin.IntroSkipper/Configuration/configPage.html
+++ b/ConfusedPolarBear.Plugin.IntroSkipper/Configuration/configPage.html
@@ -211,6 +211,12 @@
+
+ Support Bundle
+
+
+
+
Analysis Statistics (experimental)
@@ -326,6 +332,7 @@
// settings elements
var visualizer = document.querySelector("details#visualizer");
var statistics = document.querySelector("details#statistics");
+ var support = document.querySelector("details#support");
var btnEraseTimestamps = document.querySelector("button#btnEraseTimestamps");
// all plugin configuration fields that can be get or set with .value (i.e. strings or numbers).
@@ -377,6 +384,39 @@
Dashboard.hideLoadingMsg();
}
+ // fetch the support bundle whenever the detail section is opened.
+ async function supportToggled() {
+ if (!support.open) {
+ return;
+ }
+
+ // Fetch the support bundle
+ const bundle = await fetchWithAuth("IntroSkipper/SupportBundle", "GET", null);
+ const bundleText = await bundle.text();
+
+ // Display it to the user
+ document.querySelector("textarea#supportBundle").value = bundleText;
+
+ // Attempt to copy it to the clipboard automatically, falling back to selecting
+ // all text and prompting the user to press ctrl c.
+ try {
+ navigator.clipboard.writeText(bundleText)
+ Dashboard.alert("Support bundle copied to clipboard");
+ } catch {
+ supportBundleCopyFailed();
+ }
+ }
+
+ // if the automatic bundle copy fails (likely due to an insecure context), have the
+ // user copy it manually.
+ function supportBundleCopyFailed() {
+ const ta = document.querySelector("textarea#supportBundle");
+ ta.focus();
+ ta.setSelectionRange(0, ta.value.length);
+
+ Dashboard.alert("Press Ctrl+C to copy support bundle");
+ }
+
async function statisticsToggled() {
if (!statistics.open) {
return;
@@ -660,6 +700,7 @@
visualizer.addEventListener("toggle", visualizerToggled);
statistics.addEventListener("toggle", statisticsToggled);
+ support.addEventListener("toggle", supportToggled);
txtOffset.addEventListener("change", renderTroubleshooter);
selectShow.addEventListener("change", showChanged);
selectSeason.addEventListener("change", seasonChanged);
diff --git a/ConfusedPolarBear.Plugin.IntroSkipper/Controllers/TroubleshootingController.cs b/ConfusedPolarBear.Plugin.IntroSkipper/Controllers/TroubleshootingController.cs
new file mode 100644
index 0000000..3554feb
--- /dev/null
+++ b/ConfusedPolarBear.Plugin.IntroSkipper/Controllers/TroubleshootingController.cs
@@ -0,0 +1,65 @@
+using System.Net.Mime;
+using System.Text;
+using MediaBrowser.Common;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.Extensions.Logging;
+
+namespace ConfusedPolarBear.Plugin.IntroSkipper.Controllers;
+
+///
+/// Troubleshooting controller.
+///
+[Authorize(Policy = "RequiresElevation")]
+[ApiController]
+[Produces(MediaTypeNames.Application.Json)]
+[Route("IntroSkipper")]
+public class TroubleshootingController : ControllerBase
+{
+ private readonly IApplicationHost _applicationHost;
+ private readonly ILogger _logger;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// Application host.
+ /// Logger.
+ public TroubleshootingController(
+ IApplicationHost applicationHost,
+ ILogger logger)
+ {
+ _applicationHost = applicationHost;
+ _logger = logger;
+ }
+
+ ///
+ /// Gets a Markdown formatted support bundle.
+ ///
+ /// Support bundle created.
+ /// Support bundle.
+ [HttpGet("SupportBundle")]
+ [Produces(MediaTypeNames.Text.Plain)]
+ public ActionResult GetSupportBundle()
+ {
+ var bundle = new StringBuilder();
+
+ bundle.Append("* Jellyfin version: ");
+ bundle.Append(_applicationHost.ApplicationVersionString);
+ bundle.Append('\n');
+
+ bundle.Append("* Plugin version: ");
+ bundle.Append(Plugin.Instance!.Version.ToString(3));
+ bundle.Append('\n');
+
+ bundle.Append("* Queue contents: ");
+ bundle.Append(Plugin.Instance!.TotalQueued);
+ bundle.Append(" episodes, ");
+ bundle.Append(Plugin.Instance!.AnalysisQueue.Count);
+ bundle.Append(" seasons");
+ bundle.Append('\n');
+
+ bundle.Append(Chromaprint.GetChromaprintLogs());
+
+ return bundle.ToString();
+ }
+}