fix: Canvas黑屏修复+配音时长校验fallback #1438

Closed
xiaoxia wants to merge 1 commits from fix/canvas-black-screen-voice-validation into develop
Owner

Bug 1: Canvas 播放器黑屏

文件: apps/web/src/pages/generate/hooks/useCanvasPlayer.ts

根因: renderFramectx.drawImage(frame, ...) 后立即调用 frame.close() 释放 GPU 纹理。虽然 drawImage 是同步操作,但 FrameQueue.getCurrentFrame() 已经负责帧生命周期管理(关闭已过期的旧帧)。双重 close() 在某些浏览器/GPU 驱动下导致帧数据无效,呈现黑屏。

修复: 删除 renderFrame 中的 frame.close() 调用。帧释放统一由 FrameQueue.getCurrentFrame()clear() 管理。

Bug 2: 配音时长校验不生效

文件:

  • apps/web/src/pages/generate/GeneratePage.tsx
  • apps/web/src/pages/generate/components/Step5VoiceSelect.tsx
  • apps/web/src/pages/generate/utils/calculateTotalVideoDuration.ts(新增)

根因: totalVideoDurationpreviewAssets 为空时直接返回 0,导致 Step5VoiceSelectif (totalVideoDuration > 0) 条件永远不满足,校验逻辑被跳过。

修复:

  1. 提取 calculateTotalVideoDuration / estimateTotalVideoDuration 为共享工具函数
  2. GeneratePage.tsx: 素材为空时用模板 segments 的 duration_max 之和估算
  3. Step5VoiceSelect.tsx: Number() 类型转换防止后端返回字符串导致比较错误
  4. Step5VoiceSelect.tsx: 添加 debug log 便于后续排查

验证标准

  • npm run build TypeScript 无报错
  • ESLint --max-warnings 0 通过
  • Prettier 格式化通过
