From 69d58f99e33cee5233bce88b1a4e5ea672e3cc55 Mon Sep 17 00:00:00 2001 From: Leo Vasanko Date: Tue, 11 Aug 2026 05:29:32 +0000 Subject: [PATCH] Drop noisy ffmpeg fallback for non-HEIC images, quiet ffmpeg output MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A corrupt TIFF in production produced a wall of ffmpeg error output: pyvips could not decode it, the generic ffmpeg fallback was tried, and ffmpeg's TIFF decoder failed just the same — with banner, configuration and stream-mapping spam included. - Non-HEIC images are now decoded by pyvips only; a pyvips failure raises a clean one-line ValueError ("cannot decode image: ...", a 422 like any other undecodable file) instead of invoking ffmpeg. The ffmpeg path is kept for HEIC/HEIF, where pyvips genuinely falls short (tile assembly, HDR metadata). - ffmpeg runs with -hide_banner -loglevel error -nostats: error output is still shown on failure, without the version/configuration/progress noise. The -s insertion index no longer depends on fixed positions. --- cista/preview_worker.py | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/cista/preview_worker.py b/cista/preview_worker.py index a040750..8a52eb9 100644 --- a/cista/preview_worker.py +++ b/cista/preview_worker.py @@ -213,6 +213,11 @@ def _image_via_ffmpeg(path: Path, maxsize: int, quality: int) -> bytes: tmp_path = tmp_f.name cmd = [ "ffmpeg", + # Keep error messages, drop the banner/config/stream-mapping spam. + "-hide_banner", + "-loglevel", + "error", + "-nostats", "-y", "-i", str(path), @@ -233,8 +238,9 @@ def _image_via_ffmpeg(path: Path, maxsize: int, quality: int) -> bytes: new_w = int(w * scale) new_h = int(h * scale) # insert -s right after the input file - cmd.insert(4, "-s") - cmd.insert(5, f"{new_w}x{new_h}") + input_index = cmd.index(str(path)) + 1 + cmd.insert(input_index, "-s") + cmd.insert(input_index + 1, f"{new_w}x{new_h}") try: try: # stdin=DEVNULL is critical: ffmpeg must not inherit the worker's @@ -285,9 +291,10 @@ def process_image_pyvips(path, *, maxsize, quality): height=height, ) - # Other image formats: pyvips first, ffmpeg fallback. + # Other image formats: pyvips only. ffmpeg is not a useful fallback + # here — when pyvips cannot decode a file, ffmpeg's image decoders + # cannot either, and their failure output is far noisier. load_opts = {"access": "sequential"} - orig_w = orig_h = None try: img = pyvips.Image.new_from_file(str(path), **load_opts) img = img.autorot() @@ -301,11 +308,9 @@ def process_image_pyvips(path, *, maxsize, quality): effort=AVIF_FAST_EFFORT, keep="none", ) - backend = "pyvips" - except pyvips.error.Error: - orig_w, orig_h = None, None - ret = _image_via_ffmpeg(path, maxsize, quality) - backend = "ffmpeg" + except pyvips.error.Error as e: + raise ValueError(f"cannot decode image: {e}") from e + backend = "pyvips" t_end = perf_counter() return ret, PreviewResponse(