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; } // Przygotowujemy zamianę pliku ffmpeg bezpośrednio w /usr/lib/jellyfin-ffmpeg/ffmpeg string defaultFfmpeg = "/usr/lib/jellyfin-ffmpeg/ffmpeg"; string origFfmpeg = "/usr/lib/jellyfin-ffmpeg/ffmpeg.original"; string realFfmpegPath = origFfmpeg; if (!File.Exists(origFfmpeg)) { if (File.Exists(defaultFfmpeg)) { try { File.Move(defaultFfmpeg, origFfmpeg); } catch (Exception ex) { _logger.LogWarning(ex, "Nie udało się przenieść {DefaultFfmpeg} do {OrigFfmpeg}", defaultFfmpeg, origFfmpeg); realFfmpegPath = defaultFfmpeg; } } else if (File.Exists("/usr/bin/ffmpeg")) { realFfmpegPath = "/usr/bin/ffmpeg"; } } string wrapperDir = Path.Combine(_appPaths.PluginsPath, "BlackBarRemover"); Directory.CreateDirectory(wrapperDir); string wrapperPath = Path.Combine(wrapperDir, "ffmpeg"); string script = $@"#!/bin/bash REAL_FFMPEG=""{realFfmpegPath}"" INPUT_FILE="""" for (( i=1; i<=$#; i++ )); do arg=""${{!i}}"" if [[ ""$arg"" == ""-i"" ]]; then let next_idx=i+1 if [ $next_idx -le $# ]; then INPUT_FILE=""${{!next_idx}}"" fi break fi done if [ -n ""$INPUT_FILE"" ]; then CLEAN_PATH=$(echo ""$INPUT_FILE"" | tr -d '""' | sed 's/^file://') CROP_DATA=$(curl -s --max-time 1 -G --data-urlencode ""Path=$CLEAN_PATH"" http://127.0.0.1:8096/BlackBarRemover/CropByPath) if [ -n ""$CROP_DATA"" ] && [ ""$CROP_DATA"" != """" ]; then # Modyfikujemy argumenty w locie declare -a NEW_ARGS HAS_VF=0 IS_COPY=0 for (( i=1; i<=$#; i++ )); do arg=""${{!i}}"" if [[ ""$arg"" == ""copy"" ]]; then let prev_idx=i-1 prev_arg="""" if [ $prev_idx -gt 0 ]; then prev_arg=""${{!prev_idx}}"" fi if [[ ""$prev_arg"" == *codec:v* ]] || [[ ""$prev_arg"" == *-c:v* ]] || [[ ""$prev_arg"" == *-vcodec* ]]; then IS_COPY=1 fi fi if [[ ""$arg"" == ""-vf"" ]]; then HAS_VF=1 NEW_ARGS+=(""-vf"") next_arg="""" let next_idx=i+1 if [ $next_idx -le $# ]; then next_arg=""${{!next_idx}}"" fi NEW_ARGS+=(""crop=$CROP_DATA,$next_arg"") let i++ continue fi NEW_ARGS+=(""$arg"") done # Jeżeli Jellyfin nadal upiera się przy copy, nie możemy wstrzyknąć filtra crop (bo zcrashuje FFmpega) if [ $IS_COPY -eq 1 ]; then exec ""$REAL_FFMPEG"" ""$@"" exit 0 fi if [ $HAS_VF -eq 0 ]; then NEW_ARGS+=(""-vf"") NEW_ARGS+=(""crop=$CROP_DATA"") fi exec ""$REAL_FFMPEG"" ""${{NEW_ARGS[@]}}"" exit 0 fi fi exec ""$REAL_FFMPEG"" ""$@"" "; File.WriteAllText(wrapperPath, script); if (File.Exists(origFfmpeg) || defaultFfmpeg != origFfmpeg) { try { File.WriteAllText(defaultFfmpeg, script); } catch (Exception ex) { _logger.LogWarning(ex, "Nie udało się zapisać skryptu wrappera do {DefaultFfmpeg}", defaultFfmpeg); } } if (OperatingSystem.IsLinux() || OperatingSystem.IsFreeBSD() || OperatingSystem.IsMacOS()) { string[] pathsToChmod = new[] { wrapperPath, defaultFfmpeg }; foreach (var path in pathsToChmod) { if (!File.Exists(path)) continue; try { File.SetUnixFileMode(path, UnixFileMode.UserRead | UnixFileMode.UserWrite | UnixFileMode.UserExecute | UnixFileMode.GroupRead | UnixFileMode.GroupExecute | UnixFileMode.OtherRead | UnixFileMode.OtherExecute); } catch { var proc = System.Diagnostics.Process.Start("chmod", $"+x \"{path}\""); proc?.WaitForExit(); } } } _logger.LogInformation("Wygenerowano skrypt FFmpeg Wrapper w lokalizacji: {Path} oraz {DefaultFfmpeg}", wrapperPath, defaultFfmpeg); } catch (Exception ex) { _logger.LogError(ex, "Błąd podczas generowania i ustawiania skryptu FFmpeg wrapper."); } } } }