fix: 修复预览播放器视频无法播放和标题不显示(二次修复) #1424

Closed
xiaoxia wants to merge 1 commits from fix/preview-player-v2 into develop
Owner

修复内容

修复 PR #1423 未解决的3个根因:

Bug 1: video.src 检查失效

  • video.src = "" 在浏览器中被解析为当前页面 URL
  • 导致 video.src === "" 永远为 false
  • switchToSegment(0) 被跳过,play() 在无效 src 上失败
  • 修复: 用 srcLoadedRef 标记替代 video.src 检查

Bug 2: switchToSegment 竞态条件

  • 先设 video.src 再绑 loadedmetadata 监听器
  • 如果视频已缓存,事件在绑定前触发 → Promise 永不 resolve
  • 修复: 先绑事件再设 src + 检查 readyState + 10s 超时 + 显式 video.load()

Bug 3: 嵌套 .xx-preview-video 容器导致 CSS 冲突

  • FrontendPreviewPlayer 内部创建了 .xx-preview-video
  • 嵌套在 PreviewVideoPanel 的 .xx-preview-video
  • 双层 overflow:hidden + display:grid 导致布局异常
  • 修复: FrontendPreviewPlayer 改为 Fragment + absolute 定位

修改文件

  • useSegmentScheduler.ts: srcLoadedRef + readyState 检查 + video.load() + 超时保护
  • FrontendPreviewPlayer.tsx: 移除嵌套容器,改为 Fragment
  • PreviewVideoPanel.tsx: 简化为单一容器,TitleOverlay z-index:20
## 修复内容 修复 PR #1423 未解决的3个根因: ### Bug 1: video.src 检查失效 - `video.src = ""` 在浏览器中被解析为当前页面 URL - 导致 `video.src === ""` 永远为 false - `switchToSegment(0)` 被跳过,play() 在无效 src 上失败 - **修复**: 用 `srcLoadedRef` 标记替代 video.src 检查 ### Bug 2: switchToSegment 竞态条件 - 先设 `video.src` 再绑 `loadedmetadata` 监听器 - 如果视频已缓存,事件在绑定前触发 → Promise 永不 resolve - **修复**: 先绑事件再设 src + 检查 readyState + 10s 超时 + 显式 video.load() ### Bug 3: 嵌套 .xx-preview-video 容器导致 CSS 冲突 - FrontendPreviewPlayer 内部创建了 `.xx-preview-video` - 嵌套在 PreviewVideoPanel 的 `.xx-preview-video` 中 - 双层 overflow:hidden + display:grid 导致布局异常 - **修复**: FrontendPreviewPlayer 改为 Fragment + absolute 定位 ## 修改文件 - `useSegmentScheduler.ts`: srcLoadedRef + readyState 检查 + video.load() + 超时保护 - `FrontendPreviewPlayer.tsx`: 移除嵌套容器,改为 Fragment - `PreviewVideoPanel.tsx`: 简化为单一容器,TitleOverlay z-index:20
xiaoxia added 1 commit 2026-08-18 17:44:06 +08:00
fix: 修复预览播放器视频无法播放和标题不显示
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 52s
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
AI Code Review / AI Code Review (pull_request) Failing after 1m3s
CI/CD Pipeline / Validate - Type Check (mypy) (pull_request) Successful in 2m6s
CI/CD Pipeline / Validate - Migration (alembic) (pull_request) Successful in 1m54s
CI/CD Pipeline / Unit Tests (pull_request) Has been skipped
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 1m24s
Preview Deploy / Deploy Preview Environment (pull_request) Failing after 1m33s
CI/CD Pipeline / PR Build API Image (pull_request) Has been skipped
CI/CD Pipeline / Frontend Lint (pull_request) Failing after 1m23s
CI/CD Pipeline / PR Build Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been skipped
PR Automation / Auto Approve on CI Green (pull_request) Successful in 2m15s
CI/CD Pipeline / Frontend Unit Tests (pull_request) Successful in 33s
CI/CD Pipeline / Validate - Code Quality (pull_request) Successful in 6m46s
Preview Cleanup / Cleanup Preview Environment (pull_request) Successful in 51s
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
ACR Cleanup / ACR Image Cleanup (pull_request_target) Successful in 33s
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 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 / PR Build Web Image (pull_request) Failing after 5m35s
CI/CD Pipeline / CI Gate (pull_request) Failing after 10s
3ddde4c650
修复3个根因:
1. useSegmentScheduler: 用 srcLoadedRef 替代 video.src 检查
   - video.src='' 在浏览器中被解析为页面URL,导致条件永远为false
   - switchToSegment 先绑事件再设src,加 readyState 检查和10s超时
   - 显式调用 video.load() 确保加载启动
