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("", StringComparison.OrdinalIgnoreCase); contents = bodyIndex >= 0 ? contents.Insert(bodyIndex, tag + Environment.NewLine) : contents + Environment.NewLine + tag + Environment.NewLine; File.WriteAllText(indexPath, contents); _logger.LogInformation("Injected Reddit Comments script tag into {Path}", indexPath); } }