""" Audio & Export Endpoints - 音频生成和导出模块 包含: - POST /audio/generate - 音频生成 - POST /generations/audio - 统一音频生成 - POST /export/project/{project_id} - 项目导出 """ import logging from fastapi import APIRouter, Depends from src.models.schemas import ( ResponseModel, AudioGenerationRequest, GenerateAudioRequest, ExportProjectRequest ) from src.services.provider.registry import ModelRegistry, ModelType from src.services.task_manager import task_manager from src.services.export_service import VideoExportService from src.auth.dependencies import get_current_user, UserAuth from .helpers import resolve_service, check_user_api_key from src.utils.errors import ErrorCode, AppException router = APIRouter() logger = logging.getLogger(__name__) # 初始化 export service export_service = VideoExportService() @router.post("/audio/generate", response_model=ResponseModel) async def generate_audio( request: GenerateAudioRequest, current_user: UserAuth = Depends(get_current_user) ): """生成 audio from text(兼容旧接口,内部走统一任务系统)""" logger.info("Audio generation request (legacy): model=%s, text=%s...", request.model, request.text[:50]) # 兼容旧接口:model 可为空,回退到默认音频模型 model_name = request.model or ModelRegistry.get_default_id(ModelType.AUDIO) if not model_name: raise AppException( message="Audio service not configured", code=ErrorCode.INTERNAL_ERROR, status_code=500 ) # 验证模型可解析并获取服务 audio_service = resolve_service(model_name, ModelType.AUDIO) # 检查用户是否配置了 API Key # 从 audio_service 获取 provider_id provider = getattr(audio_service, 'provider_id', None) if provider: check_user_api_key(current_user.id, provider) try: task_params = { "text": request.text, "voice": request.voice, "model": model_name, "project_id": request.project_id, "storyboard_id": request.storyboard_id, "extra_params": request.extra_params or {}, } task = await task_manager.create_task( task_type="audio", model=model_name, params=task_params, user_id=current_user.id, project_id=request.project_id, ) # 兼容前端:保留 audio_url 字段但异步任务初始为空 return ResponseModel(data={ "audio_url": None, "task_id": task.id, "status": task.status }) except Exception as e: raise AppException( message=str(e), code=ErrorCode.INTERNAL_ERROR, status_code=500 ) @router.post("/generations/audio", response_model=ResponseModel) async def generate_audio_unified( request: AudioGenerationRequest, current_user: UserAuth = Depends(get_current_user) ): """统一音频生成端点(推荐)""" logger.info("Audio generation request: model=%s, text=%s...", request.model, request.text[:50]) audio_service = resolve_service(request.model, ModelType.AUDIO) # 检查用户是否配置了 API Key # 从 audio_service 获取 provider_id provider = getattr(audio_service, 'provider_id', None) if provider: check_user_api_key(current_user.id, provider) if not audio_service: raise AppException( message="Audio service not configured", code=ErrorCode.INTERNAL_ERROR, status_code=500 ) try: task_params = request.model_dump() # Get user_id from authenticated user user_id = current_user.id # Handle project_id from multiple sources # Priority 1: request.project_id (from request body, alias="projectId") # Priority 2: request.source_id if source=='project' (from extra_params, alias="sourceId") project_id = request.project_id if not project_id and request.source == "project": project_id = request.source_id task = await task_manager.create_task( task_type="audio", model=request.model, params=task_params, user_id=user_id, project_id=project_id ) except Exception as e: logger.error("Failed to create audio task: %s", e) raise AppException( message=f"Failed to create task: {e}", code=ErrorCode.INTERNAL_ERROR, status_code=500 ) return ResponseModel(data={ "task_id": task.id, "status": task.status }) @router.post("/export/project/{project_id}", response_model=ResponseModel) async def export_project(project_id: str, request: ExportProjectRequest): """ 导出 project to video""" try: result = await export_service.export_project(project_id, format=request.format) return ResponseModel(data=result) except Exception as e: raise AppException( message=str(e), code=ErrorCode.INTERNAL_ERROR, status_code=500 )