using System; using System.IO; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Common.Configuration; using MediaBrowser.Controller.Configuration; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; namespace JellyfinPlugin { public class WrapperGenerator : IHostedService { private readonly ILogger _logger; private readonly IApplicationPaths _appPaths; private readonly IServerConfigurationManager _configManager; public WrapperGenerator(ILogger logger, IApplicationPaths appPaths, IServerConfigurationManager configManager) { _logger = logger; _appPaths = appPaths; _configManager = configManager; } public Task StartAsync(CancellationToken cancellationToken) { GenerateAndSetWrapper(); return Task.CompletedTask; } public Task StopAsync(CancellationToken cancellationToken) { return Task.CompletedTask; } private void GenerateAndSetWrapper() { try { var mode = Plugin.Instance!.Configuration.PlaybackMode; if (mode == "Client") { // Jeżeli wybrano tylko klienta, nie nadpisujemy ffmpeg return; } // Pobieramy obecną konfigurację kodowania var encodingConfig = _configManager.GetConfiguration("encoding"); if (encodingConfig == null) return; // Używamy dynamic, aby ominąć potrzebę rzutowania na konkretny, wewnętrzny typ Jellyfin (EncodingOptions) dynamic dynConfig = encodingConfig; string currentFfmpegPath = dynConfig.EncoderAppPath ?? "/usr/lib/jellyfin-ffmpeg/ffmpeg"; string wrapperDir = Path.Combine(_appPaths.PluginsPath, "BlackBarRemover"); Directory.CreateDirectory(wrapperDir); string wrapperPath = Path.Combine(wrapperDir, "ffmpeg-wrapper.sh"); // Jeśli obecna ścieżka to już nasz wrapper, nie robimy pętli w nieskończoność. // Znajdujemy poprzednio zapisaną prawdziwą ścieżkę z pliku. string realFfmpegPath = currentFfmpegPath; if (currentFfmpegPath == wrapperPath) { if (File.Exists(wrapperPath)) { var lines = File.ReadAllLines(wrapperPath); foreach (var line in lines) { if (line.StartsWith("REAL_FFMPEG=")) { realFfmpegPath = line.Split('=')[1].Trim('"'); break; } } } if (realFfmpegPath == wrapperPath) realFfmpegPath = "/usr/lib/jellyfin-ffmpeg/ffmpeg"; } string script = $@"#!/bin/bash # Zmienna przechowująca ścieżkę do prawdziwego FFmpeg Jellyfina REAL_FFMPEG=""{realFfmpegPath}"" # Znajdujemy ścieżkę pliku wejściowego (argument po -i) INPUT_FILE="""" for i in ""${{!@}}""; do if [[ ""${{@:$i:1}}"" == ""-i"" ]]; then INPUT_FILE=""${{@:$i+1:1}}"" break fi done # Pobieramy crop z API naszej wtyczki if [ -n ""$INPUT_FILE"" ]; then CROP_DATA=$(curl -s --max-time 1 -G --data-urlencode ""Path=$INPUT_FILE"" http://127.0.0.1:8096/BlackBarRemover/CropByPath) if [ -n ""$CROP_DATA"" ] && [ ""$CROP_DATA"" != """" ]; then exec ""$REAL_FFMPEG"" ""$@"" -vf ""crop=$CROP_DATA"" exit 0 fi fi # Zwykłe uruchomienie bez crop exec ""$REAL_FFMPEG"" ""$@"" "; File.WriteAllText(wrapperPath, script); if (Environment.OSVersion.Platform == PlatformID.Unix) { System.Diagnostics.Process.Start("chmod", $"+x \"{wrapperPath}\""); } _logger.LogInformation("Wygenerowano skrypt FFmpeg Wrapper w lokalizacji: {Path}", wrapperPath); // Teraz automagicznie podmieniamy ścieżkę w Jellyfin! if (dynConfig.EncoderAppPath != wrapperPath) { dynConfig.EncoderAppPath = wrapperPath; _configManager.SaveConfiguration("encoding", encodingConfig); _logger.LogInformation("Automatycznie zmieniono ścieżkę FFmpeg w konfiguracji Jellyfin na wrapper wtyczki."); } } catch (Exception ex) { _logger.LogError(ex, "Błąd podczas generowania i ustawiania skryptu FFmpeg wrapper."); } } } }