## Bug 1: Canvas 播放器黑屏 **文件**: `apps/web/src/pages/generate/hooks/useCanvasPlayer.ts` **根因**: `renderFrame` 中 `ctx.drawImage(frame, ...)` 后立即调用 `frame.close()` 释放 GPU 纹理。虽然 `drawImage` 是同步操作,但 `FrameQueue.getCurrentFrame()` 已经负责帧生命周期管理(关闭已过期的旧帧)。双重 `close()` 在某些浏览器/GPU 驱动下导致帧数据无效,呈现黑屏。 **修复**: 删除 `renderFrame` 中的 `frame.close()` 调用。帧释放统一由 `FrameQueue.getCurrentFrame()` 和 `clear()` 管理。 ## Bug 2: 配音时长校验不生效 **文件**: - `apps/web/src/pages/generate/GeneratePage.tsx` - `apps/web/src/pages/generate/components/Step5VoiceSelect.tsx` - `apps/web/src/pages/generate/utils/calculateTotalVideoDuration.ts`(新增) **根因**: `totalVideoDuration` 在 `previewAssets` 为空时直接返回 0,导致 `Step5VoiceSelect` 中 `if (totalVideoDuration > 0)` 条件永远不满足,校验逻辑被跳过。 **修复**: 1. 提取 `calculateTotalVideoDuration` / `estimateTotalVideoDuration` 为共享工具函数 2. `GeneratePage.tsx`: 素材为空时用模板 segments 的 `duration_max` 之和估算 3. `Step5VoiceSelect.tsx`: `Number()` 类型转换防止后端返回字符串导致比较错误 4. `Step5VoiceSelect.tsx`: 添加 debug log 便于后续排查 ## 验证标准 - [x] `npm run build` TypeScript 无报错 - [x] ESLint `--max-warnings 0` 通过 - [x] Prettier 格式化通过
xiaoxia added 1 commit 2026-08-19 13:13:39 +08:00
fix: Canvas黑屏修复+配音时长校验fallback
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 API Integration Tests (pull_request) Has been skipped
CI/CD Pipeline / ACR Image Cleanup (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 36s
CI/CD Pipeline / Unit Tests (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
AI Code Review / AI Code Review (pull_request) Failing after 1m45s
CI/CD Pipeline / Frontend Unit Tests (pull_request) Successful in 1m8s
CI/CD Pipeline / Validate - Migration (alembic) (pull_request) Successful in 1m47s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 1m48s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 1m58s
CI/CD Pipeline / Validate - Type Check (mypy) (pull_request) Successful in 2m13s
CI/CD Pipeline / Frontend Lint (pull_request) Successful in 2m11s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 2m51s
CI/CD Pipeline / PR Build Web Image (pull_request) Successful in 2m35s
CI/CD Pipeline / Validate - Code Quality (pull_request) Successful in 6m20s
CI/CD Pipeline / Integration Tests (pull_request) Has been skipped
CI/CD Pipeline / Build Production API Image (pull_request) Has been skipped
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Build Production Web 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 20s
ACR Cleanup / ACR Image Cleanup (pull_request_target) Has been cancelled
Preview Cleanup / Cleanup Preview Environment (pull_request) Successful in 43s
c7a202ae3f
Bug 1: Canvas 播放器黑屏
- useCanvasPlayer.ts renderFrame 中删除 frame.close()
- FrameQueue 的 getCurrentFrame 统一管理帧释放,避免双重 close

Bug 2: 配音时长校验不生效
- 新增 calculateTotalVideoDuration 共享工具函数
- GeneratePage.tsx: 素材为空时用模板 duration_max 之和估算总时长
- Step5VoiceSelect.tsx: Number() 类型转换防止字符串比较错误
- Step5VoiceSelect.tsx: 添加 debug log 便于后续排查
Collaborator

【阻塞级判定】

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

📊 审查概览

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

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

  1. [apps/web/src/pages/generate/hooks/useCanvasPlayer.ts: 431] 资源泄漏风险
    • 问题类型:数据损坏风险 / 逻辑Bug
    • 问题描述:代码删除了 frame.close() 调用。在 WebCodecs API 或类似的视频处理逻辑中,VideoFrame 对象属于显式管理的资源,必须调用 close() 来释放底层的显存/内存。在渲染循环(通常由 requestAnimationFrame 驱动)中不释放这些对象会导致严重的内存泄漏,长时间运行会导致浏览器标签页崩溃。
    • 修改建议:必须恢复 frame.close() 调用,或者确认 frame 对象类型不需要手动释放(但根据原代码判断,它是需要的)。建议检查 frame 是否存在后再调用 close()

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

  1. [建议] 消除魔法数字
    • 具体内容:在 apps/web/src/pages/generate/utils/calculateTotalVideoDuration.ts 文件中,代码里出现了硬编码的默认时长数值 30(第 39 行)和 10(第 43 行)。建议将这些数值提取为常量(例如 DEFAULT_ASSET_DURATIONFALLBACK_SEGMENT_DURATION),并添加注释说明其业务含义,以提高代码可维护性。

良好实践

  • Step5VoiceSelect.tsx 中使用 Number() 显式转换 duration 进行比较,有效避免了字符串与数字比较可能产生的隐式类型转换错误。
  • 重构 GeneratePage.tsx 中的时长计算逻辑,将其提取为独立的工具函数,提高了代码的可复用性和可测试性,逻辑分层更清晰。

🤖 由 AI 代码审查机器人自动生成 | 2026-08-19 05:15:18 | 模型:

### 【阻塞级判定】 - 是否存在阻塞级问题:是 - 阻塞级问题数量:1 个 ### 📊 审查概览 - 整体评价:需修改 - 建议级问题数量:1 个 ### 🔴 阻塞级问题(必须修复) 1. **[apps/web/src/pages/generate/hooks/useCanvasPlayer.ts: 431] 资源泄漏风险** - 问题类型:数据损坏风险 / 逻辑Bug - 问题描述:代码删除了 `frame.close()` 调用。在 WebCodecs API 或类似的视频处理逻辑中,`VideoFrame` 对象属于显式管理的资源,必须调用 `close()` 来释放底层的显存/内存。在渲染循环(通常由 `requestAnimationFrame` 驱动)中不释放这些对象会导致严重的内存泄漏,长时间运行会导致浏览器标签页崩溃。 - 修改建议:必须恢复 `frame.close()` 调用,或者确认 `frame` 对象类型不需要手动释放(但根据原代码判断,它是需要的)。建议检查 `frame` 是否存在后再调用 `close()`。 ### 💡 改进建议(不阻塞合并) 1. **[建议] 消除魔法数字** - 具体内容:在 `apps/web/src/pages/generate/utils/calculateTotalVideoDuration.ts` 文件中,代码里出现了硬编码的默认时长数值 `30`(第 39 行)和 `10`(第 43 行)。建议将这些数值提取为常量(例如 `DEFAULT_ASSET_DURATION` 和 `FALLBACK_SEGMENT_DURATION`),并添加注释说明其业务含义,以提高代码可维护性。 ### ✅ 良好实践 - 在 `Step5VoiceSelect.tsx` 中使用 `Number()` 显式转换 `duration` 进行比较,有效避免了字符串与数字比较可能产生的隐式类型转换错误。 - 重构 `GeneratePage.tsx` 中的时长计算逻辑,将其提取为独立的工具函数,提高了代码的可复用性和可测试性,逻辑分层更清晰。 --- <sub>🤖 由 AI 代码审查机器人自动生成 | 2026-08-19 05:15:18 | 模型: </sub> <!-- AI_CODE_REVIEW_AUTO_COMMENT -->

🚀 预览环境已部署

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

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

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

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

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

CI全绿,自动审批通过。

CI全绿,自动审批通过。
auto-approve-bot approved these changes 2026-08-19 13:16:30 +08:00
auto-approve-bot left a comment
Collaborator

CI全绿,自动审批通过。

CI全绿,自动审批通过。
xiaoxia closed this pull request 2026-08-19 20:48:11 +08:00

🗑️ 预览环境已清理

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

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

🗑️ **预览环境已清理** PR #1438 已关闭或合并,对应的预览环境已被清理。 > 如有需要,可以重新打开 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 API Integration Tests (pull_request) Has been skipped
CI/CD Pipeline / ACR Image Cleanup (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 36s
CI/CD Pipeline / Unit Tests (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
AI Code Review / AI Code Review (pull_request) Failing after 1m45s
CI/CD Pipeline / Frontend Unit Tests (pull_request) Successful in 1m8s
CI/CD Pipeline / Validate - Migration (alembic) (pull_request) Successful in 1m47s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 1m48s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 1m58s
CI/CD Pipeline / Validate - Type Check (mypy) (pull_request) Successful in 2m13s
CI/CD Pipeline / Frontend Lint (pull_request) Successful in 2m11s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 2m51s
CI/CD Pipeline / PR Build Web Image (pull_request) Successful in 2m35s
CI/CD Pipeline / Validate - Code Quality (pull_request) Successful in 6m20s
CI/CD Pipeline / Integration Tests (pull_request) Has been skipped
CI/CD Pipeline / Build Production API Image (pull_request) Has been skipped
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Build Production Web 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 20s
ACR Cleanup / ACR Image Cleanup (pull_request_target) Has been cancelled
Preview Cleanup / Cleanup Preview Environment (pull_request) Successful in 43s

Pull request closed

Sign in to join this conversation.