Automate FFmpeg wrapper configuration
This commit is contained in:
Binary file not shown.
@@ -1,11 +1,11 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using MediaBrowser.Controller.Plugins;
|
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using MediaBrowser.Common.Configuration;
|
using MediaBrowser.Common.Configuration;
|
||||||
|
using MediaBrowser.Controller.Configuration;
|
||||||
using Microsoft.Extensions.Hosting;
|
using Microsoft.Extensions.Hosting;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace JellyfinPlugin
|
namespace JellyfinPlugin
|
||||||
{
|
{
|
||||||
@@ -13,16 +13,18 @@ namespace JellyfinPlugin
|
|||||||
{
|
{
|
||||||
private readonly ILogger<WrapperGenerator> _logger;
|
private readonly ILogger<WrapperGenerator> _logger;
|
||||||
private readonly IApplicationPaths _appPaths;
|
private readonly IApplicationPaths _appPaths;
|
||||||
|
private readonly IServerConfigurationManager _configManager;
|
||||||
|
|
||||||
public WrapperGenerator(ILogger<WrapperGenerator> logger, IApplicationPaths appPaths)
|
public WrapperGenerator(ILogger<WrapperGenerator> logger, IApplicationPaths appPaths, IServerConfigurationManager configManager)
|
||||||
{
|
{
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
_appPaths = appPaths;
|
_appPaths = appPaths;
|
||||||
|
_configManager = configManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task StartAsync(CancellationToken cancellationToken)
|
public Task StartAsync(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
GenerateWrapper();
|
GenerateAndSetWrapper();
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -31,45 +33,67 @@ namespace JellyfinPlugin
|
|||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void GenerateWrapper()
|
private void GenerateAndSetWrapper()
|
||||||
{
|
{
|
||||||
try
|
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");
|
string wrapperDir = Path.Combine(_appPaths.PluginsPath, "BlackBarRemover");
|
||||||
Directory.CreateDirectory(wrapperDir);
|
Directory.CreateDirectory(wrapperDir);
|
||||||
string wrapperPath = Path.Combine(wrapperDir, "ffmpeg-wrapper.sh");
|
string wrapperPath = Path.Combine(wrapperDir, "ffmpeg-wrapper.sh");
|
||||||
|
|
||||||
string script = @"#!/bin/bash
|
// Jeśli obecna ścieżka to już nasz wrapper, nie robimy pętli w nieskończoność.
|
||||||
# Zmienna przechowująca ścieżkę do prawdziwego FFmpeg Jellyfina (zmień jeśli trzeba)
|
// Znajdujemy poprzednio zapisaną prawdziwą ścieżkę z pliku.
|
||||||
REAL_FFMPEG=""/usr/lib/jellyfin-ffmpeg/ffmpeg""
|
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)
|
# Znajdujemy ścieżkę pliku wejściowego (argument po -i)
|
||||||
INPUT_FILE=""""
|
INPUT_FILE=""""
|
||||||
for i in ""${!@}""; do
|
for i in ""${{!@}}""; do
|
||||||
if [[ ""${@:$i:1}"" == ""-i"" ]]; then
|
if [[ ""${{@:$i:1}}"" == ""-i"" ]]; then
|
||||||
INPUT_FILE=""${@:$i+1:1}""
|
INPUT_FILE=""${{@:$i+1:1}}""
|
||||||
break
|
break
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
# Pobieramy crop z API naszej wtyczki
|
# Pobieramy crop z API naszej wtyczki
|
||||||
if [ -n ""$INPUT_FILE"" ]; then
|
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)
|
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
|
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""
|
exec ""$REAL_FFMPEG"" ""$@"" -vf ""crop=$CROP_DATA""
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
@@ -80,22 +104,25 @@ exec ""$REAL_FFMPEG"" ""$@""
|
|||||||
";
|
";
|
||||||
File.WriteAllText(wrapperPath, script);
|
File.WriteAllText(wrapperPath, script);
|
||||||
|
|
||||||
// Nadajemy uprawnienia do wykonywania w systemach Linux (używając shella)
|
|
||||||
if (Environment.OSVersion.Platform == PlatformID.Unix)
|
if (Environment.OSVersion.Platform == PlatformID.Unix)
|
||||||
{
|
{
|
||||||
System.Diagnostics.Process.Start("chmod", $"+x \"{wrapperPath}\"");
|
System.Diagnostics.Process.Start("chmod", $"+x \"{wrapperPath}\"");
|
||||||
}
|
}
|
||||||
|
|
||||||
_logger.LogInformation("Wygenerowano skrypt FFmpeg Wrapper w lokalizacji: {Path}", 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)
|
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()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,12 +36,9 @@ A Jellyfin plugin that automatically detects and removes black bars (letterboxin
|
|||||||
- `Both`: Attempts both methods depending on the client.
|
- `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.
|
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:
|
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.
|
||||||
1. The plugin automatically generates a wrapper script on startup at `<PluginsPath>/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.
|
|
||||||
|
|
||||||
### Activating the Client-Side CSS (Automatic)
|
### Activating the Client-Side CSS (Automatic)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user