diff --git a/BlackBarRemover_v1.0.zip b/BlackBarRemover_v1.0.zip new file mode 100644 index 0000000..2aaa86a Binary files /dev/null and b/BlackBarRemover_v1.0.zip differ diff --git a/JellyfinPlugin/WrapperGenerator.cs b/JellyfinPlugin/WrapperGenerator.cs index ab64a07..136aa07 100644 --- a/JellyfinPlugin/WrapperGenerator.cs +++ b/JellyfinPlugin/WrapperGenerator.cs @@ -1,11 +1,11 @@ using System; using System.IO; +using System.Threading; using System.Threading.Tasks; -using MediaBrowser.Controller.Plugins; -using Microsoft.Extensions.Logging; using MediaBrowser.Common.Configuration; - +using MediaBrowser.Controller.Configuration; using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; namespace JellyfinPlugin { @@ -13,16 +13,18 @@ namespace JellyfinPlugin { private readonly ILogger _logger; private readonly IApplicationPaths _appPaths; + private readonly IServerConfigurationManager _configManager; - public WrapperGenerator(ILogger logger, IApplicationPaths appPaths) + public WrapperGenerator(ILogger logger, IApplicationPaths appPaths, IServerConfigurationManager configManager) { _logger = logger; _appPaths = appPaths; + _configManager = configManager; } public Task StartAsync(CancellationToken cancellationToken) { - GenerateWrapper(); + GenerateAndSetWrapper(); return Task.CompletedTask; } @@ -31,45 +33,67 @@ namespace JellyfinPlugin return Task.CompletedTask; } - private void GenerateWrapper() + 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"); - string script = @"#!/bin/bash -# Zmienna przechowująca ścieżkę do prawdziwego FFmpeg Jellyfina (zmień jeśli trzeba) -REAL_FFMPEG=""/usr/lib/jellyfin-ffmpeg/ffmpeg"" + // 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}"" +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 - # Jellyfin lokalnie słucha na 8096, odpytujemy API o crop 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 - # Znaleziono crop! Wstrzykujemy filtr crop=W:H:X:Y do argumentów - # Dodajemy -vf crop przed argumentami wyjścia - NEW_ARGS=() - for arg in ""$@""; do - # Prosta iniekcja, w realnym środowisku szukamy gdzie wkleić -vf, zazwyczaj przed plikiem wyjściowym. - # Jellyfin sprzętowy tworzy filtry z -filter_complex, w softwareowym z -vf. - NEW_ARGS+=(""$arg"") - done - - # Uruchamiamy ffmpeg z crop - # UWAGA: to jest uproszczenie. Prawdziwy wrapper IntroSkipper musi łączyć filtry. - # W tym MVP dodajemy prosty filter: exec ""$REAL_FFMPEG"" ""$@"" -vf ""crop=$CROP_DATA"" exit 0 fi @@ -80,22 +104,25 @@ exec ""$REAL_FFMPEG"" ""$@"" "; File.WriteAllText(wrapperPath, script); - // Nadajemy uprawnienia do wykonywania w systemach Linux (używając shella) 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 skryptu FFmpeg wrapper."); + _logger.LogError(ex, "Błąd podczas generowania i ustawiania skryptu FFmpeg wrapper."); } } - - public void Dispose() - { - } } } diff --git a/README.md b/README.md index 807289f..70e467c 100644 --- a/README.md +++ b/README.md @@ -36,12 +36,9 @@ A Jellyfin plugin that automatically detects and removes black bars (letterboxin - `Both`: Attempts both methods depending on the client. 4. **Important for existing libraries**: The plugin automatically scans *new* files when they are added to Jellyfin. For your existing library, go to **Dashboard -> Scheduled Tasks** and run the **"Skanuj brakujące czarne pasy (Black Bar Remover)"** task manually. It will scan all existing movies and episodes in the background. -### Activating the Server-Side Wrapper (Optional) +### Activating the Server-Side Wrapper (Automagic) -If you selected "Server" or "Both" mode: -1. The plugin automatically generates a wrapper script on startup at `/BlackBarRemover/ffmpeg-wrapper.sh`. -2. In the Jellyfin Dashboard, go to **Playback** -> **FFmpeg path**. -3. Change the path to point to the generated `ffmpeg-wrapper.sh` file. +If you select "Server" or "Both" mode, the plugin automatically generates a wrapper script and dynamically injects it into Jellyfin's `EncoderAppPath` configuration during startup. It takes over the transcoding process effortlessly. If it ever points to the wrong real FFmpeg, you can adjust it in the wrapper script. ### Activating the Client-Side CSS (Automatic)