feat(#1677): 批量预览改纯前端Canvas实时预览 + 固定6步流程 + 第5步独立渲染进度 #1711

Merged
auto-approve-bot merged 1 commits from feat/1677-canvas-batch-preview into develop 2026-09-05 12:31:28 +08:00
Owner

背景

PR #1704 的批量预览错误地调后端 POST /generation/preview?preview_count=N 真渲染,导致预览卡死、429、容器重启。预览不需要渲染——本次按实测反馈修正。

改动

1. 固定 6 步流程(单视频与批量完全一致)

1 选择模板(下一步弹数量弹窗)→ 2 选择素材 → 3 选择配音 → 4 选择标题 → 5 确认生成 → 6 选择封面

2. 第4步:纯前端 Canvas 实时预览(不调任何后端渲染接口)

  • 单视频:1 个 FrontendPreviewPlayer(与旧版一致,零回归)
  • 批量:N 个播放器网格(CanvasPreviewGrid),新增 variantSeed:mulberry32 可复现随机,素材轮换+交换、片段起始点偏移,N 个变体画面有可见差异;seed=0 走旧逻辑
  • 各变体叠独立标题浮层(variantTitle),标题样式全局共用;仅第一个变体播放共用配音避免多路音频重叠
  • 变体卡片带勾选框,勾选决定生成哪些视频

3. 第4步右侧:批量标题补 AI / 标题库

  • N 个输入框全部改为 AutoComplete(接用户标题库,支持筛选)
  • 一键生成 N 个标题」+ 主题词输入 + 补填空标题(本地 AI_TITLE_TEMPLATES 同源模板池洗牌)
  • 变体0 与主标题双向同步

4. 第4步底部:「 确认生成视频 / 确认生成 N 个视频」

  • 按 PR #1701 契约提交 count + titles[]/voice_library_ids[]/cover_urls[]
  • 校验:勾选数>0、勾选变体标题非空、素材就绪;成功后单视频/批量统一跳第5步

5. 第5步「确认生成」:正式渲染实时进度

  • 批量 BatchGenerationGrid:每任务独立进度/成片播放/失败原因+「重试此视频」
  • 轮询 v4:任务独立状态机,部分失败不阻塞成功任务;全部失败才整体失败;重试走 POST /tasks/{id}/retry 后重新轮询;重试成功自动聚合进成片列表
  • 单视频:进度卡/失败重试/成功提示 + 右侧成片播放器
  • 全部到终态后「下一步:选择封面」解锁

6. 清理

  • 删除 useBatchPreviewServerPreviewGrid 及 preview 渲染接口的所有前端调用(后端接口保留不删)
  • E2E 适配回 6 步断言(数量弹窗确认 → step5 确认生成 → step6 选择封面)

验收

  • N=1 全程零回归(seed=0 旧逻辑、单视频分支保持原样)
  • N=3:3 个前端预览秒开、确认后第5步看 3 条真实渲染进度、完成进封面
  • 本地 tsc / eslint / prettier / vitest / build 全绿

Closes #1677

## 背景 PR #1704 的批量预览错误地调后端 `POST /generation/preview?preview_count=N` 真渲染,导致预览卡死、429、容器重启。**预览不需要渲染**——本次按实测反馈修正。 ## 改动 ### 1. 固定 6 步流程(单视频与批量完全一致) 1 选择模板(下一步弹数量弹窗)→ 2 选择素材 → 3 选择配音 → 4 选择标题 → 5 确认生成 → 6 选择封面 ### 2. 第4步:纯前端 Canvas 实时预览(不调任何后端渲染接口) - 单视频:1 个 `FrontendPreviewPlayer`(与旧版一致,零回归) - 批量:N 个播放器网格(`CanvasPreviewGrid`),新增 `variantSeed`:mulberry32 可复现随机,素材轮换+交换、片段起始点偏移,N 个变体画面有可见差异;seed=0 走旧逻辑 - 各变体叠独立标题浮层(variantTitle),标题样式全局共用;仅第一个变体播放共用配音避免多路音频重叠 - 变体卡片带勾选框,勾选决定生成哪些视频 ### 3. 第4步右侧:批量标题补 AI / 标题库 - N 个输入框全部改为 AutoComplete(接用户标题库,支持筛选) - 「✨ 一键生成 N 个标题」+ 主题词输入 + 补填空标题(本地 AI_TITLE_TEMPLATES 同源模板池洗牌) - 变体0 与主标题双向同步 ### 4. 第4步底部:「✨ 确认生成视频 / 确认生成 N 个视频」 - 按 PR #1701 契约提交 count + titles[]/voice_library_ids[]/cover_urls[] - 校验:勾选数>0、勾选变体标题非空、素材就绪;成功后单视频/批量统一跳第5步 ### 5. 第5步「确认生成」:正式渲染实时进度 - 批量 `BatchGenerationGrid`:每任务独立进度/成片播放/失败原因+「重试此视频」 - 轮询 v4:任务独立状态机,部分失败不阻塞成功任务;全部失败才整体失败;重试走 `POST /tasks/{id}/retry` 后重新轮询;重试成功自动聚合进成片列表 - 单视频:进度卡/失败重试/成功提示 + 右侧成片播放器 - 全部到终态后「下一步:选择封面」解锁 ### 6. 清理 - 删除 `useBatchPreview`、`ServerPreviewGrid` 及 preview 渲染接口的所有前端调用(后端接口保留不删) - E2E 适配回 6 步断言(数量弹窗确认 → step5 确认生成 → step6 选择封面) ## 验收 - N=1 全程零回归(seed=0 旧逻辑、单视频分支保持原样) - N=3:3 个前端预览秒开、确认后第5步看 3 条真实渲染进度、完成进封面 - 本地 tsc / eslint / prettier / vitest / build 全绿 Closes #1677
xiaoxia added 1 commit 2026-09-05 12:25:40 +08:00
feat(#1677): 批量预览改纯前端Canvas实时预览+固定6步流程+第5步独立渲染进度
CI/CD Pipeline / Check push changed paths (pull_request) Has been skipped
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Successful in 1s
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 2s
CI/CD Pipeline / Unit Tests (pull_request) Has been skipped
CI/CD Pipeline / Integration Tests (pull_request) Has been skipped
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 / PR Build API Image (pull_request) Has been skipped
CI/CD Pipeline / PR Build Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Has been skipped
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Has been skipped
CI/CD Pipeline / Retag skipped 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
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 1m29s
CI/CD Pipeline / Frontend Lint (pull_request) Successful in 1m38s
CI/CD Pipeline / Frontend Unit Tests (pull_request) Successful in 1m41s
CI/CD Pipeline / PR Build Web Image (pull_request) Successful in 1m40s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 1m47s
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Successful in 1m48s
CI/CD Pipeline / Validate - Style (pull_request) Successful in 2m14s
AI Code Review / AI Code Review (pull_request) Failing after 3m29s
CI/CD Pipeline / Validate - Security (pull_request) Successful in 5m10s
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 2s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 4m0s
ACR Cleanup / ACR Image Cleanup (pull_request_target) Successful in 9s
Preview Cleanup / Cleanup Preview Environment (pull_request) Successful in 22s
ac28530528
- 预览纯前端化:N 个 FrontendPreviewPlayer 网格(CanvasPreviewGrid),variantSeed
  让素材排布/起始点不同、画面有差异;不调任何后端渲染接口,秒开不占 worker;
  seed=0 走旧逻辑保证 N=1 零回归
- 固定 6 步(单视频与批量一致):模板(弹数量) → 素材 → 配音 → 标题 → 确认生成 → 封面
- 第4步右侧批量标题:N 个独立输入框(AutoComplete 接标题库)+ AI 一键生成 N 个标题
  (本地模板池同源)+ 主题词补填空标题;标题样式全局共用
- 第4步底部「 确认生成 N 个视频」:按 PR#1701 契约提交 count +
  titles[]/voice_library_ids[]/cover_urls[],成功后跳第5步
- 第5步「确认生成」:正式渲染实时进度,批量逐任务独立状态(BatchGenerationGrid,
  部分失败不阻塞、失败卡片单独「重试此视频」走 POST /tasks/{id}/retry),
  全部有终态后可进封面;单视频进度/失败/成功卡 + 右侧成片播放器
- 删除 useBatchPreview、ServerPreviewGrid 及 preview 渲染接口的所有前端调用
  (后端接口保留);E2E 适配回 6 步断言

🚀 预览环境已部署

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

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

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

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

🚀 **预览环境已部署** | 项目 | 详情 | |------|------| | PR号 | #1711 | | 预览链接 | [https://pr-1711.preview.xiaoxiajianji.com](https://pr-1711.preview.xiaoxiajianji.com) | | API环境 | staging | > 💡 预览环境使用 staging API 数据,请勿在预览环境中操作重要数据。 > > 🔄 每次提交新代码后预览环境会自动更新。 > > 🗑️ PR 关闭或合并后,预览环境会自动清理。
auto-approve-bot approved these changes 2026-09-05 12:27:27 +08:00
auto-approve-bot left a comment
Collaborator

CI全绿,自动审批通过。

CI全绿,自动审批通过。
auto-approve-bot approved these changes 2026-09-05 12:27:27 +08:00
auto-approve-bot left a comment
Collaborator

CI全绿,自动审批通过。

CI全绿,自动审批通过。
Collaborator

【阻塞级判定】

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

📊 审查概览

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

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

  1. [apps/web/src/pages/generate/GeneratePage.tsx: 324] 单视频模式下 AI 标题未完成校验
    • 问题类型:逻辑bug
    • 问题描述:在 handleConfirmGenerate 函数中,单视频模式的标题校验逻辑为 !titleSettings.aiAutoSelect && !titleSettings.title?.trim()。这意味着如果用户开启了“AI 自动选择标题”(aiAutoSelect 为 true),代码会跳过标题非空校验。如果此时 AI 标题尚未生成完成(title 为空字符串),用户点击确认将导致提交空标题给后端,生成无标题的视频或导致后端报错。
    • 修改建议:无论是否开启 AI 自动选择,都应校验标题是否为空。建议修改为:if (!titleSettings.title?.trim()) { message.warning("请先选择或输入标题"); return; }

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

  1. [apps/web/src/pages/generate/GeneratePage.tsx: 143] TTS 预览依赖项可能导致不必要的请求

    • 具体内容:在 useEffect 中,依赖数组包含了整个 previewTitles 数组。在批量模式下,实际只使用了 previewTitles[0]。当用户修改第 2、3 个视频的标题时,previewTitles 引用变化会导致该 effect 重新执行,触发不必要的 previewTts API 请求。建议提取 previewTitles[0] 或使用 useMemo 优化依赖项。
  2. [apps/web/src/pages/generate/components/BatchGenerationGrid.tsx: 34] 数组排序的健壮性

    • 具体内容:tasks.sort((a, b) => a.variantIndex - b.variantIndex) 假设 variantIndex 始终存在且为数字。如果后端数据异常导致 variantIndexundefined,计算结果为 NaN,可能导致排序不稳定或报错。建议增加默认值处理:(a.variantIndex || 0) - (b.variantIndex || 0)
  3. [apps/web/src/pages/generate/components/CanvasPreviewGrid.tsx: 60] 批量预览性能风险

    • 具体内容:代码直接根据 count 渲染对应数量的 FrontendPreviewPlayer 组件(包含 <canvas><video>)。如果 count 数值较大(例如超过 20-50),浏览器可能因同时渲染过多媒体上下文而卡顿或崩溃。虽然 PreviewCountModal 可能有限制,但建议在 CanvasPreviewGrid 内部增加硬编码上限保护(如 Math.min(count, 20)),或确保上游输入有严格校验。

良好实践

  • 前端预览架构优化:将批量预览从“服务器端渲染”改为“纯前端 Canvas 预览”,显著降低了服务器成本和预览延迟,提升了用户体验。
  • E2E 测试更新:及时更新了 E2E 测试用例以覆盖新的 6 步流程,保证了回归测试的有效性。
  • 批量任务独立状态管理BatchGenerationGrid 对每个生成任务进行独立的进度和错误处理展示,用户体验更加清晰。

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


🤖 由 AI 代码审查机器人自动生成 | 2026-09-05 04:29:09 | 模型:

### 【阻塞级判定】 - 是否存在阻塞级问题:是 - 阻塞级问题数量:1 个 ### 📊 审查概览 - 整体评价:需修改 - 建议级问题数量:3 个 ### 🔴 阻塞级问题(必须修复) 1. **[apps/web/src/pages/generate/GeneratePage.tsx: 324] 单视频模式下 AI 标题未完成校验** - 问题类型:逻辑bug - 问题描述:在 `handleConfirmGenerate` 函数中,单视频模式的标题校验逻辑为 `!titleSettings.aiAutoSelect && !titleSettings.title?.trim()`。这意味着如果用户开启了“AI 自动选择标题”(`aiAutoSelect` 为 true),代码会跳过标题非空校验。如果此时 AI 标题尚未生成完成(`title` 为空字符串),用户点击确认将导致提交空标题给后端,生成无标题的视频或导致后端报错。 - 修改建议:无论是否开启 AI 自动选择,都应校验标题是否为空。建议修改为:`if (!titleSettings.title?.trim()) { message.warning("请先选择或输入标题"); return; }`。 ### 💡 改进建议(不阻塞合并) 1. **[apps/web/src/pages/generate/GeneratePage.tsx: 143] TTS 预览依赖项可能导致不必要的请求** - 具体内容:在 `useEffect` 中,依赖数组包含了整个 `previewTitles` 数组。在批量模式下,实际只使用了 `previewTitles[0]`。当用户修改第 2、3 个视频的标题时,`previewTitles` 引用变化会导致该 effect 重新执行,触发不必要的 `previewTts` API 请求。建议提取 `previewTitles[0]` 或使用 useMemo 优化依赖项。 2. **[apps/web/src/pages/generate/components/BatchGenerationGrid.tsx: 34] 数组排序的健壮性** - 具体内容:`tasks.sort((a, b) => a.variantIndex - b.variantIndex)` 假设 `variantIndex` 始终存在且为数字。如果后端数据异常导致 `variantIndex` 为 `undefined`,计算结果为 `NaN`,可能导致排序不稳定或报错。建议增加默认值处理:`(a.variantIndex || 0) - (b.variantIndex || 0)`。 3. **[apps/web/src/pages/generate/components/CanvasPreviewGrid.tsx: 60] 批量预览性能风险** - 具体内容:代码直接根据 `count` 渲染对应数量的 `FrontendPreviewPlayer` 组件(包含 `<canvas>` 和 `<video>`)。如果 `count` 数值较大(例如超过 20-50),浏览器可能因同时渲染过多媒体上下文而卡顿或崩溃。虽然 `PreviewCountModal` 可能有限制,但建议在 `CanvasPreviewGrid` 内部增加硬编码上限保护(如 `Math.min(count, 20)`),或确保上游输入有严格校验。 ### ✅ 良好实践 - **前端预览架构优化**:将批量预览从“服务器端渲染”改为“纯前端 Canvas 预览”,显著降低了服务器成本和预览延迟,提升了用户体验。 - **E2E 测试更新**:及时更新了 E2E 测试用例以覆盖新的 6 步流程,保证了回归测试的有效性。 - **批量任务独立状态管理**:`BatchGenerationGrid` 对每个生成任务进行独立的进度和错误处理展示,用户体验更加清晰。 --- ✅ 格式检查通过 | ❌ 逻辑审查需修改 | ⚠️ 建议关注性能 --- <sub>🤖 由 AI 代码审查机器人自动生成 | 2026-09-05 04:29:09 | 模型: </sub> <!-- AI_CODE_REVIEW_AUTO_COMMENT -->
auto-approve-bot merged commit c321ac3af8 into develop 2026-09-05 12:31:28 +08:00
auto-approve-bot deleted branch feat/1677-canvas-batch-preview 2026-09-05 12:31:29 +08:00

🗑️ 预览环境已清理

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

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

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