Files
Aether/_deprecated_py_src/api/public/videos.py
fawney19 1d9c77522a refactor: 移除 Python 后端源码,全面迁移至 Rust gateway 架构
- 删除全部 Python 源码 (src/) 及 Alembic 迁移脚本,归档至 _deprecated_py_src/
- 重构 Rust gateway ai_pipeline: 拆分 planner/finalize 模块,新增 contracts/adaptation 层
- 重组 handlers 模块为 admin/public/proxy/internal/shared 子模块结构
- 新增 executor 模块,引入 Rust 原生数据库迁移 (aether-data/migrations)
- 简化 CI/Docker 构建流程,移除 base image 二级构建,统一为单一 app image
- 移除 Python 相关基础设施文件 (entrypoint.sh, gunicorn_conf.py, Dockerfile.base)
2026-04-03 16:26:16 +08:00

179 lines
5.6 KiB
Python

"""
Video Generation API 路由
"""
from __future__ import annotations
from typing import Any
from fastapi import APIRouter, Depends, Request
from sqlalchemy.orm import Session
from src.api.base.pipeline import get_pipeline
from src.api.handlers.gemini.video_adapter import GeminiVeoAdapter
from src.api.handlers.openai.video_adapter import OpenAIVideoAdapter
from src.database import get_db
router = APIRouter(tags=["Video Generation"])
pipeline = get_pipeline()
async def _run_video_route_shell(
adapter: Any,
http_request: Request,
db: Session,
*,
path_params: dict[str, Any] | None = None,
) -> Any:
return await pipeline.run(
adapter=adapter,
http_request=http_request,
db=db,
mode=adapter.mode,
api_format_hint=adapter.allowed_api_formats[0],
path_params=path_params,
)
# -------------------- OpenAI Sora compatible --------------------
@router.post("/v1/videos")
async def create_video_sora(http_request: Request, db: Session = Depends(get_db)) -> Any:
adapter = OpenAIVideoAdapter()
return await _run_video_route_shell(adapter, http_request, db)
@router.post("/v1/videos/{task_id}/cancel")
async def cancel_video_sora(
task_id: str, http_request: Request, db: Session = Depends(get_db)
) -> Any:
"""Cancel video task (OpenAI Sora style)."""
adapter = OpenAIVideoAdapter()
return await _run_video_route_shell(
adapter,
http_request,
db,
path_params={"task_id": task_id, "action": "cancel"},
)
@router.get("/v1/videos/{task_id}")
async def get_video_task_sora(
task_id: str, http_request: Request, db: Session = Depends(get_db)
) -> Any:
adapter = OpenAIVideoAdapter()
return await _run_video_route_shell(adapter, http_request, db, path_params={"task_id": task_id})
@router.get("/v1/videos")
async def list_video_tasks_sora(http_request: Request, db: Session = Depends(get_db)) -> Any:
adapter = OpenAIVideoAdapter()
return await _run_video_route_shell(adapter, http_request, db)
@router.delete("/v1/videos/{task_id}")
async def delete_video_task_sora(
task_id: str, http_request: Request, db: Session = Depends(get_db)
) -> Any:
"""删除已完成或失败的视频及其存储资源"""
adapter = OpenAIVideoAdapter()
return await _run_video_route_shell(adapter, http_request, db, path_params={"task_id": task_id})
@router.get("/v1/videos/{task_id}/content")
async def download_video_content_sora(
task_id: str, http_request: Request, db: Session = Depends(get_db)
) -> Any:
adapter = OpenAIVideoAdapter()
return await _run_video_route_shell(adapter, http_request, db, path_params={"task_id": task_id})
@router.post("/v1/videos/{task_id}/remix")
async def remix_video_sora(
task_id: str, http_request: Request, db: Session = Depends(get_db)
) -> Any:
adapter = OpenAIVideoAdapter()
return await _run_video_route_shell(adapter, http_request, db, path_params={"task_id": task_id})
# -------------------- Gemini Veo compatible --------------------
@router.post("/v1beta/models/{model}:predictLongRunning")
async def create_video_veo(model: str, http_request: Request, db: Session = Depends(get_db)) -> Any:
adapter = GeminiVeoAdapter()
return await _run_video_route_shell(adapter, http_request, db, path_params={"model": model})
# Gemini Veo operation routes - support both formats:
# 1. models/{model}/operations/{id} (official Gemini Veo format)
# 2. operations/{...} (legacy format for compatibility)
@router.get("/v1beta/models/{model}/operations/{operation_id}")
async def get_video_veo_by_model(
model: str, operation_id: str, http_request: Request, db: Session = Depends(get_db)
) -> Any:
"""Get video task status (Gemini Veo format: models/{model}/operations/{id})"""
adapter = GeminiVeoAdapter()
# Reconstruct full operation name
full_operation_name = f"models/{model}/operations/{operation_id}"
return await _run_video_route_shell(
adapter,
http_request,
db,
path_params={"task_id": full_operation_name},
)
@router.post("/v1beta/models/{model}/operations/{operation_id}:cancel")
async def cancel_video_veo_by_model(
model: str, operation_id: str, http_request: Request, db: Session = Depends(get_db)
) -> Any:
"""Cancel video task (Gemini Veo format: models/{model}/operations/{id}:cancel)"""
adapter = GeminiVeoAdapter()
full_operation_name = f"models/{model}/operations/{operation_id}"
return await _run_video_route_shell(
adapter,
http_request,
db,
path_params={"task_id": full_operation_name, "action": "cancel"},
)
# Legacy routes for backward compatibility
@router.get("/v1beta/operations/{operation_id:path}")
async def get_video_veo(
operation_id: str, http_request: Request, db: Session = Depends(get_db)
) -> Any:
adapter = GeminiVeoAdapter()
return await _run_video_route_shell(
adapter,
http_request,
db,
path_params={"task_id": operation_id},
)
@router.get("/v1beta/operations")
async def list_video_tasks_veo(http_request: Request, db: Session = Depends(get_db)) -> Any:
adapter = GeminiVeoAdapter()
return await _run_video_route_shell(adapter, http_request, db)
@router.post("/v1beta/operations/{operation_id}:cancel")
async def cancel_video_veo(
operation_id: str, http_request: Request, db: Session = Depends(get_db)
) -> Any:
adapter = GeminiVeoAdapter()
return await _run_video_route_shell(
adapter,
http_request,
db,
path_params={"task_id": operation_id, "action": "cancel"},
)
# Video download is now handled by /v1beta/files/{task_id}:download in gemini_files.py