2. FrontendPreviewPlayer: 移除嵌套的 .xx-preview-video 容器
   - 改为 Fragment + absolute 定位,避免双层 overflow:hidden 冲突
   - 视频/控制条/播放按钮直接填充父容器
3. PreviewVideoPanel: 简化为单一 .xx-preview-video 容器
   - TitleOverlay z-index:20 确保在视频(z:1)和控制条(z:10)之上
xiaoxia closed this pull request 2026-08-18 17:45:27 +08:00
Collaborator

【阻塞级判定】

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

📊 审查概览

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

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

  1. [apps/web/src/pages/generate/hooks/useSegmentScheduler.ts] 变量初始化顺序错误导致 ReferenceError
    • 问题类型:逻辑bug
    • 问题描述:在 switchToSegment 函数中,cleanup 函数定义在代码块前部,它引用了后续才定义的 const timeoutId。虽然 cleanup 是在异步回调中调用的,但代码中存在一个同步执行的判断 if (video.readyState >= 1)。如果视频已缓存(readyState >= 1),代码会立即进入该 if 块并调用 cleanup(),此时 timeoutId 尚未初始化(由于 TDZ - 暂时性死区),导致抛出 ReferenceError: Cannot access 'timeoutId' before initialization,直接导致播放器崩溃。
    • 修改建议:将 const timeoutId 的声明和赋值移动到 if (video.readyState >= 1) 判断之前,或者将 timeoutId 声明为 let timeoutId 并初始化为 null(但这需要调整类型)。推荐直接调整代码顺序位置。

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

  1. [apps/web/src/pages/generate/hooks/useSegmentScheduler.ts] Promise 重复 resolve 风险
    • 具体内容:在 switchToSegment 中,同时监听了 loadedmetadatacanplay 事件,且两者都会调用 resolve()。虽然 Promise 的 resolve 是幂等的(多次调用无效),但 cleanup() 也会被调用多次,导致重复移除事件监听器(虽然 DOM API 允许重复移除)。建议增加一个 resolved 标志位,或者在 loadedmetadata 触发后移除 canplay 监听器,逻辑会更严谨。

良好实践

  • 使用 ref (srcLoadedRef) 替代不可靠的 video.src 字符串检查,有效解决了浏览器将空 src 解析为当前 URL 的问题。
  • 增加了 10 秒超时机制,防止视频加载卡死导致 Promise 永远不 resolve。
  • reset 中使用 removeAttribute("src") 替代赋值空字符串,是更标准的重置 Video 元素的方法。
  • FrontendPreviewPlayerPreviewVideoPanel 的布局重构(将容器控制权上移)逻辑清晰,避免了 CSS 嵌套冲突。

格式检查通过 | 逻辑审查需修改 | 性能良好


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

