fix: 封面自动生成时叠加用户选择的标题文字 (#1365 修复) #1367

Closed
xiaoxia wants to merge 10 commits from fix/cover-title-overlay into develop
Owner

问题

用户在 Step 5 选择了视频标题后,Step 6 自动生成的封面图片上没有叠加该标题文字。PR #1365 的修复没有解决根本问题。

根因分析

标题文字在封面生成链路中存在三个断点:

  1. 前端未传递标题:Step 6 调用 generateCover() API 时未传入标题文字
  2. 后端未接收标题/generation/generate-cover 端点不接受 title_text 参数,无法将标题渲染到封面图上
  3. Worker 未注入标题custom_title(Step 5 用户选择的标题)未注入到 virtual_plan.config.title.text,导致渲染引擎和封面候选帧都没有标题信息

修复方案

前端(4 个文件)

  • apps/web/src/api/generation/cover.tsGenerateCoverRequest 新增 title_text 字段
  • apps/web/src/pages/generate/hooks/useStep6Cover.ts:接收 titleText prop,传给 API
  • apps/web/src/pages/generate/components/Step6CoverSettings.tsx:新增 titleText prop 并传递给 hook
  • apps/web/src/pages/generate/components/GenerateStepContent.tsx:将 titleSettings.title 传给 Step6CoverSettings

后端(2 个文件)

  • apps/api/app/api/routes/generation_cover.py

    • GenerateCoverRequest 新增 title_text 字段
    • 新增 _apply_title_to_cover_image() 辅助函数:下载封面图 → FFmpeg drawtext 叠加标题 → 重新上传 OSS
    • 在 cover_candidates 路径和 AI fallback 路径都叠加标题
  • apps/worker/worker_app/tasks/generation.py

    • _render_video() 新增 custom_title 参数
    • 在模板配置注入后、BGM 配置前,将 custom_title 写入 virtual_plan.config.title.text
    • 渲染引擎的 render_adapter.py 已有从 plan_config 读取标题并叠加到封面候选帧的逻辑

数据流修复后

Step 5 用户选标题 → titleSettings.title
  ↓
Step 6 自动生成封面 → generateCover(templateId, { title_text: titleSettings.title })
  ↓
API /generation/generate-cover → 接收 title_text → 叠加到封面图
  ↓
确认生成 → custom_title 存入 GenerationTask
  ↓
Worker 渲染 → custom_title 注入 virtual_plan.config.title.text
  ↓
render_adapter → 封面候选帧自动叠加标题(已有逻辑)

测试建议

  1. 选择标题 → 自动生成封面 → 验证封面图包含标题文字
  2. 确认生成 → 验证最终视频的封面缩略图包含标题
  3. 不选标题(空标题)→ 验证不影响原有逻辑
## 问题 用户在 Step 5 选择了视频标题后,Step 6 自动生成的封面图片上没有叠加该标题文字。PR #1365 的修复没有解决根本问题。 ## 根因分析 标题文字在封面生成链路中存在三个断点: 1. **前端未传递标题**:Step 6 调用 `generateCover()` API 时未传入标题文字 2. **后端未接收标题**:`/generation/generate-cover` 端点不接受 `title_text` 参数,无法将标题渲染到封面图上 3. **Worker 未注入标题**:`custom_title`(Step 5 用户选择的标题)未注入到 `virtual_plan.config.title.text`,导致渲染引擎和封面候选帧都没有标题信息 ## 修复方案 ### 前端(4 个文件) - **`apps/web/src/api/generation/cover.ts`**:`GenerateCoverRequest` 新增 `title_text` 字段 - **`apps/web/src/pages/generate/hooks/useStep6Cover.ts`**:接收 `titleText` prop,传给 API - **`apps/web/src/pages/generate/components/Step6CoverSettings.tsx`**:新增 `titleText` prop 并传递给 hook - **`apps/web/src/pages/generate/components/GenerateStepContent.tsx`**:将 `titleSettings.title` 传给 Step6CoverSettings ### 后端(2 个文件) - **`apps/api/app/api/routes/generation_cover.py`**: - `GenerateCoverRequest` 新增 `title_text` 字段 - 新增 `_apply_title_to_cover_image()` 辅助函数:下载封面图 → FFmpeg drawtext 叠加标题 → 重新上传 OSS - 在 cover_candidates 路径和 AI fallback 路径都叠加标题 - **`apps/worker/worker_app/tasks/generation.py`**: - `_render_video()` 新增 `custom_title` 参数 - 在模板配置注入后、BGM 配置前,将 `custom_title` 写入 `virtual_plan.config.title.text` - 渲染引擎的 `render_adapter.py` 已有从 plan_config 读取标题并叠加到封面候选帧的逻辑 ## 数据流修复后 ``` Step 5 用户选标题 → titleSettings.title ↓ Step 6 自动生成封面 → generateCover(templateId, { title_text: titleSettings.title }) ↓ API /generation/generate-cover → 接收 title_text → 叠加到封面图 ↓ 确认生成 → custom_title 存入 GenerationTask ↓ Worker 渲染 → custom_title 注入 virtual_plan.config.title.text ↓ render_adapter → 封面候选帧自动叠加标题(已有逻辑) ``` ## 测试建议 1. 选择标题 → 自动生成封面 → 验证封面图包含标题文字 2. 确认生成 → 验证最终视频的封面缩略图包含标题 3. 不选标题(空标题)→ 验证不影响原有逻辑
xiaoxia added 9 commits 2026-08-14 14:46:28 +08:00
fix: use upload_file_smart instead of non-existent upload_bytes
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 / ACR Image Cleanup (pull_request) Has been skipped
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been skipped
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been skipped
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 27s
CI/CD Pipeline / Validate - Migration (alembic) (pull_request) Successful in 1m23s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 1m52s
CI/CD Pipeline / Validate - Type Check (mypy) (pull_request) Successful in 1m53s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 2m12s
CI/CD Pipeline / Frontend Unit Tests (pull_request) Successful in 1m57s
CI/CD Pipeline / Frontend Lint (pull_request) Successful in 2m15s
CI/CD Pipeline / PR Build Web Image (pull_request) Successful in 2m30s
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
911dbb2ea5

🚀 预览环境已部署

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

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

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

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

🚀 **预览环境已部署** | 项目 | 详情 | |------|------| | PR号 | #1367 | | 预览链接 | [https://pr-1367.preview.xiaoxiajianji.com](https://pr-1367.preview.xiaoxiajianji.com) | | API环境 | staging | > 💡 预览环境使用 staging API 数据,请勿在预览环境中操作重要数据。 > > 🔄 每次提交新代码后预览环境会自动更新。 > > 🗑️ PR 关闭或合并后,预览环境会自动清理。
auto-approve-bot added 1 commit 2026-08-14 14:50:03 +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 48s
CI/CD Pipeline / Validate - Type Check (mypy) (pull_request) Successful in 1m24s
CI/CD Pipeline / Validate - Migration (alembic) (pull_request) Successful in 1m25s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 1m28s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 1m47s
CI/CD Pipeline / Frontend Lint (pull_request) Successful in 1m9s
AI Code Review / AI Code Review (pull_request) Failing after 1m57s
CI/CD Pipeline / Frontend Unit Tests (pull_request) Successful in 1m47s
CI/CD Pipeline / PR Build Web Image (pull_request) Successful in 2m14s
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 1m46s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 3m59s
CI/CD Pipeline / Unit Tests (pull_request) Failing after 3m26s
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 4m28s
CI/CD Pipeline / Validate - Code Quality (pull_request) Successful in 5m27s
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 / Integration Tests (pull_request) Successful in 2m21s
CI/CD Pipeline / CI Gate (pull_request) Failing after 6s
ACR Cleanup / ACR Image Cleanup (pull_request_target) Has been cancelled
Preview Cleanup / Cleanup Preview Environment (pull_request) Successful in 1m0s
2223f17226
Collaborator

【阻塞级判定】

  • 是否存在阻塞级问题:是
  • 阻塞级问题数量:2 个

📊 审查概览

  • 整体评价:需修改
  • 建议级问题数量:2 个

🔴 阻塞级问题(必须修复)

  1. [apps/api/app/api/routes/generation_cover.py: 79-95] 资源泄漏风险

    • 问题类型:逻辑bug / 数据损坏风险
    • 问题描述:在 _apply_title_to_cover_image 函数中,tmp_dir 临时目录的清理逻辑(finally 块)仅挂载在 OSS 上传的 try 块下。如果在“下载原图”或“FFmpeg 处理”阶段发生异常并提前 return,将导致 tmp_dir 无法被删除,长期运行会造成服务器磁盘空间泄漏(Disk Leak)。
    • 修改建议:将临时目录的清理逻辑(finally 块)提升,覆盖整个函数的处理流程,或者使用 contextlibtry...finally 包裹所有可能产生 return 的路径。
  2. [apps/web/src/pages/generate/hooks/useStep6Cover.ts: 140] useEffect 无限循环

    • 问题类型:逻辑bug
    • 问题描述:useEffect 的依赖数组中包含了 generating 状态。该 Effect 内部会修改 generating 状态(先设为 true 请求接口,完成后设为 false)。当 generating 变为 false 时,会再次触发 Effect 执行,导致“生成 -> 完成 -> 再生成”的死循环,持续向服务端发送请求。
    • 修改建议:从依赖数组中移除 generating。Effect 应仅由业务数据(如 selectedTemplate, assetIds, titleText 等)的变化触发,而不应由自身的执行状态触发。

💡 改进建议(不阻塞合并)

  1. [apps/api/app/api/routes/generation_cover.py: 274-288 & 312-327] 代码重复

    • 具体内容:在 generate_cover 函数中,两处调用 _apply_title_to_cover_image 的逻辑完全一致(try-except 包裹、日志记录)。建议将此逻辑提取为一个局部辅助函数,避免代码重复,降低维护成本。
  2. [apps/api/app/api/routes/generation_cover.py: 72] 硬编码文件扩展名

    • 具体内容:src_path 和 OSS 上传的 storage_key 均硬编码为 .jpg。如果原始封面图是 PNG 或 WebP 格式,虽然 FFmpeg 通常能识别内容,但强制保存为 JPG 可能会丢失透明通道信息。建议根据原始 URL 的后缀或 MIME 类型动态确定扩展名。

良好实践

  • apps/worker/worker_app/tasks/generation.py 中,修改 virtual_plan.config 前使用了 dict(...) 进行浅拷贝,避免了直接修改可能被共享的原始配置对象,这是良好的防御性编程实践。
  • 前端 TypeScript 接口定义清晰,Props 传递链路完整。
  • 后端新增的 title_text 参数使用了 Optional 和默认值,保持了向后兼容性。

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

### 【阻塞级判定】 - 是否存在阻塞级问题:是 - 阻塞级问题数量:2 个 ### 📊 审查概览 - 整体评价:需修改 - 建议级问题数量:2 个 ### 🔴 阻塞级问题(必须修复) 1. **[apps/api/app/api/routes/generation_cover.py: 79-95] 资源泄漏风险** - 问题类型:逻辑bug / 数据损坏风险 - 问题描述:在 `_apply_title_to_cover_image` 函数中,`tmp_dir` 临时目录的清理逻辑(`finally` 块)仅挂载在 OSS 上传的 `try` 块下。如果在“下载原图”或“FFmpeg 处理”阶段发生异常并提前 `return`,将导致 `tmp_dir` 无法被删除,长期运行会造成服务器磁盘空间泄漏(Disk Leak)。 - 修改建议:将临时目录的清理逻辑(`finally` 块)提升,覆盖整个函数的处理流程,或者使用 `contextlib` 或 `try...finally` 包裹所有可能产生 return 的路径。 2. **[apps/web/src/pages/generate/hooks/useStep6Cover.ts: 140] useEffect 无限循环** - 问题类型:逻辑bug - 问题描述:`useEffect` 的依赖数组中包含了 `generating` 状态。该 Effect 内部会修改 `generating` 状态(先设为 true 请求接口,完成后设为 false)。当 `generating` 变为 false 时,会再次触发 Effect 执行,导致“生成 -> 完成 -> 再生成”的死循环,持续向服务端发送请求。 - 修改建议:从依赖数组中移除 `generating`。Effect 应仅由业务数据(如 `selectedTemplate`, `assetIds`, `titleText` 等)的变化触发,而不应由自身的执行状态触发。 ### 💡 改进建议(不阻塞合并) 1. **[apps/api/app/api/routes/generation_cover.py: 274-288 & 312-327] 代码重复** - 具体内容:在 `generate_cover` 函数中,两处调用 `_apply_title_to_cover_image` 的逻辑完全一致(try-except 包裹、日志记录)。建议将此逻辑提取为一个局部辅助函数,避免代码重复,降低维护成本。 2. **[apps/api/app/api/routes/generation_cover.py: 72] 硬编码文件扩展名** - 具体内容:`src_path` 和 OSS 上传的 `storage_key` 均硬编码为 `.jpg`。如果原始封面图是 PNG 或 WebP 格式,虽然 FFmpeg 通常能识别内容,但强制保存为 JPG 可能会丢失透明通道信息。建议根据原始 URL 的后缀或 MIME 类型动态确定扩展名。 ### ✅ 良好实践 - 在 `apps/worker/worker_app/tasks/generation.py` 中,修改 `virtual_plan.config` 前使用了 `dict(...)` 进行浅拷贝,避免了直接修改可能被共享的原始配置对象,这是良好的防御性编程实践。 - 前端 TypeScript 接口定义清晰,Props 传递链路完整。 - 后端新增的 `title_text` 参数使用了 `Optional` 和默认值,保持了向后兼容性。 --- <sub>🤖 由 AI 代码审查机器人自动生成 | 2026-08-14 06:52:01 | 模型: </sub> <!-- AI_CODE_REVIEW_AUTO_COMMENT -->
auto-approve-bot approved these changes 2026-08-14 14:55:34 +08:00
auto-approve-bot left a comment
Collaborator

CI全绿,自动审批通过。

CI全绿,自动审批通过。
auto-approve-bot approved these changes 2026-08-14 14:55:34 +08:00
auto-approve-bot left a comment
Collaborator

CI全绿,自动审批通过。

CI全绿,自动审批通过。
xiaoxia closed this pull request 2026-08-15 09:49:17 +08:00
Author
Owner

#1368 已合入相同修复,此PR关闭。感谢贡献。

#1368 已合入相同修复,此PR关闭。感谢贡献。

🗑️ 预览环境已清理

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

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

🗑️ **预览环境已清理** PR #1367 已关闭或合并,对应的预览环境已被清理。 > 如有需要,可以重新打开 PR 来重新生成预览环境。
Some checks are pending
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 48s
CI/CD Pipeline / Validate - Type Check (mypy) (pull_request) Successful in 1m24s
CI/CD Pipeline / Validate - Migration (alembic) (pull_request) Successful in 1m25s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 1m28s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 1m47s
CI/CD Pipeline / Frontend Lint (pull_request) Successful in 1m9s
AI Code Review / AI Code Review (pull_request) Failing after 1m57s
CI/CD Pipeline / Frontend Unit Tests (pull_request) Successful in 1m47s
CI/CD Pipeline / PR Build Web Image (pull_request) Successful in 2m14s
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 1m46s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 3m59s
CI/CD Pipeline / Unit Tests (pull_request) Failing after 3m26s
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 4m28s
CI/CD Pipeline / Validate - Code Quality (pull_request) Successful in 5m27s
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 / Integration Tests (pull_request) Successful in 2m21s
CI/CD Pipeline / CI Gate (pull_request) Failing after 6s
ACR Cleanup / ACR Image Cleanup (pull_request_target) Has been cancelled
Preview Cleanup / Cleanup Preview Environment (pull_request) Successful in 1m0s

Pull request closed

Sign in to join this conversation.