feat: #1208 AI封面接入MediaKit视频截帧 #1214

Merged
xiaoxia merged 2 commits from feat/mediakit-ai-cover-1208 into develop 2026-08-02 01:14:02 +08:00
Owner

目标

_call_ai_cover_service 从 stub 改为真实调用 MediaKit 视频截帧

改动

  • 新增 MediaKit 客户端 (packages/shared/mediakit_client.py)

    • 封装视频抽帧 API,支持 SceneChange 策略
    • 异步任务提交 + 轮询机制
    • 未配置 API Key 时 is_available=False,调用方自动降级
  • 修改 AI 封面服务 (packages/shared/ai_service.py)

    • _call_ai_cover_service 新增 primary_video_url 参数
    • cover_typeai_frameai_regenerate 时调用 MediaKit
    • 失败时降级到 stub 行为(保持向后兼容)
  • 修改 API 路由 (apps/api/app/api/routes/templates_editor/cover.py)

    • asset_ids 获取第一个视频的下载 URL
    • 传递给 run_generate_cover
  • 新增配置 (packages/config/base.py)

    • MEDIAKIT_API_KEY: MediaKit API Key
    • MEDIAKIT_BASE_URL: 默认 https://mediakit.cn-beijing.volces.com/api/v1
    • MEDIAKIT_TIMEOUT: 默认 60 秒

测试

  • 15 个单元测试全绿 (tests/unit/test_mediakit_cover.py)
    • MediaKit 客户端:成功/失败/不可用场景
    • AI 封面服务:MediaKit 成功/失败降级/无 URL/未配置
    • run_generate_cover:参数传递正确性

向后兼容

  • primary_video_url 为可选参数,默认 None
  • MediaKit 未配置或调用失败时,降级到原有 stub 行为
  • uploadmanual 类型的封面逻辑完全不变

环境变量

需要在 staging 环境配置:

MEDIAKIT_API_KEY=AKLTNWRhNWNiMTdmMDZiNGIxMGJmOWVlZDE4NTE1YmJiMmE
## 目标 把 `_call_ai_cover_service` 从 stub 改为真实调用 MediaKit 视频截帧 ## 改动 - **新增 MediaKit 客户端** (`packages/shared/mediakit_client.py`) - 封装视频抽帧 API,支持 SceneChange 策略 - 异步任务提交 + 轮询机制 - 未配置 API Key 时 `is_available=False`,调用方自动降级 - **修改 AI 封面服务** (`packages/shared/ai_service.py`) - `_call_ai_cover_service` 新增 `primary_video_url` 参数 - `cover_type` 为 `ai_frame` 或 `ai_regenerate` 时调用 MediaKit - 失败时降级到 stub 行为(保持向后兼容) - **修改 API 路由** (`apps/api/app/api/routes/templates_editor/cover.py`) - 从 `asset_ids` 获取第一个视频的下载 URL - 传递给 `run_generate_cover` - **新增配置** (`packages/config/base.py`) - `MEDIAKIT_API_KEY`: MediaKit API Key - `MEDIAKIT_BASE_URL`: 默认 `https://mediakit.cn-beijing.volces.com/api/v1` - `MEDIAKIT_TIMEOUT`: 默认 60 秒 ## 测试 - 15 个单元测试全绿 (`tests/unit/test_mediakit_cover.py`) - MediaKit 客户端:成功/失败/不可用场景 - AI 封面服务:MediaKit 成功/失败降级/无 URL/未配置 - `run_generate_cover`:参数传递正确性 ## 向后兼容 - `primary_video_url` 为可选参数,默认 `None` - MediaKit 未配置或调用失败时,降级到原有 stub 行为 - `upload` 和 `manual` 类型的封面逻辑完全不变 ## 环境变量 需要在 staging 环境配置: ``` MEDIAKIT_API_KEY=AKLTNWRhNWNiMTdmMDZiNGIxMGJmOWVlZDE4NTE1YmJiMmE ```
xiaoxia added 1 commit 2026-08-02 01:01:37 +08:00
feat: #1208 AI封面接入MediaKit视频截帧
CI/CD Pipeline / Build Staging API Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Web Image (pull_request) Has been skipped
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been skipped
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been skipped
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been skipped
CI/CD Pipeline / ACR Image Cleanup (pull_request) Has been skipped
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 29s
CI/CD Pipeline / Frontend Unit Tests (pull_request) Has been skipped
CI/CD Pipeline / Frontend Lint (pull_request) Has been skipped
CI/CD Pipeline / PR Build Web Image (pull_request) Has been skipped
CI/CD Pipeline / Validate - Migration (alembic) (pull_request) Successful in 56s
CI/CD Pipeline / Validate - Type Check (mypy) (pull_request) Successful in 1m2s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 1m31s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 1m54s
CI/CD Pipeline / Validate - Code Quality (pull_request) Has been cancelled
CI/CD Pipeline / Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / PR Build API Image (pull_request) Has been cancelled
CI/CD Pipeline / PR Build Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Production (pull_request) Has been cancelled
CI/CD Pipeline / Production Browser E2E (pull_request) Has been cancelled
CI/CD Pipeline / Canary Release to Production (pull_request) Has been cancelled
CI/CD Pipeline / CI Gate (pull_request) Has been cancelled
AI Code Review / AI Code Review (pull_request) Has been cancelled
PR Automation / Auto Approve on CI Green (pull_request) Has been cancelled
7bd7a19e44
- 新增 MediaKit 客户端 (packages/shared/mediakit_client.py)
  - 支持视频抽帧 API,SceneChange 策略
  - 异步任务提交 + 轮询机制
  - 未配置 API Key 时自动降级

