Initial commit: Freepik REST API

FastAPI async wrapper for Freepik cloud AI API supporting image generation
(Mystic, Flux Dev/Pro, SeedReam), video generation (Kling, MiniMax, Seedance),
image editing (upscale, relight, style transfer, expand, inpaint), and
utilities (background removal, classifier, audio isolation). Includes async
task tracking with polling, Docker containerization, and Gitea CI/CD workflow.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-16 14:07:36 +01:00
commit 99c24adfe8
32 changed files with 1814 additions and 0 deletions

View File

@@ -0,0 +1,70 @@
import logging
from datetime import datetime, timezone
from fastapi import APIRouter, HTTPException
from app.schemas.common import TaskResponse, TaskStatus
from app.schemas.video_generation import KlingRequest, MinimaxRequest, SeedanceRequest
from app.services import freepik_client, task_tracker
logger = logging.getLogger(__name__)
router = APIRouter(prefix='/api/v1/generate/video', tags=['video-generation'])
def _extract_task_id(result: dict) -> str:
data = result.get('data', result)
freepik_task_id = str(data.get('task_id') or data.get('id', ''))
if not freepik_task_id:
raise HTTPException(status_code=502, detail='No task_id in Freepik response')
return freepik_task_id
@router.post('/kling', response_model=TaskResponse)
async def generate_video_kling(request: KlingRequest):
result = await freepik_client.generate_video_kling(
image=request.image,
prompt=request.prompt,
duration=request.duration,
aspect_ratio=request.aspect_ratio,
)
freepik_id = _extract_task_id(result)
internal_id = task_tracker.submit(freepik_id, {'model': 'kling'})
return TaskResponse(
task_id=internal_id,
status=TaskStatus.pending,
created_at=datetime.now(timezone.utc),
)
@router.post('/minimax', response_model=TaskResponse)
async def generate_video_minimax(request: MinimaxRequest):
result = await freepik_client.generate_video_minimax(
prompt=request.prompt,
first_frame_image=request.first_frame_image,
subject_reference=request.subject_reference,
)
freepik_id = _extract_task_id(result)
internal_id = task_tracker.submit(freepik_id, {'model': 'minimax'})
return TaskResponse(
task_id=internal_id,
status=TaskStatus.pending,
created_at=datetime.now(timezone.utc),
)
@router.post('/seedance', response_model=TaskResponse)
async def generate_video_seedance(request: SeedanceRequest):
result = await freepik_client.generate_video_seedance(
prompt=request.prompt,
image=request.image,
duration=request.duration,
resolution=request.resolution,
)
freepik_id = _extract_task_id(result)
internal_id = task_tracker.submit(freepik_id, {'model': 'seedance'})
return TaskResponse(
task_id=internal_id,
status=TaskStatus.pending,
created_at=datetime.now(timezone.utc),
)