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>
|
||||
public static class Chromaprint
|
||||
{
|
||||
private static bool _loggedVersionInformation;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the logger.
|
||||
/// </summary>
|
||||
public static ILogger? Logger { get; set; }
|
||||
|
||||
private static Dictionary<string, string> ChromaprintLogs { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// Check that the installed version of ffmpeg supports chromaprint.
|
||||
/// </summary>
|
||||
@ -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"));
|
||||
}
|
||||
|
||||
/// <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>
|
||||
</div>
|
||||
|
||||
<details id="support">
|
||||
<summary>Support Bundle</summary>
|
||||
|
||||
<textarea id="supportBundle" rows="20" cols="75" readonly></textarea>
|
||||
</details>
|
||||
|
||||
<details id="statistics">
|
||||
<summary>Analysis Statistics (experimental)</summary>
|
||||
|
||||
@ -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);
|
||||
|
@ -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