using System; using System.Linq; using System.Net.Mime; using MediaBrowser.Controller.Library; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; namespace JellyfinPlugin.Api { [ApiController] [Route("BlackBarRemover")] public class BlackBarController : ControllerBase { private readonly ILibraryManager _libraryManager; private readonly ILogger _logger; public BlackBarController(ILibraryManager libraryManager, ILogger logger) { _libraryManager = libraryManager; _logger = logger; } [HttpGet("CropByPath")] public ActionResult GetCropByPath([FromQuery] string path) { if (string.IsNullOrEmpty(path)) return Ok(string.Empty); var mode = Plugin.Instance!.Configuration.PlaybackMode; if (mode == "Client") return Ok(string.Empty); var item = _libraryManager.RootFolder.GetRecursiveChildren().FirstOrDefault(x => x.Path == path); if (item != null && item.ProviderIds != null && item.ProviderIds.TryGetValue("BlackBarCrop", out string? cropData)) { return Ok(cropData ?? string.Empty); } return Ok(string.Empty); } [HttpGet("CropById")] public ActionResult GetCropById([FromQuery] string itemId) { if (string.IsNullOrEmpty(itemId)) return Ok(string.Empty); var mode = Plugin.Instance!.Configuration.PlaybackMode; if (mode == "Server") return Ok(string.Empty); if (Guid.TryParse(itemId, out Guid id)) { var item = _libraryManager.GetItemById(id); if (item != null && item.ProviderIds != null && item.ProviderIds.TryGetValue("BlackBarCrop", out string? cropData)) { return Ok(cropData ?? string.Empty); } } return Ok(string.Empty); } [HttpGet("ClientScript.js")] [Produces("application/javascript")] public ContentResult GetClientScript() { string js = @" (function() { console.log('Black Bar Remover: Script loaded'); let currentVideoElement = null; function applyCrop(cropData) { if (!currentVideoElement) return; const parts = cropData.split(':'); if (parts.length === 4) { const w = parseInt(parts[0]); const h = parseInt(parts[1]); const x = parseInt(parts[2]); const y = parseInt(parts[3]); const topPercent = (y / 1080) * 100; const bottomPercent = ((1080 - (y + h)) / 1080) * 100; const leftPercent = (x / 1920) * 100; const rightPercent = ((1920 - (x + w)) / 1920) * 100; currentVideoElement.style.clipPath = `inset(${topPercent}% ${rightPercent}% ${bottomPercent}% ${leftPercent}%)`; const scaleY = 1 / (1 - (topPercent + bottomPercent)/100); const scaleX = 1 / (1 - (leftPercent + rightPercent)/100); const scale = Math.max(scaleY, scaleX); if (scale > 1.01) { currentVideoElement.style.transform = `scale(${scale})`; } console.log('Black Bar Remover: Applied crop', cropData, 'scale', scale); } } function checkPlayback() { const video = document.querySelector('video'); if (video && video !== currentVideoElement) { currentVideoElement = video; const params = new URLSearchParams(window.location.hash.split('?')[1]); const itemId = params.get('id') || params.get('itemId'); if (itemId) { fetch('/BlackBarRemover/CropById?itemId=' + itemId) .then(r => r.text()) .then(crop => { if (crop) applyCrop(crop); }); } } else if (!video && currentVideoElement) { currentVideoElement = null; } } setInterval(checkPlayback, 2000); })(); "; return Content(js, "application/javascript"); } } }