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

75
app/routers/tasks.py Normal file
View File

@@ -0,0 +1,75 @@
import logging
from typing import Optional
from fastapi import APIRouter, HTTPException, Query
from fastapi.responses import FileResponse
from app.schemas.common import TaskDetail, TaskListResponse, TaskStatus
from app.services import task_tracker
from app.services.file_manager import get_result_path
logger = logging.getLogger(__name__)
router = APIRouter(prefix='/api/v1/tasks', tags=['tasks'])
@router.get('', response_model=TaskListResponse)
async def list_tasks(
status: Optional[TaskStatus] = Query(None),
limit: int = Query(20, ge=1, le=100),
offset: int = Query(0, ge=0),
):
tasks, total = task_tracker.list_tasks(status=status, limit=limit, offset=offset)
return TaskListResponse(
tasks=[_to_detail(t) for t in tasks],
total=total,
)
@router.get('/{task_id}', response_model=TaskDetail)
async def get_task(task_id: str):
task = task_tracker.get_task(task_id)
if not task:
raise HTTPException(status_code=404, detail='Task not found')
return _to_detail(task)
@router.get('/{task_id}/result')
async def get_task_result(task_id: str):
task = task_tracker.get_task(task_id)
if not task:
raise HTTPException(status_code=404, detail='Task not found')
if task['status'] != TaskStatus.completed:
raise HTTPException(status_code=409, detail=f'Task status is {task["status"].value}')
path = task.get('local_path') or get_result_path(task_id)
if not path:
# Fall back to redirect if we have a remote URL
if task.get('result_url'):
from fastapi.responses import RedirectResponse
return RedirectResponse(url=task['result_url'])
raise HTTPException(status_code=404, detail='Result file not found')
return FileResponse(path)
@router.delete('/{task_id}')
async def delete_task(task_id: str):
if not task_tracker.delete_task(task_id):
raise HTTPException(status_code=404, detail='Task not found')
return {'status': 'deleted', 'task_id': task_id}
def _to_detail(task: dict) -> TaskDetail:
result_url = None
if task['status'] == TaskStatus.completed:
result_url = f'/api/v1/tasks/{task["task_id"]}/result'
return TaskDetail(
task_id=task['task_id'],
status=task['status'],
created_at=task['created_at'],
updated_at=task['updated_at'],
progress=task.get('progress'),
result_url=result_url,
error=task.get('error'),
)