- 修改 _call_ai_cover_service (packages/shared/ai_service.py)
  - 新增 primary_video_url 参数
  - 调用 MediaKit 进行智能截帧
  - 失败时降级到 stub 行为

- 修改 API 路由 (apps/api/app/api/routes/templates_editor/cover.py)
  - 从 asset_ids 获取第一个视频的下载 URL
  - 传递给 run_generate_cover

- 新增配置 (packages/config/base.py)
  - MEDIAKIT_API_KEY
  - MEDIAKIT_BASE_URL
  - MEDIAKIT_TIMEOUT

- 15 个单元测试全绿 (tests/unit/test_mediakit_cover.py)

🚀 预览环境已部署

项目 详情
PR号 #1214
预览链接 https://pr-1214.preview.xiaoxiajianji.com
API环境 staging

💡 预览环境使用 staging API 数据,请勿在预览环境中操作重要数据。

🔄 每次提交新代码后预览环境会自动更新。

🗑️ PR 关闭或合并后,预览环境会自动清理。

🚀 **预览环境已部署** | 项目 | 详情 | |------|------| | PR号 | #1214 | | 预览链接 | [https://pr-1214.preview.xiaoxiajianji.com](https://pr-1214.preview.xiaoxiajianji.com) | | API环境 | staging | > 💡 预览环境使用 staging API 数据,请勿在预览环境中操作重要数据。 > > 🔄 每次提交新代码后预览环境会自动更新。 > > 🗑️ PR 关闭或合并后,预览环境会自动清理。
auto-approve-bot added 1 commit 2026-08-02 01:04:27 +08:00
style: auto-format with black + isort + prettier [skip ci-format-check]
CI/CD Pipeline / Build Staging API Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Web Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been skipped
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been skipped
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been skipped
CI/CD Pipeline / ACR Image Cleanup (pull_request) Has been skipped
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 38s
CI/CD Pipeline / Frontend Lint (pull_request) Has been skipped
CI/CD Pipeline / Frontend Unit Tests (pull_request) Has been skipped
CI/CD Pipeline / PR Build Web Image (pull_request) Has been skipped
CI/CD Pipeline / Validate - Type Check (mypy) (pull_request) Successful in 44s
CI/CD Pipeline / Validate - Migration (alembic) (pull_request) Successful in 44s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 1m19s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 1m39s
AI Code Review / AI Code Review (pull_request) Successful in 2m21s
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 1m59s
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 1m59s
CI/CD Pipeline / Validate - Code Quality (pull_request) Successful in 3m19s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 3m19s
CI/CD Pipeline / Integration Tests (pull_request) Successful in 1m24s
CI/CD Pipeline / Unit Tests (pull_request) Successful in 6m52s
CI/CD Pipeline / Build Production API Image (pull_request) Has been skipped
CI/CD Pipeline / Build Production Web Image (pull_request) Has been skipped
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Deploy Production (pull_request) Has been skipped
CI/CD Pipeline / Canary Release to Production (pull_request) Has been skipped
CI/CD Pipeline / Production Browser E2E (pull_request) Has been skipped
CI/CD Pipeline / CI Gate (pull_request) Successful in 7s
Preview Cleanup / Cleanup Preview Environment (pull_request) Successful in 33s
ACR Cleanup / ACR Image Cleanup (pull_request_target) Successful in 36s
b13b007b19
Collaborator

代码审查结果 - PR #1214

