diff --git a/plugins/video_gen/fal/__init__.py b/plugins/video_gen/fal/__init__.py index 6c4361447..6ad310427 100644 --- a/plugins/video_gen/fal/__init__.py +++ b/plugins/video_gen/fal/__init__.py @@ -106,8 +106,9 @@ FAL_FAMILIES: Dict[str, Dict[str, Any]] = { "text_endpoint": "fal-ai/veo3.1", "image_endpoint": "fal-ai/veo3.1/image-to-video", "aspect_ratios": ("16:9", "9:16"), - "resolutions": ("720p", "1080p"), + "resolutions": ("720p", "1080p", "4k"), "durations": (4, 6, 8), + "duration_suffix": "s", # FAL veo3.1 wants "4s" not "4" "audio": True, "negative": True, }, @@ -272,7 +273,9 @@ def _build_payload( clamped = _clamp_duration(family, duration) if clamped is not None and family.get("durations"): # FAL exposes duration as a string in the queue API ("8" not 8). - payload["duration"] = str(clamped) + # Some families (e.g. veo3.1) require a unit suffix ("4s" not "4"). + suffix = family.get("duration_suffix", "") + payload["duration"] = f"{clamped}{suffix}" if family.get("audio") and audio is not None: payload["generate_audio"] = bool(audio) diff --git a/tests/plugins/video_gen/test_fal_plugin.py b/tests/plugins/video_gen/test_fal_plugin.py index 346df6c8a..7af1bd409 100644 --- a/tests/plugins/video_gen/test_fal_plugin.py +++ b/tests/plugins/video_gen/test_fal_plugin.py @@ -257,7 +257,7 @@ class TestPayloadBuilder: seed=42, ) assert p["prompt"] == "x" - assert p["duration"] == "8" # FAL queue API uses strings + assert p["duration"] == "8s" # veo3.1 uses "Ns" format per FAL API assert p["aspect_ratio"] == "16:9" assert p["resolution"] == "720p" assert p["generate_audio"] is True