### 【阻塞级判定】 - 是否存在阻塞级问题:是 - 阻塞级问题数量:1 个 ### 📊 审查概览 - 整体评价:需修改 - 建议级问题数量:1 个 ### 🔴 阻塞级问题(必须修复) 1. **[apps/web/src/pages/generate/hooks/useSegmentScheduler.ts] 变量初始化顺序错误导致 ReferenceError** - 问题类型:逻辑bug - 问题描述:在 `switchToSegment` 函数中,`cleanup` 函数定义在代码块前部,它引用了后续才定义的 `const timeoutId`。虽然 `cleanup` 是在异步回调中调用的,但代码中存在一个同步执行的判断 `if (video.readyState >= 1)`。如果视频已缓存(readyState >= 1),代码会立即进入该 if 块并调用 `cleanup()`,此时 `timeoutId` 尚未初始化(由于 TDZ - 暂时性死区),导致抛出 `ReferenceError: Cannot access 'timeoutId' before initialization`,直接导致播放器崩溃。 - 修改建议:将 `const timeoutId` 的声明和赋值移动到 `if (video.readyState >= 1)` 判断之前,或者将 `timeoutId` 声明为 `let timeoutId` 并初始化为 `null`(但这需要调整类型)。推荐直接调整代码顺序位置。 ### 💡 改进建议(不阻塞合并) 1. **[apps/web/src/pages/generate/hooks/useSegmentScheduler.ts] Promise 重复 resolve 风险** - 具体内容:在 `switchToSegment` 中,同时监听了 `loadedmetadata` 和 `canplay` 事件,且两者都会调用 `resolve()`。虽然 Promise 的 resolve 是幂等的(多次调用无效),但 `cleanup()` 也会被调用多次,导致重复移除事件监听器(虽然 DOM API 允许重复移除)。建议增加一个 `resolved` 标志位,或者在 `loadedmetadata` 触发后移除 `canplay` 监听器,逻辑会更严谨。 ### ✅ 良好实践 - 使用 `ref` (`srcLoadedRef`) 替代不可靠的 `video.src` 字符串检查,有效解决了浏览器将空 src 解析为当前 URL 的问题。 - 增加了 10 秒超时机制,防止视频加载卡死导致 Promise 永远不 resolve。 - 在 `reset` 中使用 `removeAttribute("src")` 替代赋值空字符串,是更标准的重置 Video 元素的方法。 - `FrontendPreviewPlayer` 和 `PreviewVideoPanel` 的布局重构(将容器控制权上移)逻辑清晰,避免了 CSS 嵌套冲突。 --- ✅ 格式检查通过 | ❌ 逻辑审查需修改 | ✅ 性能良好 --- <sub>🤖 由 AI 代码审查机器人自动生成 | 2026-08-18 09:46:04 | 模型: </sub> <!-- AI_CODE_REVIEW_AUTO_COMMENT -->

🗑️ 预览环境已清理

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

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

🗑️ **预览环境已清理** PR #1424 已关闭或合并,对应的预览环境已被清理。 > 如有需要,可以重新打开 PR 来重新生成预览环境。
Some checks are pending
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 52s
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
AI Code Review / AI Code Review (pull_request) Failing after 1m3s
CI/CD Pipeline / Validate - Type Check (mypy) (pull_request) Successful in 2m6s
CI/CD Pipeline / Validate - Migration (alembic) (pull_request) Successful in 1m54s
CI/CD Pipeline / Unit Tests (pull_request) Has been skipped
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 1m24s
Preview Deploy / Deploy Preview Environment (pull_request) Failing after 1m33s
CI/CD Pipeline / PR Build API Image (pull_request) Has been skipped
CI/CD Pipeline / Frontend Lint (pull_request) Failing after 1m23s
CI/CD Pipeline / PR Build Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been skipped
PR Automation / Auto Approve on CI Green (pull_request) Successful in 2m15s
CI/CD Pipeline / Frontend Unit Tests (pull_request) Successful in 33s
CI/CD Pipeline / Validate - Code Quality (pull_request) Successful in 6m46s
Preview Cleanup / Cleanup Preview Environment (pull_request) Successful in 51s
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
ACR Cleanup / ACR Image Cleanup (pull_request_target) Successful in 33s
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 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 / PR Build Web Image (pull_request) Failing after 5m35s
CI/CD Pipeline / CI Gate (pull_request) Failing after 10s

Pull request closed

Sign in to join this conversation.