⚠️ 问题(2个需要修改)

  1. packages/shared/mediakit_client.py 第107-174行:同步阻塞导致严重的性能瓶颈和可用性风险。

    • 问题描述extract_frames 方法使用了同步的 time.sleephttpx 客户端进行轮询。在默认配置下(轮询间隔2秒,最大30次),该函数会阻塞请求线程长达60秒。在 Web 服务器(如 Gunicorn/Uvicorn workers)中,这会直接占用工作线程,导致并发能力急剧下降,高并发时容易耗尽线程池造成服务拒绝(DoS)。
    • 修改建议:将 MediaKitClient 改为异步实现(使用 httpx.AsyncClientasyncio.sleep),并将调用链路上的 editor_generate_coverrun_generate_cover 均改为 async def。或者,将抽帧任务提交到后台队列(如 Celery)异步处理,接口立即返回任务 ID。
  2. packages/shared/mediakit_client.py 第162-165行:异常处理逻辑缺陷,无效重试浪费资源。

    • 问题描述:在 _poll_task_result 的轮询循环中,捕获了通用的 Exception 并在每次异常后 sleep 然后继续重试。如果错误是客户端错误(如 401 Unauthorized, 403 Forbidden, 404 Not Found)或请求格式错误,重试不仅无效,还会浪费长达 60 秒的时间(30次重试)并产生大量无效日志。
    • 修改建议:应区分异常类型。对于 httpx.HTTPStatusError,如果状态码为 4xx,应直接抛出异常或返回 None,停止重试;仅在 5xx 或网络连接错误时进行重试。

💡 建议(2个可选)

  1. packages/shared/ai_service.py 第298行:代码冗余。

    • if frames and len(frames) > 0: 中,frames 为列表时,len(frames) > 0 是多余的,直接写 if frames: 即可。
  2. apps/api/app/api/routes/templates_editor/cover.py 第187-190行:导入位置说明。

    • 在函数内部进行 import(如 from app.database import get_db_session)通常是为了解决循环依赖。如果确实存在循环依赖,建议添加注释说明原因;如果没有,建议移到文件顶部。

格式检查通过 | 逻辑审查需修改 | ⚠️ 建议关注性能


🤖 由 AI 代码审查机器人自动生成 | 2026-08-01 17:06:45 | 模型:

## 代码审查结果 - PR #1214 ### ⚠️ 问题(2个需要修改) 1. **packages/shared/mediakit_client.py 第107-174行**:同步阻塞导致严重的性能瓶颈和可用性风险。 - **问题描述**:`extract_frames` 方法使用了同步的 `time.sleep` 和 `httpx` 客户端进行轮询。在默认配置下(轮询间隔2秒,最大30次),该函数会阻塞请求线程长达60秒。在 Web 服务器(如 Gunicorn/Uvicorn workers)中,这会直接占用工作线程,导致并发能力急剧下降,高并发时容易耗尽线程池造成服务拒绝(DoS)。 - **修改建议**:将 `MediaKitClient` 改为异步实现(使用 `httpx.AsyncClient` 和 `asyncio.sleep`),并将调用链路上的 `editor_generate_cover` 和 `run_generate_cover` 均改为 `async def`。或者,将抽帧任务提交到后台队列(如 Celery)异步处理,接口立即返回任务 ID。 2. **packages/shared/mediakit_client.py 第162-165行**:异常处理逻辑缺陷,无效重试浪费资源。 - **问题描述**:在 `_poll_task_result` 的轮询循环中,捕获了通用的 `Exception` 并在每次异常后 `sleep` 然后继续重试。如果错误是客户端错误(如 401 Unauthorized, 403 Forbidden, 404 Not Found)或请求格式错误,重试不仅无效,还会浪费长达 60 秒的时间(30次重试)并产生大量无效日志。 - **修改建议**:应区分异常类型。对于 `httpx.HTTPStatusError`,如果状态码为 4xx,应直接抛出异常或返回 None,停止重试;仅在 5xx 或网络连接错误时进行重试。 ### 💡 建议(2个可选) 1. **packages/shared/ai_service.py 第298行**:代码冗余。 - `if frames and len(frames) > 0:` 中,`frames` 为列表时,`len(frames) > 0` 是多余的,直接写 `if frames:` 即可。 2. **apps/api/app/api/routes/templates_editor/cover.py 第187-190行**:导入位置说明。 - 在函数内部进行 `import`(如 `from app.database import get_db_session`)通常是为了解决循环依赖。如果确实存在循环依赖,建议添加注释说明原因;如果没有,建议移到文件顶部。 --- ✅ 格式检查通过 | ❌ 逻辑审查需修改 | ⚠️ 建议关注性能 --- <sub>🤖 由 AI 代码审查机器人自动生成 | 2026-08-01 17:06:45 | 模型: </sub> <!-- AI_CODE_REVIEW_AUTO_COMMENT -->
xiaoxia merged commit 92b29de511 into develop 2026-08-02 01:14:02 +08:00

🗑️ 预览环境已清理

PR #1214 已关闭或合并,对应的预览环境已被清理。

如有需要,可以重新打开 PR 来重新生成预览环境。

🗑️ **预览环境已清理** PR #1214 已关闭或合并,对应的预览环境已被清理。 > 如有需要,可以重新打开 PR 来重新生成预览环境。
Sign in to join this conversation.