Make NVENC formats encoder-aware with CPU fallback

The nvenc_* formats require an ffmpeg built with NVENC. Static builds
(e.g. johnvansickle, which _get_ffmpeg may resolve) lack it, producing a
cryptic "Unknown encoder 'av1_nvenc'" crash.

- _get_ffmpeg(required_encoder): prefer the first existing ffmpeg that
  actually provides the encoder, so a NVENC-capable system ffmpeg wins
  over a static build.
- save_video: if the hardware encoder is unavailable anywhere, fall back
  to the CPU codec for the same container (av1_nvenc-webm -> VP9 webm,
  etc.) with a loud warning instead of losing the run's output.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-09 13:52:46 +02:00
co-authored by Claude Opus 4.8
parent 04be690a15
commit dbba6cd5f0
2 changed files with 114 additions and 17 deletions
+30
View File
@@ -108,3 +108,33 @@ def test_load_latent_absolute_rejects_relative_paths():
with pytest.raises(ValueError, match="absolute"):
JDL_LoadLatentAbsolute().load_latent("sample.latent")
def test_hw_cpu_fallback_maps_to_valid_same_container_formats():
import fast_saver as fs
# Every fallback target exists and keeps the same container extension.
for hw, cpu in fs._HW_CPU_FALLBACK.items():
assert hw in fs.VIDEO_FORMATS, hw
assert cpu in fs.VIDEO_FORMATS, cpu
assert fs.VIDEO_FORMATS[hw]["ext"] == fs.VIDEO_FORMATS[cpu]["ext"], hw
# Every hardware (nvenc) format must define a CPU fallback.
for name in fs.VIDEO_FORMATS:
if "nvenc" in name:
assert name in fs._HW_CPU_FALLBACK, name
def test_get_ffmpeg_prefers_binary_with_required_encoder(monkeypatch):
import fast_saver as fs
monkeypatch.setattr(fs, "_existing_ffmpeg_paths", lambda: ["/static/ffmpeg", "/nvenc/ffmpeg"])
monkeypatch.setattr(fs, "_ffmpeg_has_encoder",
lambda p, e: p == "/nvenc/ffmpeg" and e == "av1_nvenc")
# Required encoder lives in the lower-priority binary -> that one wins.
assert fs._get_ffmpeg("av1_nvenc") == "/nvenc/ffmpeg"
# No requirement -> highest-priority existing binary.
assert fs._get_ffmpeg() == "/static/ffmpeg"
# Required encoder available nowhere -> default binary (caller handles fallback).
assert fs._get_ffmpeg("h264_nvenc") == "/static/ffmpeg"