using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
namespace Jellyfin.Plugin.RedditComments.Services;
///
/// Injects the plugin's script tag into the Jellyfin web client's index.html at startup,
/// so the player gets the comments button. Re-applies automatically after web client updates.
///
public class WebScriptInjector : IHostedService
{
private const string ScriptMarker = "RedditComments/Static/reddit-comments.js";
private readonly ILogger _logger;
private readonly IApplicationPaths _applicationPaths;
private readonly IServerConfigurationManager _configurationManager;
///
/// Initializes a new instance of the class.
///
/// Logger.
/// Application paths.
/// Server configuration manager.
public WebScriptInjector(
ILogger logger,
IApplicationPaths applicationPaths,
IServerConfigurationManager configurationManager)
{
_logger = logger;
_applicationPaths = applicationPaths;
_configurationManager = configurationManager;
}
///
public Task StartAsync(CancellationToken cancellationToken)
{
try
{
InjectScriptTag();
}
catch (Exception ex)
{
_logger.LogWarning(
ex,
"Could not inject the Reddit Comments script into the web client. The API will still work; "
+ "to enable the player button, add this tag to index.html of jellyfin-web manually: {Tag}",
BuildScriptTag());
}
return Task.CompletedTask;
}
///
public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
private string BuildScriptTag()
{
var baseUrl = string.Empty;
try
{
if (_configurationManager.GetConfiguration("network") is NetworkConfiguration networkConfiguration)
{
baseUrl = (networkConfiguration.BaseUrl ?? string.Empty).TrimEnd('/');
}
}
catch (Exception ex)
{
_logger.LogDebug(ex, "Could not read the configured base URL, assuming none");
}
return string.Format(System.Globalization.CultureInfo.InvariantCulture, "", baseUrl, ScriptMarker);
}
private void InjectScriptTag()
{
var indexPath = Path.Combine(_applicationPaths.WebPath, "index.html");
if (!File.Exists(indexPath))
{
_logger.LogWarning("jellyfin-web index.html not found at {Path}, skipping script injection", indexPath);
return;
}
var contents = File.ReadAllText(indexPath);
if (contents.Contains(ScriptMarker, StringComparison.Ordinal))
{
_logger.LogDebug("Reddit Comments script tag already present in index.html");
return;
}
var tag = BuildScriptTag();
var bodyIndex = contents.LastIndexOf("