Add support bundle
Bundles include the following information: - Jellyfin version - Plugin version - Chromaprint detection status and logs
This commit is contained in:
parent
6f86fa935e
commit
69ddd7a20d
@ -13,13 +13,13 @@ namespace ConfusedPolarBear.Plugin.IntroSkipper;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static class Chromaprint
|
public static class Chromaprint
|
||||||
{
|
{
|
||||||
private static bool _loggedVersionInformation;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the logger.
|
/// Gets or sets the logger.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static ILogger? Logger { get; set; }
|
public static ILogger? Logger { get; set; }
|
||||||
|
|
||||||
|
private static Dictionary<string, string> ChromaprintLogs { get; set; } = new();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Check that the installed version of ffmpeg supports chromaprint.
|
/// Check that the installed version of ffmpeg supports chromaprint.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -28,44 +28,47 @@ public static class Chromaprint
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// Log the output of "ffmpeg -version" at the first call to this function
|
// Log the output of "ffmpeg -version".
|
||||||
if (!_loggedVersionInformation)
|
ChromaprintLogs["version"] = Encoding.UTF8.GetString(GetOutput("-version", 2000));
|
||||||
{
|
Logger?.LogDebug("ffmpeg version information: {Version}", ChromaprintLogs["version"]);
|
||||||
_loggedVersionInformation = true;
|
|
||||||
var version = Encoding.UTF8.GetString(GetOutput("-version", 2000));
|
|
||||||
Logger?.LogDebug("ffmpeg version information: {Version}", version);
|
|
||||||
}
|
|
||||||
|
|
||||||
// First, validate that the installed version of ffmpeg supports chromaprint at all.
|
// First, validate that the installed version of ffmpeg supports chromaprint at all.
|
||||||
var muxers = Encoding.UTF8.GetString(GetOutput("-muxers", 2000));
|
var muxers = Encoding.UTF8.GetString(GetOutput("-muxers", 2000));
|
||||||
|
ChromaprintLogs["muxer list"] = muxers;
|
||||||
Logger?.LogTrace("ffmpeg muxers: {Muxers}", muxers);
|
Logger?.LogTrace("ffmpeg muxers: {Muxers}", muxers);
|
||||||
|
|
||||||
if (!muxers.Contains("chromaprint", StringComparison.OrdinalIgnoreCase))
|
if (!muxers.Contains("chromaprint", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
|
ChromaprintLogs["error"] = "muxer_not_supported";
|
||||||
Logger?.LogError("The installed version of ffmpeg does not support chromaprint");
|
Logger?.LogError("The installed version of ffmpeg does not support chromaprint");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Second, validate that ffmpeg understands the "-fp_format raw" option.
|
// Second, validate that ffmpeg understands the "-fp_format raw" option.
|
||||||
var muxerHelp = Encoding.UTF8.GetString(GetOutput("-h muxer=chromaprint", 2000));
|
var muxerHelp = Encoding.UTF8.GetString(GetOutput("-h muxer=chromaprint", 2000));
|
||||||
|
ChromaprintLogs["muxer options"] = muxerHelp;
|
||||||
Logger?.LogTrace("ffmpeg chromaprint help: {MuxerHelp}", muxerHelp);
|
Logger?.LogTrace("ffmpeg chromaprint help: {MuxerHelp}", muxerHelp);
|
||||||
|
|
||||||
if (!muxerHelp.Contains("-fp_format", StringComparison.OrdinalIgnoreCase))
|
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");
|
Logger?.LogError("The installed version of ffmpeg does not support the -fp_format flag");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
else if (!muxerHelp.Contains("binary raw fingerprint", StringComparison.OrdinalIgnoreCase))
|
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");
|
Logger?.LogError("The installed version of ffmpeg does not support raw binary fingerprints");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger?.LogDebug("Installed version of ffmpeg meets fingerprinting requirements");
|
Logger?.LogDebug("Installed version of ffmpeg meets fingerprinting requirements");
|
||||||
|
ChromaprintLogs["error"] = "okay";
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
|
ChromaprintLogs["error"] = "unknown_error";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -273,4 +276,52 @@ public static class Chromaprint
|
|||||||
{
|
{
|
||||||
return Path.Join(Plugin.Instance!.FingerprintCachePath, episode.EpisodeId.ToString("N"));
|
return Path.Join(Plugin.Instance!.FingerprintCachePath, episode.EpisodeId.ToString("N"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets Chromaprint debugging logs.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Markdown formatted logs.</returns>
|
||||||
|
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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -211,6 +211,12 @@
|
|||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<details id="support">
|
||||||
|
<summary>Support Bundle</summary>
|
||||||
|
|
||||||
|
<textarea id="supportBundle" rows="20" cols="75" readonly></textarea>
|
||||||
|
</details>
|
||||||
|
|
||||||
<details id="statistics">
|
<details id="statistics">
|
||||||
<summary>Analysis Statistics (experimental)</summary>
|
<summary>Analysis Statistics (experimental)</summary>
|
||||||
|
|
||||||
@ -326,6 +332,7 @@
|
|||||||
// settings elements
|
// settings elements
|
||||||
var visualizer = document.querySelector("details#visualizer");
|
var visualizer = document.querySelector("details#visualizer");
|
||||||
var statistics = document.querySelector("details#statistics");
|
var statistics = document.querySelector("details#statistics");
|
||||||
|
var support = document.querySelector("details#support");
|
||||||
var btnEraseTimestamps = document.querySelector("button#btnEraseTimestamps");
|
var btnEraseTimestamps = document.querySelector("button#btnEraseTimestamps");
|
||||||
|
|
||||||
// all plugin configuration fields that can be get or set with .value (i.e. strings or numbers).
|
// all plugin configuration fields that can be get or set with .value (i.e. strings or numbers).
|
||||||
@ -377,6 +384,39 @@
|
|||||||
Dashboard.hideLoadingMsg();
|
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() {
|
async function statisticsToggled() {
|
||||||
if (!statistics.open) {
|
if (!statistics.open) {
|
||||||
return;
|
return;
|
||||||
@ -660,6 +700,7 @@
|
|||||||
|
|
||||||
visualizer.addEventListener("toggle", visualizerToggled);
|
visualizer.addEventListener("toggle", visualizerToggled);
|
||||||
statistics.addEventListener("toggle", statisticsToggled);
|
statistics.addEventListener("toggle", statisticsToggled);
|
||||||
|
support.addEventListener("toggle", supportToggled);
|
||||||
txtOffset.addEventListener("change", renderTroubleshooter);
|
txtOffset.addEventListener("change", renderTroubleshooter);
|
||||||
selectShow.addEventListener("change", showChanged);
|
selectShow.addEventListener("change", showChanged);
|
||||||
selectSeason.addEventListener("change", seasonChanged);
|
selectSeason.addEventListener("change", seasonChanged);
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Troubleshooting controller.
|
||||||
|
/// </summary>
|
||||||
|
[Authorize(Policy = "RequiresElevation")]
|
||||||
|
[ApiController]
|
||||||
|
[Produces(MediaTypeNames.Application.Json)]
|
||||||
|
[Route("IntroSkipper")]
|
||||||
|
public class TroubleshootingController : ControllerBase
|
||||||
|
{
|
||||||
|
private readonly IApplicationHost _applicationHost;
|
||||||
|
private readonly ILogger<TroubleshootingController> _logger;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="TroubleshootingController"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="applicationHost">Application host.</param>
|
||||||
|
/// <param name="logger">Logger.</param>
|
||||||
|
public TroubleshootingController(
|
||||||
|
IApplicationHost applicationHost,
|
||||||
|
ILogger<TroubleshootingController> logger)
|
||||||
|
{
|
||||||
|
_applicationHost = applicationHost;
|
||||||
|
_logger = logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a Markdown formatted support bundle.
|
||||||
|
/// </summary>
|
||||||
|
/// <response code="200">Support bundle created.</response>
|
||||||
|
/// <returns>Support bundle.</returns>
|
||||||
|
[HttpGet("SupportBundle")]
|
||||||
|
[Produces(MediaTypeNames.Text.Plain)]
|
||||||
|
public ActionResult<string> 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();
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user