From 24a85cf0ec106cfe2498fe68a8b202e447baf0d2 Mon Sep 17 00:00:00 2001 From: Karol Labus Date: Thu, 17 Sep 2026 15:35:25 +0200 Subject: [PATCH] Fix crop bounding box and limit defaults, setting reset=1 --- .../Configuration/PluginConfiguration.cs | 4 +- JellyfinPlugin/CropHelper.cs | 41 +++++++++++++++++-- 2 files changed, 39 insertions(+), 6 deletions(-) diff --git a/JellyfinPlugin/Configuration/PluginConfiguration.cs b/JellyfinPlugin/Configuration/PluginConfiguration.cs index f24011e..610cf9e 100644 --- a/JellyfinPlugin/Configuration/PluginConfiguration.cs +++ b/JellyfinPlugin/Configuration/PluginConfiguration.cs @@ -4,8 +4,8 @@ namespace JellyfinPlugin.Configuration { public class PluginConfiguration : BasePluginConfiguration { - // Tolerancja detekcji czarnych pasów (domyślnie 0.15 czyli 15%) - public double CropDetectLimit { get; set; } = 0.15; + // Tolerancja detekcji czarnych pasów (0.094 = domyślne 24/255) + public double CropDetectLimit { get; set; } = 0.094; // Czy wtyczka jest aktywna public bool IsEnabled { get; set; } = true; diff --git a/JellyfinPlugin/CropHelper.cs b/JellyfinPlugin/CropHelper.cs index e63ef97..eb6498e 100644 --- a/JellyfinPlugin/CropHelper.cs +++ b/JellyfinPlugin/CropHelper.cs @@ -11,12 +11,15 @@ namespace JellyfinPlugin { public static async Task DetectCropAsync(string filePath, double limit) { + // FFmpeg w nowych wersjach przyjmuje limit w formie ułamka, np. 24/255 to ok 0.094 + // Używamy domyślnego limitu ffmpeg (ok. 0.094), ewentualnie tego z configu jeśli użytkownik zmienił string limitStr = limit.ToString(System.Globalization.CultureInfo.InvariantCulture); var startInfo = new ProcessStartInfo { FileName = "ffmpeg", - Arguments = $"-skip_frame nokey -i \"{filePath}\" -vf \"cropdetect=limit={limitStr}:round=16:reset=0\" -f null -", + // KLUCZOWE: reset=1, żeby oceniał każdą klatkę niezależnie! + Arguments = $"-skip_frame nokey -i \"{filePath}\" -vf \"cropdetect=limit={limitStr}:round=16:reset=1\" -f null -", RedirectStandardError = true, RedirectStandardOutput = true, UseShellExecute = false, @@ -44,9 +47,39 @@ namespace JellyfinPlugin if (totalFrames == 0) return string.Empty; - var bestCrop = crops.OrderByDescending(kvp => kvp.Value).FirstOrDefault().Key; - - return bestCrop ?? string.Empty; + // Bierzemy pod uwagę tylko wymiary, które występują przez co najmniej 5% czasu filmu + // Dzięki temu ignorujemy jednosekundowe loga czy zaszumione klatki + var validCrops = crops.Where(kvp => (double)kvp.Value / totalFrames > 0.05).ToList(); + if (!validCrops.Any()) + { + validCrops = crops.OrderByDescending(kvp => kvp.Value).Take(1).ToList(); + } + + // Tworzymy maksymalną obwiednię (Bounding Box) ze wszystkich WAŻNYCH kadrów + // Dzięki temu jeśli film to np. Interstellar (30% czasu w 16:9), kadr 16:9 zostanie zachowany + // i nie utniemy ważnych scen! + int minX = int.MaxValue, minY = int.MaxValue, maxXW = 0, maxYH = 0; + + foreach (var kvp in validCrops) + { + var parts = kvp.Key.Split(':'); + if (parts.Length == 4 && + int.TryParse(parts[0], out int w) && int.TryParse(parts[1], out int h) && + int.TryParse(parts[2], out int x) && int.TryParse(parts[3], out int y)) + { + minX = Math.Min(minX, x); + minY = Math.Min(minY, y); + maxXW = Math.Max(maxXW, x + w); + maxYH = Math.Max(maxYH, y + h); + } + } + + int finalW = maxXW - minX; + int finalH = maxYH - minY; + + if (finalW <= 0 || finalH <= 0) return string.Empty; + + return $"{finalW}:{finalH}:{minX}:{minY}"; } } }