105ab54059
- packages/config/base.py: 新增 GPU_ENCODE_* 配置项(开关/endpoint/relay/secret/编码参数/fallback)
- packages/shared/gpu_encoder.py: 新增 GpuEncoderClient,封装 health 探测 + mezzanine 上传 OSS + P4000 nvenc 编码 + relay 回传下载,失败抛 GpuEncodeError 触发 CPU 降级
- apps/api/app/api/routes/gpu_relay.py: 新增 internal PUT/GET/DELETE /api/v1/internal/gpu-relay/{key}(token 鉴权),P4000 PUT 编码结果,worker GET 下载
- apps/api/app/api/router.py: 注册 gpu_relay_router
- apps/worker/video_processing/unified_render_service.py: _execute_ffmpeg 和 _render_pass_through 尝试 GPU 路径:CPU ultrafast mezzanine → P4000 nvenc → 输出到最终路径;任何失败自动回退到原 CPU libx264 路径
- apps/worker/worker_app/tasks/_startup.py: worker_ready 时探测 P4000 健康并打日志
- tests/unit/test_gpu_encoder.py: GpuEncoderClient 单测(health/sync 调用/失败/fallback/relay URL)
架构:
- 输入:CPU 输出 libx264 ultrafast mezzanine → 上传 OSS 临时前缀 → P4000 签名 URL 下载
- 输出:P4000 PUT → 宿主机 nginx(tailscale:80)→ API /api/ 反代 → gpu_relay 路由落盘到 generated/gpu_relay/
- 回传:worker 通过 docker 网络 http://xiaoxia-api-staging:8000 GET 下载最终 mp4 到 output_path
- 降级:GPU 任何环节异常(health/上传/编码/回传/下载)→ 原 CPU 路径继续执行,不影响成片
243 lines
7.3 KiB
Python
Executable File
243 lines
7.3 KiB
Python
Executable File
from app.api.routes.ai import router as ai_router
|
||
from app.api.routes.ai_avatar_render import router as ai_avatar_render_router
|
||
from app.api.routes.asset_diagnosis import router as asset_diagnosis_router
|
||
from app.api.routes.asset_libraries import router as asset_libraries_router
|
||
from app.api.routes.assets import router as assets_router
|
||
from app.api.routes.auth import router as auth_router
|
||
from app.api.routes.chunked_upload import router as chunked_upload_router
|
||
from app.api.routes.classification_jobs import router as classification_jobs_router
|
||
from app.api.routes.clips_standalone import router as clips_standalone_router
|
||
from app.api.routes.cover_templates import router as cover_templates_router
|
||
from app.api.routes.drafts_standalone import router as drafts_standalone_router
|
||
from app.api.routes.duplication import router as duplication_router
|
||
from app.api.routes.feature_flags import router as feature_flags_router
|
||
from app.api.routes.generation_cover import router as generation_cover_router
|
||
from app.api.routes.generation_preview import router as generation_preview_router
|
||
from app.api.routes.generation_tasks import router as generation_tasks_router
|
||
from app.api.routes.generation_variant_plans import router as generation_variant_plans_router
|
||
from app.api.routes.gpu_lipsync import router as gpu_lipsync_router
|
||
from app.api.routes.gpu_relay import router as gpu_relay_router
|
||
from app.api.routes.health import router as health_check_router
|
||
from app.api.routes.ingest_jobs import router as ingest_jobs_router
|
||
from app.api.routes.internal_render import router as internal_render_router
|
||
from app.api.routes.lipsync import router as lipsync_router
|
||
from app.api.routes.points import router as points_router
|
||
from app.api.routes.points import usage_router
|
||
from app.api.routes.projects import router as projects_router
|
||
from app.api.routes.scripts import router as scripts_router
|
||
from app.api.routes.scripts_ai import router as scripts_ai_router
|
||
from app.api.routes.share import router as share_router
|
||
from app.api.routes.subscription import router as subscription_router
|
||
from app.api.routes.tags import router as tags_router
|
||
from app.api.routes.task_center import router as task_center_router
|
||
from app.api.routes.templates import router as templates_router
|
||
from app.api.routes.templates_editor import router as templates_editor_router
|
||
from app.api.routes.titles import router as titles_router
|
||
from app.api.routes.tts import router as tts_router
|
||
from app.api.routes.upload import router as upload_router
|
||
from app.api.routes.videos import router as videos_router
|
||
from app.api.routes.voice_clones import router as voice_clones_router
|
||
from app.api.routes.voices import router as voices_router
|
||
from fastapi import APIRouter
|
||
|
||
api_router = APIRouter(prefix="/api/v1")
|
||
health_router = APIRouter()
|
||
health_router.include_router(health_check_router)
|
||
|
||
# ── /api/health 别名:部分前端/探针把 health 放在 /api 前缀下 ──────────────
|
||
# 原来 /health 在根路径;额外加一个 /api/health 别名避免 404。
|
||
api_health_router = APIRouter(prefix="/api")
|
||
api_health_router.include_router(health_check_router)
|
||
health_router.include_router(api_health_router)
|
||
|
||
# ── 旧前端路径别名(无需 template_id 路径参数)────────────────────────────
|
||
# /api/v1/clips/from-assets 已有 clips_standalone;此处额外挂 /api/v1/editor/*,
|
||
# 解决前端调 /api/v1/editor/clips/from-assets 和 /api/v1/editor/drafts 的 404。
|
||
editor_legacy_router = APIRouter(prefix="/editor", tags=["Editor Legacy Alias"])
|
||
editor_legacy_router.include_router(clips_standalone_router)
|
||
editor_legacy_router.include_router(drafts_standalone_router)
|
||
|
||
api_router.include_router(
|
||
auth_router,
|
||
tags=["Auth"],
|
||
)
|
||
api_router.include_router(
|
||
lipsync_router,
|
||
prefix="/lipsync",
|
||
tags=["Lipsync"],
|
||
)
|
||
api_router.include_router(
|
||
projects_router,
|
||
prefix="/projects",
|
||
tags=["Project"],
|
||
)
|
||
api_router.include_router(
|
||
tags_router,
|
||
prefix="/tags",
|
||
tags=["Tag"],
|
||
)
|
||
api_router.include_router(
|
||
cover_templates_router,
|
||
tags=["CoverTemplate"],
|
||
)
|
||
api_router.include_router(
|
||
task_center_router,
|
||
tags=["TaskCenter"],
|
||
)
|
||
api_router.include_router(
|
||
asset_diagnosis_router,
|
||
tags=["AssetDiagnosis"],
|
||
)
|
||
api_router.include_router(
|
||
asset_libraries_router,
|
||
prefix="/asset-libraries",
|
||
tags=["AssetLibrary"],
|
||
)
|
||
api_router.include_router(
|
||
assets_router,
|
||
prefix="/assets",
|
||
tags=["Asset"],
|
||
)
|
||
api_router.include_router(
|
||
ingest_jobs_router,
|
||
prefix="/ingest-jobs",
|
||
tags=["IngestJob"],
|
||
)
|
||
api_router.include_router(
|
||
classification_jobs_router,
|
||
prefix="/classification-jobs",
|
||
tags=["ClassificationJob"],
|
||
)
|
||
api_router.include_router(
|
||
upload_router,
|
||
prefix="/upload",
|
||
tags=["Upload"],
|
||
)
|
||
api_router.include_router(
|
||
chunked_upload_router,
|
||
prefix="/upload/chunk",
|
||
tags=["ChunkedUpload"],
|
||
)
|
||
api_router.include_router(
|
||
generation_tasks_router,
|
||
prefix="/generation",
|
||
tags=["Generation"],
|
||
)
|
||
api_router.include_router(
|
||
generation_preview_router,
|
||
prefix="/generation",
|
||
tags=["Generation"],
|
||
)
|
||
api_router.include_router(
|
||
generation_variant_plans_router,
|
||
prefix="/generation",
|
||
tags=["Generation"],
|
||
)
|
||
api_router.include_router(
|
||
generation_cover_router,
|
||
prefix="/generation",
|
||
tags=["Generation"],
|
||
)
|
||
api_router.include_router(
|
||
titles_router,
|
||
prefix="/titles",
|
||
tags=["TitleLibrary"],
|
||
)
|
||
api_router.include_router(
|
||
voices_router,
|
||
prefix="/voices",
|
||
tags=["VoiceLibrary"],
|
||
)
|
||
api_router.include_router(
|
||
voice_clones_router,
|
||
prefix="/voice-clones",
|
||
tags=["VoiceClone"],
|
||
)
|
||
api_router.include_router(
|
||
videos_router,
|
||
tags=["VideoCenter"],
|
||
)
|
||
api_router.include_router(
|
||
share_router,
|
||
tags=["Share"],
|
||
)
|
||
api_router.include_router(
|
||
duplication_router,
|
||
prefix="/duplication",
|
||
tags=["Duplication"],
|
||
)
|
||
api_router.include_router(
|
||
subscription_router,
|
||
prefix="/subscription",
|
||
tags=["Subscription"],
|
||
)
|
||
api_router.include_router(
|
||
templates_router,
|
||
prefix="/templates",
|
||
tags=["Template"],
|
||
)
|
||
api_router.include_router(
|
||
clips_standalone_router,
|
||
tags=["Clips"],
|
||
)
|
||
api_router.include_router(
|
||
templates_editor_router,
|
||
prefix="/templates/{template_id}/editor",
|
||
tags=["TemplateEditor"],
|
||
)
|
||
api_router.include_router(
|
||
editor_legacy_router,
|
||
)
|
||
api_router.include_router(
|
||
tts_router,
|
||
prefix="/tts",
|
||
tags=["TTS"],
|
||
)
|
||
api_router.include_router(
|
||
ai_router,
|
||
prefix="/ai",
|
||
tags=["AI"],
|
||
)
|
||
api_router.include_router(
|
||
feature_flags_router,
|
||
tags=["Internal"],
|
||
)
|
||
api_router.include_router(
|
||
internal_render_router,
|
||
tags=["Internal"],
|
||
)
|
||
api_router.include_router(
|
||
gpu_relay_router,
|
||
tags=["GpuRelay"],
|
||
)
|
||
api_router.include_router(
|
||
scripts_router,
|
||
prefix="/scripts",
|
||
tags=["ScriptLibrary"],
|
||
)
|
||
api_router.include_router(
|
||
scripts_ai_router,
|
||
prefix="/scripts",
|
||
tags=["ScriptLibrary AI"],
|
||
)
|
||
api_router.include_router(
|
||
ai_avatar_render_router,
|
||
prefix="/ai-avatar/render",
|
||
tags=["AI Avatar Render"],
|
||
)
|
||
api_router.include_router(
|
||
points_router,
|
||
prefix="/points",
|
||
tags=["Points"],
|
||
)
|
||
api_router.include_router(
|
||
usage_router,
|
||
prefix="/usage",
|
||
tags=["Usage"],
|
||
)
|
||
api_router.include_router(
|
||
gpu_lipsync_router,
|
||
prefix="/gpu",
|
||
tags=["GPU Worker"],
|
||
)
|