fix: skip aspect_ratio for kling-o1 models and detect image MIME via Pillow

kling-o1-pro and kling-o1-std silently fail when aspect_ratio is included
in the payload — they derive it from the input image. Added
VIDEO_ASPECT_RATIO_MODELS whitelist so only kling-elements and minimax-hailuo
receive the parameter.

Also switched image_to_base64 to use Pillow for format detection instead of
trusting the file extension, which correctly handles files saved with the
wrong extension.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-10 18:50:41 +02:00
parent 0de3f7d6bc
commit 4cd88ba477
3 changed files with 27 additions and 12 deletions
+8 -1
View File
@@ -107,7 +107,14 @@ VIDEO_IMAGE_FIELDS: dict[VideoModel, str] = {
VideoModel.MINIMAX_HAILUO: "image", VideoModel.MINIMAX_HAILUO: "image",
} }
# Kling Elements requires named aspect ratio slugs # Models that support an aspect_ratio parameter at all
VIDEO_ASPECT_RATIO_MODELS: set[VideoModel] = {
VideoModel.KLING_ELEMENTS_PRO,
VideoModel.KLING_ELEMENTS_STD,
VideoModel.MINIMAX_HAILUO,
}
# Of those, these require named slug aspect ratios instead of "W:H" strings
VIDEO_SLUG_ASPECT_RATIO_MODELS: set[VideoModel] = { VIDEO_SLUG_ASPECT_RATIO_MODELS: set[VideoModel] = {
VideoModel.KLING_ELEMENTS_PRO, VideoModel.KLING_ELEMENTS_PRO,
VideoModel.KLING_ELEMENTS_STD, VideoModel.KLING_ELEMENTS_STD,
+2 -2
View File
@@ -9,7 +9,7 @@ from freepik_cli.api.models import (
VIDEO_POST_ENDPOINTS, VIDEO_POST_ENDPOINTS,
VIDEO_STATUS_ENDPOINTS, VIDEO_STATUS_ENDPOINTS,
VIDEO_IMAGE_FIELDS, VIDEO_IMAGE_FIELDS,
VIDEO_SLUG_ASPECT_RATIO_MODELS, VIDEO_ASPECT_RATIO_MODELS,
VideoModel, VideoModel,
get_output_urls, get_output_urls,
get_status, get_status,
@@ -49,7 +49,7 @@ class VideoAPI:
effective_duration = 6 effective_duration = 6
payload["duration"] = str(effective_duration) payload["duration"] = str(effective_duration)
if aspect_ratio: if aspect_ratio and model in VIDEO_ASPECT_RATIO_MODELS:
payload["aspect_ratio"] = normalize_aspect_ratio_video(aspect_ratio, model) payload["aspect_ratio"] = normalize_aspect_ratio_video(aspect_ratio, model)
if seed is not None: if seed is not None:
+17 -9
View File
@@ -20,16 +20,24 @@ from rich.progress import (
def image_to_base64(path: Path) -> str: def image_to_base64(path: Path) -> str:
"""Read an image file and return a base64-encoded string.""" """Read an image file and return a base64-encoded string.
suffix = path.suffix.lower().lstrip(".")
mime_map = { Uses Pillow to detect the actual format rather than trusting the file
"jpg": "image/jpeg", extension — mismatched extensions (e.g. a JPEG saved as .png) would
"jpeg": "image/jpeg", produce an incorrect MIME type that causes silent failures with some models.
"png": "image/png", """
"gif": "image/gif", from PIL import Image
"webp": "image/webp",
_pillow_to_mime = {
"JPEG": "image/jpeg",
"PNG": "image/png",
"GIF": "image/gif",
"WEBP": "image/webp",
} }
mime = mime_map.get(suffix, "image/jpeg") with Image.open(path) as img:
fmt = img.format or "JPEG"
mime = _pillow_to_mime.get(fmt, "image/jpeg")
with open(path, "rb") as f: with open(path, "rb") as f:
encoded = base64.b64encode(f.read()).decode() encoded = base64.b64encode(f.read()).decode()
return f"data:{mime};base64,{encoded}" return f"data:{mime};base64,{encoded}"