feat: initial Freepik AI CLI

Sophisticated Python CLI for generating and manipulating images and
video via the Freepik API, built with typer + rich.

Commands:
- generate-image: text-to-image with 8 models (flux-2-pro, mystic, seedream, etc.)
- generate-video: image-to-video with 7 models (kling, minimax, runway, etc.)
- generate-icon: text-to-icon in solid/outline/color/flat/sticker styles
- upscale-image: 3 modes (precision-v2, precision, creative) + 2x/4x scale
- upscale-video: standard/turbo modes
- expand-image: outpainting with per-side pixel offsets
- relight: AI-controlled relighting (Premium)
- style-transfer: artistic style application (Premium)
- describe-image: reverse-engineer an image into a prompt
- config set/get/show/reset: configuration management

Features: Rich Live polling panel, exponential backoff, --wait/--no-wait,
auto-timestamped output filenames, streaming download with progress bar,
FREEPIK_API_KEY env var support, venv-based setup.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-08 10:56:45 +02:00
commit f24d138ab4
24 changed files with 2511 additions and 0 deletions
+88
View File
@@ -0,0 +1,88 @@
"""Image and video upscaling 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 (
UPSCALE_POST_ENDPOINTS,
UPSCALE_STATUS_ENDPOINTS,
VIDEO_UPSCALE_POST_ENDPOINTS,
VIDEO_UPSCALE_STATUS_ENDPOINT,
UpscaleMode,
VideoUpscaleMode,
get_output_urls,
get_status,
get_task_id,
)
class UpscaleAPI:
def __init__(self, client: FreepikClient) -> None:
self._client = client
# ------------------------------------------------------------------
# Image upscaling
# ------------------------------------------------------------------
def upscale_image(
self,
mode: UpscaleMode,
image_b64: str,
scale_factor: Optional[str] = None,
creativity: Optional[int] = None,
prompt: Optional[str] = None,
seed: Optional[int] = None,
) -> str:
"""Submit an image upscale task. Returns task_id."""
payload: dict[str, Any] = {"image": image_b64}
if scale_factor:
# Convert "2x" → 2, "4x" → 4
factor = scale_factor.rstrip("xX")
try:
payload["scale_factor"] = int(factor)
except ValueError:
payload["scale_factor"] = 2
if mode == UpscaleMode.CREATIVE:
if creativity is not None:
payload["creativity"] = creativity
if prompt:
payload["prompt"] = prompt
if seed is not None:
payload["seed"] = seed
endpoint = UPSCALE_POST_ENDPOINTS[mode]
raw = self._client.post(endpoint, json=payload)
return get_task_id(raw)
def upscale_image_status(self, mode: UpscaleMode, task_id: str) -> Tuple[str, dict[str, Any]]:
endpoint = UPSCALE_STATUS_ENDPOINTS[mode].format(task_id=task_id)
raw = self._client.get(endpoint)
return get_status(raw), raw
# ------------------------------------------------------------------
# Video upscaling
# ------------------------------------------------------------------
def upscale_video(
self,
mode: VideoUpscaleMode,
video_b64: str,
) -> str:
"""Submit a video upscale task. Returns task_id."""
payload: dict[str, Any] = {"video": video_b64}
endpoint = VIDEO_UPSCALE_POST_ENDPOINTS[mode]
raw = self._client.post(endpoint, json=payload)
return get_task_id(raw)
def upscale_video_status(self, task_id: str) -> Tuple[str, dict[str, Any]]:
endpoint = VIDEO_UPSCALE_STATUS_ENDPOINT.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)