2026-04-08 10:56:45 +02:00
|
|
|
"""Video generation API methods."""
|
|
|
|
|
|
|
|
|
|
from __future__ import annotations
|
|
|
|
|
|
|
|
|
|
from typing import Any, Optional, Tuple
|
|
|
|
|
|
|
|
|
|
from freepik_cli.api.client import FreepikClient
|
|
|
|
|
from freepik_cli.api.models import (
|
|
|
|
|
VIDEO_POST_ENDPOINTS,
|
|
|
|
|
VIDEO_STATUS_ENDPOINTS,
|
2026-04-10 18:15:59 +02:00
|
|
|
VIDEO_IMAGE_FIELDS,
|
2026-04-10 18:50:41 +02:00
|
|
|
VIDEO_ASPECT_RATIO_MODELS,
|
2026-04-08 10:56:45 +02:00
|
|
|
VideoModel,
|
|
|
|
|
get_output_urls,
|
|
|
|
|
get_status,
|
|
|
|
|
get_task_id,
|
2026-04-10 18:15:59 +02:00
|
|
|
normalize_aspect_ratio_video,
|
2026-04-08 10:56:45 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class VideoAPI:
|
|
|
|
|
def __init__(self, client: FreepikClient) -> None:
|
|
|
|
|
self._client = client
|
|
|
|
|
|
|
|
|
|
def generate(
|
|
|
|
|
self,
|
|
|
|
|
model: VideoModel,
|
|
|
|
|
image_b64: str,
|
|
|
|
|
prompt: Optional[str] = None,
|
|
|
|
|
duration: int = 5,
|
|
|
|
|
aspect_ratio: str = "16:9",
|
|
|
|
|
seed: Optional[int] = None,
|
|
|
|
|
) -> str:
|
|
|
|
|
"""Submit an image-to-video task. Returns task_id."""
|
2026-04-10 18:59:09 +02:00
|
|
|
# Video API expects raw base64, not a data URI
|
|
|
|
|
if ";base64," in image_b64:
|
|
|
|
|
image_b64 = image_b64.split(";base64,", 1)[1]
|
|
|
|
|
|
2026-04-10 18:15:59 +02:00
|
|
|
image_field = VIDEO_IMAGE_FIELDS[model]
|
|
|
|
|
|
|
|
|
|
# kling-elements uses an array; all others use a scalar
|
|
|
|
|
if image_field == "images":
|
|
|
|
|
payload: dict[str, Any] = {"images": [image_b64]}
|
|
|
|
|
else:
|
|
|
|
|
payload = {image_field: image_b64}
|
|
|
|
|
|
2026-04-08 10:56:45 +02:00
|
|
|
if prompt:
|
|
|
|
|
payload["prompt"] = prompt
|
2026-04-10 18:15:59 +02:00
|
|
|
|
|
|
|
|
# minimax only supports duration=6; clamp silently
|
|
|
|
|
effective_duration = duration
|
|
|
|
|
if model == VideoModel.MINIMAX_HAILUO:
|
|
|
|
|
effective_duration = 6
|
|
|
|
|
payload["duration"] = str(effective_duration)
|
|
|
|
|
|
2026-04-10 18:50:41 +02:00
|
|
|
if aspect_ratio and model in VIDEO_ASPECT_RATIO_MODELS:
|
2026-04-10 18:15:59 +02:00
|
|
|
payload["aspect_ratio"] = normalize_aspect_ratio_video(aspect_ratio, model)
|
|
|
|
|
|
2026-04-08 10:56:45 +02:00
|
|
|
if seed is not None:
|
|
|
|
|
payload["seed"] = seed
|
|
|
|
|
|
|
|
|
|
endpoint = VIDEO_POST_ENDPOINTS[model]
|
|
|
|
|
raw = self._client.post(endpoint, json=payload)
|
|
|
|
|
return get_task_id(raw)
|
|
|
|
|
|
|
|
|
|
def get_status(self, model: VideoModel, task_id: str) -> Tuple[str, dict[str, Any]]:
|
|
|
|
|
"""Poll status. Returns (status_str, raw_response)."""
|
|
|
|
|
endpoint = VIDEO_STATUS_ENDPOINTS[model].format(task_id=task_id)
|
|
|
|
|
raw = self._client.get(endpoint)
|
|
|
|
|
return get_status(raw), raw
|
|
|
|
|
|
|
|
|
|
def get_output_urls(self, raw: dict[str, Any]) -> list[str]:
|
|
|
|
|
return get_output_urls(raw)
|