feat: integrate session-backed HLS playback flow#200
Conversation
…y detection - Integrate codec compatibility checking in PlayerPage loadData stage for early format detection - Add TranscodeService client for backend FastAPI JIT transcoding API communication - Implement CodecCompatibilityChecker with browser codec support detection using HTMLVideoElement.canPlayType() - Create TranscodeCacheManager for caching transcoding results and performance optimization - Add TranscodeIndicator UI component with Ant Design styling for transcoding status feedback - Extend player state management with HLS mode support and transcoding status tracking - Refactor VideoSurface to simplify responsibilities, removing active codec detection logic - Update VideoErrorRecovery component to support extended error types including codec-specific errors - Add Task Master integration with comprehensive PRD parsing and task management Changes: - PlayerPage.tsx: Add proactive transcoding detection in loadData with fallback error handling - VideoSurface.tsx: Simplify to focus on playback control, trust pre-processed src from PlayerPage - TranscodeService.ts: Implement HTTP client with retry logic, request deduplication, and error handling - CodecCompatibilityChecker.ts: Browser codec compatibility detection with H.265/HEVC support mapping - TranscodeCacheManager.ts: Local caching with expiration, memory optimization, and batch processing - TranscodeIndicator.tsx: Status indicator component with theme compatibility and loading animations - player.store.ts: Extend state management for HLS mode, transcode status, and source switching This implementation enables seamless video format handling where incompatible formats (H.265, unsupported audio codecs) are automatically transcoded to HLS during data loading, providing users with immediate playback capability when the player interface loads.
…y notifications - Add 'hls-player-missing' error type to ExtendedErrorType for HLS player unavailability - Enhance VideoSurface error detection to identify HLS URL playback failures - Implement HLS-specific error recognition for DEMUXER_ERROR_COULD_NOT_OPEN and FFmpegDemuxer errors - Add user-friendly error dialog in VideoErrorRecovery with transcode status information - Update TranscodeIndicator to show "waiting for HLS player" state when transcode completes - Fix PlayerSettingsLoader type compatibility by adding missing hlsMode and transcodeInfo fields Key Features: - Smart distinction between genuine transcode failures and HLS player absence - Comprehensive status display showing transcode completion and HLS player readiness - Contextual error messages explaining that transcoding succeeded but HLS playback unavailable - Seamless integration preparing for future HLS player implementation (Task 2) Technical Implementation: - Enhanced handleVideoError in VideoSurface with HLS URL pattern detection (.m3u8, playlist) - Extended VideoErrorRecovery with HLS-specific status panel and styled components - Updated TranscodeIndicator logic to show warning state for completed HLS transcodes - Maintained type safety across all player state management components This enhancement bridges the gap between successful transcoding and pending HLS player implementation, providing clear user feedback about system capabilities and limitations. Completes Task 1.9: HLS playback error state handling and user notifications
…ssion management - Add HLSPlayer component with hls.js integration and engine compatibility - Create HLSVideoSurface as complete replacement for VideoSurface with subtitle overlay - Implement PlayerSelector for intelligent switching between native and HLS players - Add SessionService for HLS session management and transcoding coordination - Introduce VideoFormatDetector for comprehensive format analysis and compatibility checking - Refactor TranscodeService architecture by removing legacy cache management - Enhance CodecCompatibilityChecker with extended error handling - Update player store with improved HLS state management - Add file utilities for video format detection and validation Key Features: - Seamless player switching based on codec compatibility - Automatic HLS fallback for unsupported native formats - Unified interface maintaining VideoSurface API compatibility - Session-based HLS streaming with window management - Comprehensive error handling and recovery mechanisms - Integration with existing player engine and state management Technical Implementation: - HLSPlayer: Direct hls.js integration with player engine bridging - PlayerSelector: Smart format detection and automatic player selection - SessionService: RESTful session management for HLS streaming - VideoFormatDetector: Multi-level format analysis (quick/standard/deep) - Enhanced transcoding workflow with session-based architecture This implementation provides a robust foundation for handling diverse video formats while maintaining seamless user experience and backward compatibility.
|
Caution Review failedThe pull request is closed. Walkthrough此变更引入基于 HLS 的转码播放路径:在 PlayerPage 中进行编解码兼容性检测,必要时通过 SessionService 启动转码会话并切换到 HLS 播放;新增 HLSPlayer/HLSVideoSurface/PlayerSelector 等组件与状态管理;扩展错误类型为 ExtendedErrorType;并补充格式检测、会话客户端、UI 指示器与工具函数等。 Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor User as 用户
participant PP as PlayerPage
participant CCC as CodecCompatibilityChecker
participant SS as SessionService
participant Store as Player Store
participant PS as PlayerSelector
participant HV as HLSVideoSurface
participant HP as HLSPlayer
participant NV as VideoSurface
participant HLS as hls.js
User->>PP: 打开视频(file://...)
PP->>CCC: checkCompatibility(filePath)
CCC-->>PP: 兼容性结果(needsTranscode?)
alt 需转码
PP->>Store: setTranscodeStatus(detecting/transcoding)
PP->>SS: createSession(file_path,...)
SS-->>PP: {playlist_url, session_id,...}
PP->>Store: switchToHlsSource(playlist_url,...)
else 不需转码
PP->>Store: switchToOriginalSource()
end
PP->>PS: 请求渲染
alt Store.hlsMode && hlsSrc
PS->>HV: 渲染 HLSVideoSurface
HV->>HP: 初始化 HLSPlayer
HP->>HLS: 加载 m3u8
HLS-->>HP: 事件/错误
HP-->>Store: 更新播放/等待/寻址状态
HP-->>PP: 报告错误
else
PS->>NV: 渲染 VideoSurface(原生)
NV-->>Store: 更新播放/等待/寻址状态
NV-->>PP: 报告错误
end
sequenceDiagram
autonumber
participant VE as Video Element
participant Hook as usePlayerEngine
participant Orch as PlayerOrchestrator
participant Store as Player Store
VE-->>Hook: waiting
Hook->>Orch: onWaiting()
Orch->>Store: setVideoWaiting(true)
VE-->>Hook: canplay / playing
Hook->>Orch: onCanPlay()
Orch->>Store: setVideoWaiting(false)
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60–90 minutes Possibly related PRs
Poem
Pre-merge checks and finishing touches and finishing touches✅ Passed checks (3 passed)
📜 Recent review detailsConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro 📒 Files selected for processing (15)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 12
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (3)
src/renderer/src/pages/player/PlayerPage.tsx (1)
250-263: 释放转码会话资源当前创建了会话
SessionService.createSession(...)却在组件卸载时没有调用SessionService.deleteSession,后端会一直保留这些 session,长时间播放/切换视频会导致资源泄漏。请保存session_id(可用useRef),并在清理阶段异步调用SessionService.deleteSession(sessionId)(加上 try/catch 记录日志),清空 ref,避免堆积。src/renderer/src/pages/player/components/VideoSurface.tsx (2)
143-149: 修正 Surface 的可访问性实现Biome 报错指出
role="button"的 div 应直接使用<button>等语义元素。请改为 button(或通过 styled-components 包装 button),并确保保留现有样式与可聚焦行为,以消除 lint 错误。
180-196: 使用主题 CSS 变量替换硬编码颜色
background: #000同样违背 renderer 的样式规范。请改用项目主题/设计系统提供的 CSS 变量或常量,保持风格与可配置性一致。
(依据样式规范)
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (21)
.coderabbit.yaml(1 hunks).taskmaster/docs/prd.txt(1 hunks).taskmaster/tasks/tasks.json(1 hunks)CLAUDE.md(1 hunks)backend(1 hunks)package.json(1 hunks)src/renderer/src/pages/player/PlayerPage.tsx(7 hunks)src/renderer/src/pages/player/components/HLSPlayer.tsx(1 hunks)src/renderer/src/pages/player/components/HLSVideoSurface.tsx(1 hunks)src/renderer/src/pages/player/components/PlayerSelector.tsx(1 hunks)src/renderer/src/pages/player/components/TranscodeIndicator.tsx(1 hunks)src/renderer/src/pages/player/components/VideoErrorRecovery.tsx(5 hunks)src/renderer/src/pages/player/components/VideoSurface.tsx(6 hunks)src/renderer/src/pages/player/components/index.ts(1 hunks)src/renderer/src/services/CodecCompatibilityChecker.ts(1 hunks)src/renderer/src/services/PlayerSettingsLoader.ts(2 hunks)src/renderer/src/services/SessionService.ts(1 hunks)src/renderer/src/services/VideoFormatDetector.ts(1 hunks)src/renderer/src/services/index.ts(1 hunks)src/renderer/src/state/stores/player.store.ts(5 hunks)src/renderer/src/utils/file.ts(2 hunks)
🧰 Additional context used
📓 Path-based instructions (6)
src/renderer/src/**/*.{ts,tsx,scss,css}
📄 CodeRabbit inference engine (CLAUDE.md)
优先使用 CSS 变量,避免硬编码样式值(颜色等)
Files:
src/renderer/src/services/index.tssrc/renderer/src/pages/player/components/index.tssrc/renderer/src/pages/player/components/VideoErrorRecovery.tsxsrc/renderer/src/pages/player/components/HLSVideoSurface.tsxsrc/renderer/src/pages/player/components/HLSPlayer.tsxsrc/renderer/src/utils/file.tssrc/renderer/src/pages/player/components/PlayerSelector.tsxsrc/renderer/src/services/VideoFormatDetector.tssrc/renderer/src/state/stores/player.store.tssrc/renderer/src/pages/player/components/TranscodeIndicator.tsxsrc/renderer/src/services/CodecCompatibilityChecker.tssrc/renderer/src/services/PlayerSettingsLoader.tssrc/renderer/src/pages/player/components/VideoSurface.tsxsrc/renderer/src/services/SessionService.tssrc/renderer/src/pages/player/PlayerPage.tsx
src/renderer/src/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
尺寸与时长等不要硬编码,优先使用 useTheme() 的 token 或集中样式变量(如 motionDurationMid、borderRadiusSM/MD)
Files:
src/renderer/src/services/index.tssrc/renderer/src/pages/player/components/index.tssrc/renderer/src/pages/player/components/VideoErrorRecovery.tsxsrc/renderer/src/pages/player/components/HLSVideoSurface.tsxsrc/renderer/src/pages/player/components/HLSPlayer.tsxsrc/renderer/src/utils/file.tssrc/renderer/src/pages/player/components/PlayerSelector.tsxsrc/renderer/src/services/VideoFormatDetector.tssrc/renderer/src/state/stores/player.store.tssrc/renderer/src/pages/player/components/TranscodeIndicator.tsxsrc/renderer/src/services/CodecCompatibilityChecker.tssrc/renderer/src/services/PlayerSettingsLoader.tssrc/renderer/src/pages/player/components/VideoSurface.tsxsrc/renderer/src/services/SessionService.tssrc/renderer/src/pages/player/PlayerPage.tsx
**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{ts,tsx}: 定制 antd 组件样式优先使用 styled-components 包装 styled(Component),避免全局 classNames
项目的图标统一使用 lucide-react,不使用 emoji 作为图标
组件/Hook 顶层必须通过 useStore(selector) 使用 Zustand,禁止在 useMemo/useEffect 内部调用 store Hook
避免使用返回对象的 Zustand 选择器(如 useStore(s => ({ a: s.a, b: s.b })));应使用单字段选择器或配合 shallow 比较器
遵循 React「副作用与状态更新」规范:渲染纯函数、Effect 三分法、幂等更新、稳定引用、严格清理、禁止写回自身依赖、Provider 值 memo、外部状态 selector 稳定等
统一使用 loggerService 记录日志而不是 console
logger 使用示例中第二个参数必须为对象字面量(如 logger.error('msg', { error }))
任何组件或页面都不要写入 media 元素的 currentTime,播放器控制由编排器统一负责
在 styled-components 中:主题相关属性使用 AntD CSS 变量(如 var(--ant-color-bg-elevated));
在 styled-components 中:设计系统常量(尺寸、动画、层级、字体、毛玻璃等)使用 JS 常量(如 SPACING、BORDER_RADIUS、Z_INDEX、FONT_SIZES 等)
Files:
src/renderer/src/services/index.tssrc/renderer/src/pages/player/components/index.tssrc/renderer/src/pages/player/components/VideoErrorRecovery.tsxsrc/renderer/src/pages/player/components/HLSVideoSurface.tsxsrc/renderer/src/pages/player/components/HLSPlayer.tsxsrc/renderer/src/utils/file.tssrc/renderer/src/pages/player/components/PlayerSelector.tsxsrc/renderer/src/services/VideoFormatDetector.tssrc/renderer/src/state/stores/player.store.tssrc/renderer/src/pages/player/components/TranscodeIndicator.tsxsrc/renderer/src/services/CodecCompatibilityChecker.tssrc/renderer/src/services/PlayerSettingsLoader.tssrc/renderer/src/pages/player/components/VideoSurface.tsxsrc/renderer/src/services/SessionService.tssrc/renderer/src/pages/player/PlayerPage.tsx
**/player/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Player 页面:统一在组件顶层使用 Zustand selector,禁止在 useMemo/useEffect 内调用 store Hook;useSubtitleEngine 通过参数传入 subtitles 等防御处理
Files:
src/renderer/src/pages/player/components/index.tssrc/renderer/src/pages/player/components/VideoErrorRecovery.tsxsrc/renderer/src/pages/player/components/HLSVideoSurface.tsxsrc/renderer/src/pages/player/components/HLSPlayer.tsxsrc/renderer/src/pages/player/components/PlayerSelector.tsxsrc/renderer/src/pages/player/components/TranscodeIndicator.tsxsrc/renderer/src/pages/player/components/VideoSurface.tsxsrc/renderer/src/pages/player/PlayerPage.tsx
.taskmaster/tasks/tasks.json
📄 CodeRabbit inference engine (AGENTS.md)
.taskmaster/tasks/tasks.json: Never manually edit .taskmaster/tasks/tasks.json; use Task Master CLI commands to modify tasks
If tasks.json is changed manually, runtask-master generateto regenerate task files
Files:
.taskmaster/tasks/tasks.json
**/{stores,infrastructure,persistence}/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Zustand 状态管理按三层架构组织:stores、infrastructure、persistence
Files:
src/renderer/src/state/stores/player.store.ts
🧠 Learnings (5)
📚 Learning: 2025-09-17T14:59:36.985Z
Learnt from: CR
PR: mkdir700/EchoPlayer#0
File: CLAUDE.md:0-0
Timestamp: 2025-09-17T14:59:36.985Z
Learning: Applies to **/DictionaryPopover.tsx : DictionaryPopover 组件已用 Ant Design CSS 变量实现主题化(文本、背景、边框、滚动条、交互状态);不得恢复硬编码深色主题样式
Applied to files:
CLAUDE.md
📚 Learning: 2025-09-17T14:59:36.985Z
Learnt from: CR
PR: mkdir700/EchoPlayer#0
File: CLAUDE.md:0-0
Timestamp: 2025-09-17T14:59:36.985Z
Learning: Applies to **/*.{ts,tsx} : 在 styled-components 中:设计系统常量(尺寸、动画、层级、字体、毛玻璃等)使用 JS 常量(如 SPACING、BORDER_RADIUS、Z_INDEX、FONT_SIZES 等)
Applied to files:
src/renderer/src/pages/player/components/VideoErrorRecovery.tsx
📚 Learning: 2025-09-07T02:13:46.142Z
Learnt from: CR
PR: mkdir700/EchoPlayer#0
File: AGENTS.md:0-0
Timestamp: 2025-09-07T02:13:46.142Z
Learning: Applies to .taskmaster/tasks/tasks.json : Never manually edit .taskmaster/tasks/tasks.json; use Task Master CLI commands to modify tasks
Applied to files:
.taskmaster/tasks/tasks.json
📚 Learning: 2025-09-17T14:59:36.985Z
Learnt from: CR
PR: mkdir700/EchoPlayer#0
File: CLAUDE.md:0-0
Timestamp: 2025-09-17T14:59:36.985Z
Learning: Applies to **/player/**/*.{ts,tsx} : Player 页面:统一在组件顶层使用 Zustand selector,禁止在 useMemo/useEffect 内调用 store Hook;useSubtitleEngine 通过参数传入 subtitles 等防御处理
Applied to files:
src/renderer/src/pages/player/components/PlayerSelector.tsxsrc/renderer/src/state/stores/player.store.ts
📚 Learning: 2025-09-17T14:59:36.985Z
Learnt from: CR
PR: mkdir700/EchoPlayer#0
File: CLAUDE.md:0-0
Timestamp: 2025-09-17T14:59:36.985Z
Learning: Applies to **/*.{ts,tsx} : 任何组件或页面都不要写入 media 元素的 currentTime,播放器控制由编排器统一负责
Applied to files:
src/renderer/src/pages/player/components/VideoSurface.tsx
🧬 Code graph analysis (11)
src/renderer/src/pages/player/components/VideoErrorRecovery.tsx (1)
src/renderer/src/services/CodecCompatibilityChecker.ts (1)
ExtendedErrorType(28-39)
src/renderer/src/pages/player/components/HLSVideoSurface.tsx (4)
src/renderer/src/services/Logger.ts (2)
loggerService(817-817)error(422-424)src/renderer/src/services/CodecCompatibilityChecker.ts (1)
ExtendedErrorType(28-39)src/renderer/src/pages/player/hooks/usePlayerCommands.ts (1)
usePlayerCommands(18-305)src/renderer/src/pages/player/components/AutoResumeCountdown.tsx (1)
AutoResumeCountdown(22-137)
src/renderer/src/pages/player/components/HLSPlayer.tsx (3)
src/renderer/src/services/Logger.ts (2)
loggerService(817-817)error(422-424)src/renderer/src/services/CodecCompatibilityChecker.ts (1)
ExtendedErrorType(28-39)src/renderer/src/state/stores/player.store.ts (1)
usePlayerStore(583-585)
src/renderer/src/utils/file.ts (2)
src/renderer/src/services/Logger.ts (1)
loggerService(817-817)src/main/constant.ts (1)
isWin(2-2)
src/renderer/src/pages/player/components/PlayerSelector.tsx (3)
src/renderer/src/services/Logger.ts (2)
loggerService(817-817)error(422-424)src/renderer/src/services/CodecCompatibilityChecker.ts (1)
ExtendedErrorType(28-39)src/renderer/src/state/stores/player.store.ts (1)
usePlayerStore(583-585)
src/renderer/src/services/VideoFormatDetector.ts (2)
src/renderer/src/services/Logger.ts (2)
loggerService(817-817)error(422-424)src/renderer/src/services/CodecCompatibilityChecker.ts (3)
CodecCompatibilityResult(9-23)ExtendedErrorType(28-39)CodecCompatibilityChecker(49-303)
src/renderer/src/pages/player/components/TranscodeIndicator.tsx (2)
src/renderer/src/services/Logger.ts (1)
loggerService(817-817)src/renderer/src/state/stores/player.store.ts (1)
usePlayerStore(583-585)
src/renderer/src/services/CodecCompatibilityChecker.ts (1)
src/renderer/src/services/Logger.ts (2)
loggerService(817-817)error(422-424)
src/renderer/src/pages/player/components/VideoSurface.tsx (2)
src/renderer/src/services/CodecCompatibilityChecker.ts (1)
ExtendedErrorType(28-39)src/renderer/src/pages/player/hooks/usePlayerEngine.ts (1)
usePlayerEngine(93-295)
src/renderer/src/services/SessionService.ts (1)
src/renderer/src/services/Logger.ts (2)
loggerService(817-817)error(422-424)
src/renderer/src/pages/player/PlayerPage.tsx (2)
src/renderer/src/services/CodecCompatibilityChecker.ts (2)
ExtendedErrorType(28-39)CodecCompatibilityChecker(49-303)src/renderer/src/services/SessionService.ts (1)
SessionService(111-480)
🪛 Biome (2.1.2)
src/renderer/src/pages/player/components/HLSVideoSurface.tsx
[error] 52-52: The elements with this role can be changed to the following elements:
For examples and more information, see WAI-ARIA Roles
(lint/a11y/useSemanticElements)
src/renderer/src/pages/player/components/VideoSurface.tsx
[error] 158-158: The elements with this role can be changed to the following elements:
For examples and more information, see WAI-ARIA Roles
(lint/a11y/useSemanticElements)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
- GitHub Check: test (ubuntu-latest, 20)
- GitHub Check: test (windows-latest, 20)
- GitHub Check: test (macos-latest, 20)
🔇 Additional comments (12)
package.json (1)
81-81: 请验证 ReactPlayer 主版本升级的兼容性
react-player从 2.x 升到 3.3.2 属于破坏性升级,官方发布说明提到默认导出、懒加载入口与配置项有调整。请确认代码中ReactPlayer的导入方式、属性命名及懒加载逻辑已与 3.x 对齐,并补充对应的回归测试/手测记录。若需要,我可以协助整理 3.x 的关键变更点。.coderabbit.yaml (1)
2-2: 语言设置更新符合配置架构
zh-CN在配置枚举中合法,调整后可按预期启用中文审查流程。src/renderer/src/utils/file.ts (1)
82-118: 函数实现正确,边界情况处理完善。
fileUrlToPath函数的实现逻辑清晰合理:
- 正确处理空字符串、HTTP/HTTPS URL、本地路径等多种输入场景
- Windows 平台的盘符路径处理(移除前导斜杠)符合预期
- URL 解码的错误处理避免了异常中断
.taskmaster/tasks/tasks.json (1)
1-520: 避免手动编辑 tasks.json,应使用 Task Master CLI。根据项目编码规范和学习记录,
.taskmaster/tasks/tasks.json不应手动编辑。请使用 Task Master CLI 命令来修改任务定义,完成后运行task-master generate重新生成任务文件。基于学习记录(Based on learnings)
如果这些任务定义是通过 CLI 工具生成的,请忽略此评论。否则,建议通过以下步骤修正:
- 使用适当的 Task Master CLI 命令添加/修改任务
- 运行
task-master generate重新生成任务文件src/renderer/src/services/CodecCompatibilityChecker.ts (2)
145-226: 异步兼容性检查实现完善,错误处理健壮。
checkCompatibility方法的实现:
- 正确使用 MediaParserService 获取编解码器信息
- 详细记录检测过程和结果
- 错误处理完善,提供合理的降级行为(返回需要转码的结果)
- 日志级别使用恰当(debug、info、error)
234-269: 错误类型映射逻辑清晰,H.265 特殊处理合理。
getErrorTypeFromCompatibility方法正确区分了不同的不兼容场景,对 H.265/HEVC 的特殊识别(第 251-258 行)有助于提供更精确的错误提示。src/renderer/src/pages/player/components/index.ts (1)
6-12: 新组件导出正确,与转码功能集成良好。添加的
PlayerSelector和TranscodeIndicator导出符合项目结构,支持新的转码和 HLS 播放流程。注意:AI 摘要中提到VideoSurface被移除,但实际上第 14 行仍保留了该导出,这与实际代码不一致。.taskmaster/docs/prd.txt (1)
1-3: 文档清晰描述了转码架构和 HLS 回退策略。文档准确说明了 JIT 转码后端的集成和运行时格式回退机制,与 PR 中的其他实现变更保持一致。
src/renderer/src/services/index.ts (1)
2-7: 服务模块导出扩展正确,支持新的转码功能。新增的
CodecCompatibilityChecker、SessionService和VideoFormatDetector导出与 PR 中引入的服务模块对应,公共 API 扩展合理。src/renderer/src/pages/player/components/TranscodeIndicator.tsx (2)
20-21: Zustand 选择器使用正确,遵循项目规范。在组件顶层使用单字段选择器
usePlayerStore((s) => s.transcodeInfo)和usePlayerStore((s) => s.hlsMode),符合项目的状态管理最佳实践,避免了无限重渲染问题。依据编码规范(As per coding guidelines)
137-217: 样式实现优秀,动画流畅,主题兼容性良好。Styled-components 实现:
- 正确使用 AntD CSS 变量(
var(--ant-color-bg-elevated)、var(--ant-box-shadow-secondary)等)- 动画实现流畅(fadeInOut、spin)
- 响应式设计适配移动端
- 层级管理合理(z-index: 1000)
依据编码规范(As per coding guidelines)
src/renderer/src/pages/player/components/HLSVideoSurface.tsx (1)
24-61: 组件实现完善,与现有播放器架构无缝集成。
HLSVideoSurface组件设计良好:
- 正确组合
HLSPlayer、SubtitleOverlay和AutoResumeCountdown- 错误处理和事件转发实现正确
- 接口与原
VideoSurface兼容- 使用
usePlayerCommandshook 符合项目规范
| onPlay={() => mediaEventHandlers.onPlay()} | ||
| onPause={() => mediaEventHandlers.onPause()} | ||
| onTimeUpdate={(e) => mediaEventHandlers.onTimeUpdate(e.nativeEvent)} | ||
| onSeeking={() => mediaEventHandlers.onSeeking()} | ||
| onSeeked={(e) => mediaEventHandlers.onSeeked(e.nativeEvent)} | ||
| onDurationChange={(e) => mediaEventHandlers.onDurationChange(e.nativeEvent)} | ||
| onRateChange={(e) => mediaEventHandlers.onRateChange(e.nativeEvent)} | ||
| onLoadedMetadata={handleLoadedMetadata} | ||
| onError={handleError} | ||
| onEnded={handleEnded} | ||
| controlsList="nodownload" |
There was a problem hiding this comment.
补发 onEnded 事件给 orchestrator
当前 onEnded 仅调用 pause,未通过 mediaEventHandlers.onEnded 通知 orchestrator,导致编排器无法获知结束事件(如自动重播/状态同步失效)。建议在处理 ended 时同步调用 mediaEventHandlers.onEnded(e.nativeEvent),确保事件链完整。
🤖 Prompt for AI Agents
In src/renderer/src/pages/player/components/HLSPlayer.tsx around lines 297-307,
the onEnded handler currently only pauses playback and does not notify the
orchestrator; update the onEnded wiring so the ended event is forwarded to the
orchestrator by calling mediaEventHandlers.onEnded with the native event (e.g.,
call mediaEventHandlers.onEnded(e.nativeEvent) from the onEnded prop or have
handleEnded accept the event and invoke mediaEventHandlers.onEnded before/after
pause), ensuring the orchestrator receives the end-of-media event for
replay/state sync.
| <Surface | ||
| ref={surfaceRef} | ||
| role="button" | ||
| data-testid="hls-video-surface" | ||
| onClick={handleSurfaceClick} | ||
| tabIndex={0} | ||
| > |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
可访问性实现合理,静态分析提示可忽略。
第 47 行使用 role="button" 配合 onClick 和 tabIndex={0} 使视频容器可通过键盘访问,这对于视频播放控制是合理的设计。静态分析工具建议使用 <button> 元素,但这在语义上不适合作为视频容器。
当前实现的可访问性已足够,但如果需要进一步改进,可以考虑:
- 添加
aria-label描述用途 - 添加键盘事件处理(Enter/Space 键触发播放/暂停)
可选改进:
<Surface
ref={surfaceRef}
role="button"
+ aria-label="点击播放或暂停视频"
data-testid="hls-video-surface"
onClick={handleSurfaceClick}
+ onKeyDown={(e) => {
+ if (e.key === 'Enter' || e.key === ' ') {
+ e.preventDefault()
+ handleSurfaceClick()
+ }
+ }}
tabIndex={0}
>📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| <Surface | |
| ref={surfaceRef} | |
| role="button" | |
| data-testid="hls-video-surface" | |
| onClick={handleSurfaceClick} | |
| tabIndex={0} | |
| > | |
| <Surface | |
| ref={surfaceRef} | |
| role="button" | |
| aria-label="点击播放或暂停视频" | |
| data-testid="hls-video-surface" | |
| onClick={handleSurfaceClick} | |
| onKeyDown={(e) => { | |
| if (e.key === 'Enter' || e.key === ' ') { | |
| e.preventDefault() | |
| handleSurfaceClick() | |
| } | |
| }} | |
| tabIndex={0} | |
| > |
🤖 Prompt for AI Agents
In src/renderer/src/pages/player/components/HLSVideoSurface.tsx around lines 45
to 51, the Surface uses role="button", onClick and tabIndex={0} which is
acceptable but should also expose an accessible name and keyboard activation:
add an aria-label describing the control (e.g., "Play/Pause video" or a
prop-driven label) and add an onKeyDown handler that listens for Enter and Space
and calls the same handler as onClick (preventDefault for Space to avoid
scrolling); keep role and tabIndex as-is so screen readers and keyboard users
can focus and interact.
| ref={handleVideoRef} | ||
| src={src} | ||
| onTimeUpdate={(e) => mediaEventHandlers.onTimeUpdate(e.nativeEvent)} | ||
| onPlay={() => mediaEventHandlers.onPlay()} | ||
| onPause={() => mediaEventHandlers.onPause()} | ||
| onEnded={() => { | ||
| mediaEventHandlers.onEnded() | ||
| handleEnded() | ||
| }} | ||
| onTimeUpdate={(e) => mediaEventHandlers.onTimeUpdate(e.nativeEvent)} | ||
| onSeeking={() => mediaEventHandlers.onSeeking()} | ||
| onSeeked={(e) => mediaEventHandlers.onSeeked(e.nativeEvent)} | ||
| onDurationChange={(e) => mediaEventHandlers.onDurationChange(e.nativeEvent)} | ||
| onRateChange={(e) => mediaEventHandlers.onRateChange(e.nativeEvent)} | ||
| onLoadedMetadata={handleLoadedMetadata} | ||
| onError={handleVideoError} | ||
| onEnded={handleEnded} | ||
| controlsList="nodownload" | ||
| disablePictureInPicture={false} | ||
| preload="metadata" | ||
| autoPlay={false} // 明确禁用自动播放 | ||
| autoPlay={false} | ||
| playsInline | ||
| // 添加更多有用的事件处理 | ||
| // onCanPlay={() => { | ||
| // logger.debug('视频可以开始播放') | ||
| // }} | ||
| // onWaiting={() => { | ||
| // logger.debug('视频缓冲中') | ||
| // }} | ||
| // onStalled={() => { | ||
| // logger.warn('视频数据停滞') | ||
| // }} | ||
| /> |
There was a problem hiding this comment.
补发 onEnded 事件给 orchestrator
此处 onEnded 只调用 handleEnded,未把事件交给 mediaEventHandlers.onEnded,编排器将无法收到结束通知。请在 onEnded 里同时调用 mediaEventHandlers.onEnded(e.nativeEvent) 以保持全局状态同步。
🧰 Tools
🪛 Biome (2.1.2)
[error] 158-158: The elements with this role can be changed to the following elements:
For examples and more information, see WAI-ARIA Roles
(lint/a11y/useSemanticElements)
🤖 Prompt for AI Agents
In src/renderer/src/pages/player/components/VideoSurface.tsx around lines 151 to
167, onEnded currently only calls handleEnded and therefore never notifies the
orchestrator; update the onEnded handling so it forwards the native event to
mediaEventHandlers.onEnded(e.nativeEvent) as well (either by changing the JSX to
onEnded={(e) => { handleEnded(e); mediaEventHandlers.onEnded(e.nativeEvent); }}
or by having handleEnded accept the event and call
mediaEventHandlers.onEnded(e.nativeEvent) internally) to ensure the orchestrator
receives the end notification.
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
There was a problem hiding this comment.
Actionable comments posted: 2
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
📒 Files selected for processing (1)
src/renderer/src/pages/player/components/TranscodeIndicator.tsx(1 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
src/renderer/src/**/*.{ts,tsx,scss,css}
📄 CodeRabbit inference engine (CLAUDE.md)
优先使用 CSS 变量,避免硬编码样式值(颜色等)
Files:
src/renderer/src/pages/player/components/TranscodeIndicator.tsx
src/renderer/src/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
尺寸与时长等不要硬编码,优先使用 useTheme() 的 token 或集中样式变量(如 motionDurationMid、borderRadiusSM/MD)
Files:
src/renderer/src/pages/player/components/TranscodeIndicator.tsx
**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{ts,tsx}: 定制 antd 组件样式优先使用 styled-components 包装 styled(Component),避免全局 classNames
项目的图标统一使用 lucide-react,不使用 emoji 作为图标
组件/Hook 顶层必须通过 useStore(selector) 使用 Zustand,禁止在 useMemo/useEffect 内部调用 store Hook
避免使用返回对象的 Zustand 选择器(如 useStore(s => ({ a: s.a, b: s.b })));应使用单字段选择器或配合 shallow 比较器
遵循 React「副作用与状态更新」规范:渲染纯函数、Effect 三分法、幂等更新、稳定引用、严格清理、禁止写回自身依赖、Provider 值 memo、外部状态 selector 稳定等
统一使用 loggerService 记录日志而不是 console
logger 使用示例中第二个参数必须为对象字面量(如 logger.error('msg', { error }))
任何组件或页面都不要写入 media 元素的 currentTime,播放器控制由编排器统一负责
在 styled-components 中:主题相关属性使用 AntD CSS 变量(如 var(--ant-color-bg-elevated));
在 styled-components 中:设计系统常量(尺寸、动画、层级、字体、毛玻璃等)使用 JS 常量(如 SPACING、BORDER_RADIUS、Z_INDEX、FONT_SIZES 等)
Files:
src/renderer/src/pages/player/components/TranscodeIndicator.tsx
**/player/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Player 页面:统一在组件顶层使用 Zustand selector,禁止在 useMemo/useEffect 内调用 store Hook;useSubtitleEngine 通过参数传入 subtitles 等防御处理
Files:
src/renderer/src/pages/player/components/TranscodeIndicator.tsx
🧬 Code graph analysis (1)
src/renderer/src/pages/player/components/TranscodeIndicator.tsx (2)
src/renderer/src/services/Logger.ts (1)
loggerService(817-817)src/renderer/src/state/stores/player.store.ts (1)
usePlayerStore(583-585)
🪛 Biome (2.1.2)
src/renderer/src/pages/player/components/TranscodeIndicator.tsx
[error] 131-131: This hook is being called conditionally, but all hooks must be called in the exact same order in every component render.
Hooks should not be called after an early return.
For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level
(lint/correctness/useHookAtTopLevel)
🪛 GitHub Actions: Test
src/renderer/src/pages/player/components/TranscodeIndicator.tsx
[error] 118-118: ESLint: React Hook 'useEffect' is called conditionally. React Hooks must be called in the same order on every render. (react-hooks/rules-of-hooks)
🪛 GitHub Check: test (ubuntu-latest, 20)
src/renderer/src/pages/player/components/TranscodeIndicator.tsx
[failure] 118-118:
React Hook "useEffect" is called conditionally. React Hooks must be called in the exact same order in every component render. Did you accidentally call a React Hook after an early return?
src/renderer/src/pages/player/components/TranscodeIndicator.tsx
Outdated
Show resolved
Hide resolved
src/renderer/src/pages/player/components/TranscodeIndicator.tsx
Outdated
Show resolved
Hide resolved
…-driven architecture Replace hardcoded codec support lists with elegant CodecRegistry system: - Introduce CodecRule interface for declarative codec configuration - Create CodecRegistry with centralized video/audio codec rules - Remove duplicate detection logic with unified checkCodecSupportByRule method - Add user-friendly codec display names (e.g., "H.265/HEVC" instead of "hevc") - Remove h265-unsupported error type (now handled generically) - Mark H.265 as supported (via HLS transcoding) instead of unsupported MediaParserService improvements: - Fix codec parsing to prioritize top-level fields (videoCodec, audioCodec) - Add fallback to tracks array for codec information - Add comprehensive debug logging for Remotion result structure - Return null when no valid codec detected to trigger FFmpeg fallback Benefits: - New codecs can be added via configuration without code changes - Eliminates hardcoded Set collections and scattered string matching - Better maintainability and extensibility - Improved error messages with friendly codec names - Cleaner architecture with single responsibility principle This refactoring addresses the limitation where codec support logic was scattered across hardcoded Sets and duplicate checking methods, making it difficult to maintain and extend when adding new codec support. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
…dling - Remove redundant debug logs in HLS error event handler - Remove bufferStalledError special handling and recovery strategies - Remove render-time side-effect logs from PlayerSelector component - Simplify non-fatal HLS error logging to essential information only Changes: - HLSPlayer: Remove detailed HLS error debug logs and buffer stall recovery logic - PlayerSelector: Remove debug logs in render path to follow React pure render principles This refactoring reduces console noise during HLS playback and aligns with React best practices by eliminating side effects from component render functions 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
问题描述: - PlayerPage 在创建转码会话后没有在组件卸载时释放 - 导致后端持续保留会话,长时间使用会造成资源泄漏 修复内容: - 添加 sessionIdRef 保存会话 ID - 在组件清理时调用 SessionService.deleteSession 释放会话 - 添加完整的错误处理和日志记录 - 确保 ref 在清理后重置为 null 影响范围: - PlayerPage.tsx 组件清理逻辑 - 转码会话生命周期管理 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
- Move @types/hls.js from dependencies to devDependencies section - Type definitions are only needed during development and build time - Reduces production bundle size by excluding unnecessary type packages This change aligns with TypeScript best practices where @types/* packages should be in devDependencies as they are not required at runtime. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
- Add resetTranscodeInfo() at the start of loadData to clear previous HLS state - Add resetTranscodeInfo() in component cleanup to ensure state reset on unmount - Prevent PlayerSelector from rendering old HLS player for new non-HLS videos The player store could retain hlsMode and stale hlsSrc from previous HLS sessions, causing PlayerSelector to incorrectly render the HLS player for new non-HLS videos. This fix ensures all transcode state is cleared before loading new videos and on component unmount, preventing cross-session state pollution. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Remove the TranscodeIndicator UI component as transcode failures should be handled through the default error recovery flow via VideoErrorRecovery component. Changes: - Delete TranscodeIndicator.tsx component file - Remove TranscodeIndicator from PlayerPage imports and JSX - Remove TranscodeIndicator export from components/index.ts 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
- Implement universal VideoStatusIndicator component with framer-motion animations * Support multiple status types: loading, volume, custom * Configurable card display (loading type shows icon-only by default) * Smooth fade-in/fade-out and scale transitions with 0.2s duration - Add TranscodeLoadingIndicator for HLS transcoding wait states * Display loading spinner during seeking to untranscoded segments * Monitor transcoding, seeking, and waiting states via player store * Prevent user confusion during transcode buffer periods - Add VolumeIndicator example component (ready for future integration) * Auto-hide after 1s delay on volume changes * Dynamic icon selection based on volume level (0-100) - Extend player state management with video loading states * Add isVideoSeeking and isVideoWaiting to player store * Wire onWaiting/onCanPlay events through PlayerOrchestrator * Connect video element events to state updater - Integrate with HLSPlayer and HLSVideoSurface components * Listen to waiting/canPlay events in HLSPlayer * Mount TranscodeLoadingIndicator in HLSVideoSurface Changes: - VideoStatusIndicator: Universal status overlay with glass morphism effects - TranscodeLoadingIndicator: Specialized loading state for HLS transcoding - VolumeIndicator: Example implementation for volume feedback - player.store: Add isVideoSeeking, isVideoWaiting state fields - PlayerOrchestrator: Add onWaiting/onCanPlay event handlers - usePlayerEngine: Wire waiting/canPlay events to state management - HLSPlayer: Listen to video waiting/canPlay events - HLSVideoSurface: Integrate TranscodeLoadingIndicator component This enhancement provides clear visual feedback during HLS transcoding operations, preventing users from thinking the application has frozen when seeking to untranscoded video segments. The universal indicator design enables future extensions for volume, playback speed, and other status displays. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Add isVideoSeeking and isVideoWaiting properties to PlayerState objects in PlayerSettingsLoader to fix TypeScript compilation errors. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 17
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (9)
src/renderer/src/pages/player/engine/PlayerOrchestrator.ts (4)
495-504: 播放速度上限与 store 不一致(4 vs 3),会造成状态不同步这里将播放速度夹紧到 4,而 player.store.ts 的 setPlaybackRate 会夹紧到 3。导致视频元素为 3.5x,而 Store 为 3x,UI 同步出现偏差。请统一上限(建议 3.0),或提取共享常量。
- const clampedRate = Math.max(0.25, Math.min(4, rate)) + const clampedRate = Math.max(0.25, Math.min(3, rate))(更佳做法:抽取 PLAYBACK_RATE_MAX 常量并在引擎与 store 共享。)
1009-1039: 未处理 HTMLMediaElement.play() 返回的 Promise,可能产生未捕获拒绝play() 会返回 Promise,若被浏览器策略拒绝(自动播放限制)将产生未捕获的 rejected promise。当前 try/catch 捕获不到。建议显式捕获。
case 'play': - // 纯副作用:调用视频控制器 - this.videoController?.play() + // 纯副作用:调用视频控制器,并捕获可能的拒绝 + void this.videoController?.play()?.catch((err) => { + logger.warn('media.play() 被拒绝/失败', { error: err }) + }) break case 'seek': // 纯副作用:执行跳转 this.videoController?.seek(effect.payload.to) if (effect.payload.followUpPlay) { // 纯副作用:调度延迟播放 this.clockScheduler.scheduleAfter( 80, - () => this.videoController?.play(), + () => + void this.videoController?.play()?.catch((err) => + logger.warn('seek 后 followUpPlay 失败', { error: err }) + ), 'followUpPlay after seek' ) } break
1341-1369: 定时动作里的 play() 同样需要捕获 Promise 拒绝与 execute 阶段一致,这里的 play() 也应捕获 Promise 拒绝,避免未处理的异步错误。
case 'seek': if (action.params?.to !== undefined) { this.videoController?.seek(action.params.to) if (action.params.followUpPlay) { // 纯副作用:调度后续动作 this.clockScheduler.scheduleAfter( 100, - () => { - this.videoController?.play() - logger.debug('跳转后自动继续播放') - }, + () => { + void this.videoController?.play()?.catch((err) => + logger.warn('scheduled seek 后自动播放失败', { error: err }) + ) + logger.debug('跳转后自动继续播放') + }, 'followUpPlay after scheduled seek' ) } } break
183-183: logger 第二参数请使用对象字面量包装,保持结构化日志一致性为符合项目规范,建议改为传递对象字面量。
- logger.debug('PlayerOrchestrator initialized', this.config) + logger.debug('PlayerOrchestrator initialized', { config: this.config })As per coding guidelines
src/renderer/src/pages/player/PlayerPage.tsx (2)
86-86: Zustand 用法不符合规范:避免无选择器整店订阅请改为单字段选择器,减少重渲染并符合项目规范。
As per coding guidelines
- const { subtitlePanelVisible, toggleSubtitlePanel } = usePlayerStore() + const subtitlePanelVisible = usePlayerStore((s) => s.subtitlePanelVisible) + const toggleSubtitlePanel = usePlayerStore((s) => s.toggleSubtitlePanel)
634-641: 样式颜色建议改为 CSS 变量,避免硬编码
background: #000等硬编码颜色建议替换为 CSS 变量(带合理 fallback),与全局主题保持一致。As per coding guidelines
- background: #000; + background: var(--player-video-bg, #000);(同理可评估 BackButton 的 #fff/#1677ff 等。)
src/main/services/MediaParserService.ts (3)
214-224: FFmpeg 调用应使用已转换的本地路径策略方法中已通过
PathConverter.convertToLocalPath转为pathResult.localPath,但 FFmpeg 分支仍传入inputPath,可能包含file://或非本地路径,导致失败。建议修复:
- result = await Promise.race([ - this.ffmpegService.getVideoInfo(inputPath), + result = await Promise.race([ + this.ffmpegService.getVideoInfo(pathResult.localPath), this.createTimeoutPromise<typeof result>(timeoutMs, 'FFmpeg') ])并请确保
FFmpegService.getVideoInfo期望的路径形态与此一致。Based on learnings
355-374: 与上面一致:fallback 分支也应传入本地路径
getVideoInfo中 Remotion 失败后 fallback 到 FFmpeg 仍传inputPath,而非localInputPath。建议修复:
- const ffmpegVideoInfo = await this.ffmpegService.getVideoInfo(inputPath) + const ffmpegVideoInfo = await this.ffmpegService.getVideoInfo(localInputPath)
22-53: 路径转换实现重复,可收敛到 PathConverter
convertFileUrlToLocalPath与PathConverter.convertToLocalPath职责重叠,建议统一使用后者并移除前者,减少分叉逻辑与维护成本。Also applies to: 288-298
♻️ Duplicate comments (5)
src/renderer/src/pages/player/PlayerPage.tsx (1)
116-118: 已在加载前清理旧的 HLS 状态,避免残留污染新视频加载这正是之前建议的修复点,赞。
src/renderer/src/services/CodecCompatibilityChecker.ts (1)
285-338: 避免用静态 supported 短路,优先按规则的 mimeTypes 做 canPlayType 动态检测当前实现一旦命中规则即直接返回 rule.supported,会导致在部分浏览器上将 HEVC/Theora 等实际不支持的编解码器误判为“支持”,从而跳过转码,最终播放失败。建议:命中规则时优先用该规则提供的 mimeTypes 逐一用 canPlayType 检测;仅当无法判定时再退回到 rule.supported 作为保守默认。这样同时解决不同浏览器差异问题,也与之前评论所建议的“运行时检测优先级更高”一致。基于此,请修改如下:
private static checkCodecSupportByRule( codec: string, rule: CodecRule | undefined, elementType: 'video' | 'audio' ): boolean { - // 如果找到规则,直接返回配置的支持状态 - if (rule) { - logger.debug(`找到编解码器规则: ${rule.displayName}`, { - codec, - supported: rule.supported, - category: rule.category - }) - return rule.supported - } + // 如果找到规则,优先使用规则提供的 MIME 进行 canPlayType 动态检测 + if (rule) { + logger.debug(`找到编解码器规则: ${rule.displayName}`, { + codec, + category: rule.category + }) + const element = document.createElement(elementType) + const detected = rule.mimeTypes.some((type) => { + const support = element.canPlayType(type) + const ok = support === 'probably' || support === 'maybe' + if (ok) { + logger.debug(`编解码器检测成功: ${type}`, { support }) + } + return ok + }) + if (detected) { + logger.debug(`按规则动态检测通过: ${rule.displayName}`, { codec }) + return true + } + // 无法通过 canPlayType 判定时,回退到规则默认值(更保守) + if (rule.supported === false) { + logger.debug(`规则标记为不支持,判定不支持: ${rule.displayName}`) + return false + } + logger.debug(`规则未能判定支持性,继续使用通用动态检测: ${rule.displayName}`) + // 继续走下方的“未知编解码器”分支进行通用检测 + } // 未知编解码器,使用浏览器 canPlayType API 动态检测 logger.debug(`未知编解码器,使用动态检测: ${codec}`, { elementType }) const element = document.createElement(elementType) const normalizedCodec = codec.toLowerCase().trim() // 构建默认 MIME 类型进行检测 const testTypes = elementType === 'video' ? [ `video/mp4; codecs="${codec}"`, `video/mp4; codecs="${normalizedCodec}"`, `video/webm; codecs="${codec}"` ] : [ `audio/mp4; codecs="${codec}"`, `audio/mp4; codecs="${normalizedCodec}"`, `audio/webm; codecs="${codec}"`, `audio/mpeg; codecs="${codec}"` ] const isSupported = testTypes.some((type) => { const support = element.canPlayType(type) const result = support === 'probably' || support === 'maybe' if (result) { logger.debug(`编解码器检测成功: ${type}`, { support }) } return result }) logger.debug(`动态检测结果: ${codec}`, { elementType, supported: isSupported }) return isSupported }src/renderer/src/pages/player/components/HLSPlayer.tsx (2)
270-277: onEnded 需转发给 orchestrator当前仅调用 pause(),未通知 orchestrator,导致编排器无法感知结束事件(自动重播/状态同步异常)。请将 ended 事件转发给 mediaEventHandlers。
- onEnded={handleEnded} + onEnded={(e) => { + mediaEventHandlers.onEnded(e.nativeEvent) + handleEnded() + }}(依据播放器架构规范)
290-305: 用主题 CSS 变量替代硬编码颜色 #000遵循 renderer 样式规范,避免硬编码,改用 AntD/设计系统变量,提供 #000 兜底。
(依据样式规范)
const PlayerContainer = styled.div` @@ - background: #000; + background: var(--ant-color-bg-elevated, #000); overflow: hidden; ` const StyledVideo = styled.video` @@ - background: #000; + background: var(--ant-color-bg-elevated, #000); `src/renderer/src/pages/player/components/HLSVideoSurface.tsx (1)
45-52: 为可点击容器补充可访问性:添加 aria-label 与键盘激活保持 div+role="button" 可行,但应提供无障碍名称与 Enter/Space 激活,兼容静态分析提示。
<Surface ref={surfaceRef} role="button" + aria-label="点击播放或暂停视频" data-testid="hls-video-surface" onClick={handleSurfaceClick} + onKeyDown={(e) => { + if (e.key === 'Enter' || e.key === ' ') { + e.preventDefault() + handleSurfaceClick() + } + }} tabIndex={0} >
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
📒 Files selected for processing (14)
package.json(2 hunks)src/main/services/MediaParserService.ts(2 hunks)src/renderer/src/pages/player/PlayerPage.tsx(8 hunks)src/renderer/src/pages/player/components/HLSPlayer.tsx(1 hunks)src/renderer/src/pages/player/components/HLSVideoSurface.tsx(1 hunks)src/renderer/src/pages/player/components/PlayerSelector.tsx(1 hunks)src/renderer/src/pages/player/components/TranscodeLoadingIndicator.tsx(1 hunks)src/renderer/src/pages/player/components/VideoStatusIndicator.tsx(1 hunks)src/renderer/src/pages/player/components/VolumeIndicator.tsx(1 hunks)src/renderer/src/pages/player/components/index.ts(1 hunks)src/renderer/src/pages/player/engine/PlayerOrchestrator.ts(2 hunks)src/renderer/src/pages/player/hooks/usePlayerEngine.ts(2 hunks)src/renderer/src/services/CodecCompatibilityChecker.ts(1 hunks)src/renderer/src/state/stores/player.store.ts(5 hunks)
🧰 Additional context used
📓 Path-based instructions (5)
src/renderer/src/**/*.{ts,tsx,scss,css}
📄 CodeRabbit inference engine (CLAUDE.md)
优先使用 CSS 变量,避免硬编码样式值(颜色等)
Files:
src/renderer/src/services/CodecCompatibilityChecker.tssrc/renderer/src/pages/player/PlayerPage.tsxsrc/renderer/src/pages/player/components/index.tssrc/renderer/src/pages/player/components/TranscodeLoadingIndicator.tsxsrc/renderer/src/pages/player/engine/PlayerOrchestrator.tssrc/renderer/src/pages/player/components/HLSPlayer.tsxsrc/renderer/src/pages/player/components/VideoStatusIndicator.tsxsrc/renderer/src/pages/player/components/HLSVideoSurface.tsxsrc/renderer/src/state/stores/player.store.tssrc/renderer/src/pages/player/hooks/usePlayerEngine.tssrc/renderer/src/pages/player/components/VolumeIndicator.tsxsrc/renderer/src/pages/player/components/PlayerSelector.tsx
src/renderer/src/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
尺寸与时长等不要硬编码,优先使用 useTheme() 的 token 或集中样式变量(如 motionDurationMid、borderRadiusSM/MD)
Files:
src/renderer/src/services/CodecCompatibilityChecker.tssrc/renderer/src/pages/player/PlayerPage.tsxsrc/renderer/src/pages/player/components/index.tssrc/renderer/src/pages/player/components/TranscodeLoadingIndicator.tsxsrc/renderer/src/pages/player/engine/PlayerOrchestrator.tssrc/renderer/src/pages/player/components/HLSPlayer.tsxsrc/renderer/src/pages/player/components/VideoStatusIndicator.tsxsrc/renderer/src/pages/player/components/HLSVideoSurface.tsxsrc/renderer/src/state/stores/player.store.tssrc/renderer/src/pages/player/hooks/usePlayerEngine.tssrc/renderer/src/pages/player/components/VolumeIndicator.tsxsrc/renderer/src/pages/player/components/PlayerSelector.tsx
**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{ts,tsx}: 定制 antd 组件样式优先使用 styled-components 包装 styled(Component),避免全局 classNames
项目的图标统一使用 lucide-react,不使用 emoji 作为图标
组件/Hook 顶层必须通过 useStore(selector) 使用 Zustand,禁止在 useMemo/useEffect 内部调用 store Hook
避免使用返回对象的 Zustand 选择器(如 useStore(s => ({ a: s.a, b: s.b })));应使用单字段选择器或配合 shallow 比较器
遵循 React「副作用与状态更新」规范:渲染纯函数、Effect 三分法、幂等更新、稳定引用、严格清理、禁止写回自身依赖、Provider 值 memo、外部状态 selector 稳定等
统一使用 loggerService 记录日志而不是 console
logger 使用示例中第二个参数必须为对象字面量(如 logger.error('msg', { error }))
任何组件或页面都不要写入 media 元素的 currentTime,播放器控制由编排器统一负责
在 styled-components 中:主题相关属性使用 AntD CSS 变量(如 var(--ant-color-bg-elevated));
在 styled-components 中:设计系统常量(尺寸、动画、层级、字体、毛玻璃等)使用 JS 常量(如 SPACING、BORDER_RADIUS、Z_INDEX、FONT_SIZES 等)
Files:
src/renderer/src/services/CodecCompatibilityChecker.tssrc/renderer/src/pages/player/PlayerPage.tsxsrc/renderer/src/pages/player/components/index.tssrc/renderer/src/pages/player/components/TranscodeLoadingIndicator.tsxsrc/renderer/src/pages/player/engine/PlayerOrchestrator.tssrc/renderer/src/pages/player/components/HLSPlayer.tsxsrc/main/services/MediaParserService.tssrc/renderer/src/pages/player/components/VideoStatusIndicator.tsxsrc/renderer/src/pages/player/components/HLSVideoSurface.tsxsrc/renderer/src/state/stores/player.store.tssrc/renderer/src/pages/player/hooks/usePlayerEngine.tssrc/renderer/src/pages/player/components/VolumeIndicator.tsxsrc/renderer/src/pages/player/components/PlayerSelector.tsx
**/player/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Player 页面:统一在组件顶层使用 Zustand selector,禁止在 useMemo/useEffect 内调用 store Hook;useSubtitleEngine 通过参数传入 subtitles 等防御处理
Files:
src/renderer/src/pages/player/PlayerPage.tsxsrc/renderer/src/pages/player/components/index.tssrc/renderer/src/pages/player/components/TranscodeLoadingIndicator.tsxsrc/renderer/src/pages/player/engine/PlayerOrchestrator.tssrc/renderer/src/pages/player/components/HLSPlayer.tsxsrc/renderer/src/pages/player/components/VideoStatusIndicator.tsxsrc/renderer/src/pages/player/components/HLSVideoSurface.tsxsrc/renderer/src/pages/player/hooks/usePlayerEngine.tssrc/renderer/src/pages/player/components/VolumeIndicator.tsxsrc/renderer/src/pages/player/components/PlayerSelector.tsx
**/{stores,infrastructure,persistence}/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Zustand 状态管理按三层架构组织:stores、infrastructure、persistence
Files:
src/renderer/src/state/stores/player.store.ts
🧠 Learnings (5)
📚 Learning: 2025-09-17T14:59:36.985Z
Learnt from: CR
PR: mkdir700/EchoPlayer#0
File: CLAUDE.md:0-0
Timestamp: 2025-09-17T14:59:36.985Z
Learning: Applies to **/player/**/*.{ts,tsx} : Player 页面:统一在组件顶层使用 Zustand selector,禁止在 useMemo/useEffect 内调用 store Hook;useSubtitleEngine 通过参数传入 subtitles 等防御处理
Applied to files:
src/renderer/src/pages/player/PlayerPage.tsxsrc/renderer/src/state/stores/player.store.tssrc/renderer/src/pages/player/components/PlayerSelector.tsx
📚 Learning: 2025-09-17T14:59:36.985Z
Learnt from: CR
PR: mkdir700/EchoPlayer#0
File: CLAUDE.md:0-0
Timestamp: 2025-09-17T14:59:36.985Z
Learning: Applies to **/*.{ts,tsx} : 任何组件或页面都不要写入 media 元素的 currentTime,播放器控制由编排器统一负责
Applied to files:
src/renderer/src/pages/player/components/HLSPlayer.tsx
📚 Learning: 2025-09-17T14:59:36.985Z
Learnt from: CR
PR: mkdir700/EchoPlayer#0
File: CLAUDE.md:0-0
Timestamp: 2025-09-17T14:59:36.985Z
Learning: Applies to src/renderer/src/**/*.{ts,tsx,scss,css} : 优先使用 CSS 变量,避免硬编码样式值(颜色等)
Applied to files:
src/renderer/src/pages/player/components/HLSPlayer.tsx
📚 Learning: 2025-09-17T14:59:36.985Z
Learnt from: CR
PR: mkdir700/EchoPlayer#0
File: CLAUDE.md:0-0
Timestamp: 2025-09-17T14:59:36.985Z
Learning: Applies to **/*.{ts,tsx} : 在 styled-components 中:主题相关属性使用 AntD CSS 变量(如 var(--ant-color-bg-elevated));
Applied to files:
src/renderer/src/pages/player/components/HLSPlayer.tsx
📚 Learning: 2025-09-17T14:59:36.985Z
Learnt from: CR
PR: mkdir700/EchoPlayer#0
File: CLAUDE.md:0-0
Timestamp: 2025-09-17T14:59:36.985Z
Learning: Applies to src/renderer/src/**/*.{ts,tsx} : 尺寸与时长等不要硬编码,优先使用 useTheme() 的 token 或集中样式变量(如 motionDurationMid、borderRadiusSM/MD)
Applied to files:
src/renderer/src/pages/player/components/HLSPlayer.tsx
🧬 Code graph analysis (9)
src/renderer/src/services/CodecCompatibilityChecker.ts (1)
src/renderer/src/services/Logger.ts (2)
loggerService(817-817)error(422-424)
src/renderer/src/pages/player/PlayerPage.tsx (3)
src/renderer/src/services/CodecCompatibilityChecker.ts (2)
ExtendedErrorType(257-267)CodecCompatibilityChecker(277-524)src/renderer/src/state/stores/player.store.ts (1)
usePlayerStore(609-611)src/renderer/src/services/SessionService.ts (1)
SessionService(111-480)
src/renderer/src/pages/player/components/TranscodeLoadingIndicator.tsx (2)
src/renderer/src/services/Logger.ts (1)
loggerService(817-817)src/renderer/src/state/stores/player.store.ts (1)
usePlayerStore(609-611)
src/renderer/src/pages/player/components/HLSPlayer.tsx (3)
src/renderer/src/services/Logger.ts (2)
loggerService(817-817)error(422-424)src/renderer/src/services/CodecCompatibilityChecker.ts (1)
ExtendedErrorType(257-267)src/renderer/src/pages/player/hooks/usePlayerEngine.ts (1)
usePlayerEngine(97-305)
src/renderer/src/pages/player/components/VideoStatusIndicator.tsx (1)
src/renderer/src/infrastructure/styles/theme.ts (4)
Z_INDEX(75-88)SPACING(43-58)GLASS_EFFECT(129-161)BORDER_RADIUS(61-72)
src/renderer/src/pages/player/components/HLSVideoSurface.tsx (4)
src/renderer/src/services/Logger.ts (2)
loggerService(817-817)error(422-424)src/renderer/src/services/CodecCompatibilityChecker.ts (1)
ExtendedErrorType(257-267)src/renderer/src/pages/player/hooks/usePlayerCommands.ts (1)
usePlayerCommands(18-305)src/renderer/src/pages/player/components/AutoResumeCountdown.tsx (1)
AutoResumeCountdown(22-137)
src/renderer/src/pages/player/hooks/usePlayerEngine.ts (2)
src/renderer/src/state/stores/player.store.ts (1)
usePlayerStore(609-611)src/renderer/src/state/index.ts (1)
usePlayerStore(1-1)
src/renderer/src/pages/player/components/VolumeIndicator.tsx (1)
src/renderer/src/state/stores/player.store.ts (1)
usePlayerStore(609-611)
src/renderer/src/pages/player/components/PlayerSelector.tsx (3)
src/renderer/src/services/Logger.ts (2)
loggerService(817-817)error(422-424)src/renderer/src/services/CodecCompatibilityChecker.ts (1)
ExtendedErrorType(257-267)src/renderer/src/state/stores/player.store.ts (1)
usePlayerStore(609-611)
🪛 Biome (2.1.2)
src/renderer/src/pages/player/components/HLSVideoSurface.tsx
[error] 53-53: The elements with this role can be changed to the following elements:
For examples and more information, see WAI-ARIA Roles
(lint/a11y/useSemanticElements)
🔇 Additional comments (12)
package.json (2)
80-80: react-player 升级至 3.3.2:请核对破坏性变更与打包形态
- 3.x 版包含破坏性调整(包含 ESM 打包/导入路径变化等)。请确认:
- 现有 import 方式与打包工具(Vite/Electron)兼容;
- React 19 环境下运行无警告;
- 若你已手动集成 hls.js 的 HLS 播放路径,确保未与 react-player 的内置 HLS 处理产生重复/冲突。
74-74: 移除 @types/hls.js(hls.js v1+ 已自带类型定义)
- 从 devDependencies 中删除
@types/hls.js,避免重复声明和类型冲突。- 可选:若需锁定依赖版本以防次要更新引入回归,可将
hls.js版本从^1.6.13修改为1.6.13。src/renderer/src/pages/player/engine/PlayerOrchestrator.ts (1)
606-614: 新增 waiting 事件映射清晰可用onWaiting/onCanPlay 与外部状态同步方式正确,符合本 PR 的 HLS/转码等待态设计。
src/renderer/src/pages/player/hooks/usePlayerEngine.ts (2)
49-57: 媒体 seeking/waiting 状态同步到 Store 的实现合理通过 setVideoSeeking/setVideoWaiting 同步到 Store 并记录日志,接口清晰。
283-288: 媒体事件 onWaiting/onCanPlay 转发到编排器的接线正确事件面扩展与 Orchestrator 新增 API 对齐,无副作用风险。
src/renderer/src/state/stores/player.store.ts (1)
199-213: 转码/HLS 状态与动作设计清晰setHlsMode/setTranscodeStatus/update/reset/switch 概念边界清楚,便于 UI/引擎协作。视频加载态 setVideoSeeking/setVideoWaiting 切片合理。
src/renderer/src/pages/player/PlayerPage.tsx (1)
262-279: 卸载时同时 resetTranscodeInfo 与删除会话,清理流程完善会话清理包裹了 then/catch/finally,且将 sessionId 置空,流程健壮。
src/renderer/src/pages/player/components/index.ts (1)
6-6: 新增导出符合预期补充导出 PlayerSelector 合理,无其他问题。
src/renderer/src/pages/player/components/TranscodeLoadingIndicator.tsx (1)
14-45: 实现清晰,条件与选择器使用正确仅在 HLS 模式且转码/缓冲相关状态下展示,单字段选择器合规。
src/renderer/src/pages/player/components/PlayerSelector.tsx (1)
1-3: 确认 ExtendedErrorType 的导入路径是否正确
import { type ExtendedErrorType } from '@renderer/services'依赖汇总导出。请确认该 barrel 确实 re-export 了该类型(见CodecCompatibilityChecker.ts定义)。src/main/services/MediaParserService.ts (2)
85-86: 确认 FFmpegVideoInfo.bitrate 的类型约定此处将比特率固定为字符串
'0'。若FFmpegVideoInfo.bitrate设计为 number,建议统一为数值类型(如0),避免后续计算或展示格式化冲突。
92-111: 编解码与分辨率补全逻辑清晰可取优先顶级字段,回落 tracks,未解析出有效编解码器时触发 FFmpeg fallback,策略合理。
Also applies to: 118-125, 127-135
| "@types/react-dom": "^19.1.9", | ||
| "@types/react-virtualized": "^9.22.2", | ||
| "@types/yaml": "^1.9.7", | ||
| "@types/hls.js": "^1.0.0", |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
已从 dependencies 移至 devDependencies,👍;但建议彻底移除以避免类型冲突
- 先前建议已落实。不过鉴于 hls.js v1 自带类型,保留 @types/hls.js 可能造成重复声明。
- 建议删除该条目。
应用此 diff 移除类型包:
- "@types/hls.js": "^1.0.0",📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| "@types/hls.js": "^1.0.0", |
🤖 Prompt for AI Agents
In package.json around line 111, the @types/hls.js entry remains (now in
devDependencies) which can cause duplicate type declarations because hls.js v1
ships its own types; remove the "@types/hls.js": "^1.0.0" entry from
package.json entirely and then update your lockfile by running npm install or
yarn install to persist the change.
| // 记录完整的原始结果结构以便调试 | ||
| logger.debug('📋 Remotion 原始结果结构', { | ||
| keys: Object.keys(result), | ||
| videoCodec: result.videoCodec, | ||
| audioCodec: result.audioCodec, | ||
| videoTracks: result.videoTracks, | ||
| audioTracks: result.audioTracks, | ||
| dimensions: result.dimensions, | ||
| tracks: result.tracks, | ||
| fullResult: JSON.stringify(result, null, 2) | ||
| }) |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
避免在生产日志中 JSON.stringify 全量结果(体积与性能风险)
fullResult: JSON.stringify(result, null, 2) 体积大且序列化开销高,易淹没日志、影响性能。建议仅输出关键字段,或仅在开发环境/调试开关下输出。
可选修正:
- logger.debug('📋 Remotion 原始结果结构', {
+ const debugPayload = {
keys: Object.keys(result),
videoCodec: result.videoCodec,
audioCodec: result.audioCodec,
videoTracks: result.videoTracks,
audioTracks: result.audioTracks,
dimensions: result.dimensions,
- tracks: result.tracks,
- fullResult: JSON.stringify(result, null, 2)
- })
+ tracksCount: Array.isArray(result.tracks) ? result.tracks.length : 0
+ }
+ if (process.env.NODE_ENV !== 'production') {
+ logger.debug('📋 Remotion 原始结果结构', debugPayload)
+ }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| // 记录完整的原始结果结构以便调试 | |
| logger.debug('📋 Remotion 原始结果结构', { | |
| keys: Object.keys(result), | |
| videoCodec: result.videoCodec, | |
| audioCodec: result.audioCodec, | |
| videoTracks: result.videoTracks, | |
| audioTracks: result.audioTracks, | |
| dimensions: result.dimensions, | |
| tracks: result.tracks, | |
| fullResult: JSON.stringify(result, null, 2) | |
| }) | |
| // 记录完整的原始结果结构以便调试 | |
| const debugPayload = { | |
| keys: Object.keys(result), | |
| videoCodec: result.videoCodec, | |
| audioCodec: result.audioCodec, | |
| videoTracks: result.videoTracks, | |
| audioTracks: result.audioTracks, | |
| dimensions: result.dimensions, | |
| tracksCount: Array.isArray(result.tracks) ? result.tracks.length : 0 | |
| } | |
| if (process.env.NODE_ENV !== 'production') { | |
| logger.debug('📋 Remotion 原始结果结构', debugPayload) | |
| } |
🤖 Prompt for AI Agents
In src/main/services/MediaParserService.ts around lines 67 to 77, logging uses
JSON.stringify(result, null, 2) which can produce very large payloads and harm
performance; change the logging to avoid serializing the entire result in
production by either (a) logging only a curated subset of key fields (e.g. id,
duration, format, size, videoCodec, audioCodec, dimensions, track counts) or (b)
gating the full JSON.stringify behind an explicit debug flag or NODE_ENV check
so that fullResult is only serialized when process.env.NODE_ENV !== 'production'
or when a dedicated verbose debug flag is enabled; implement one of these
options and replace the current fullResult entry accordingly.
| // 稳定的 video 元素引用处理,延迟连接直到准备完成 | ||
| const handleVideoRef = useCallback( | ||
| (node: HTMLVideoElement | null) => { | ||
| videoRef.current = node | ||
| isReadyRef.current = false | ||
|
|
||
| if (node && isMountedRef.current) { | ||
| const currentSrc = currentSrcRef.current | ||
| // 对于非 HLS 视频,立即连接 | ||
| if (!currentSrc?.includes('.m3u8')) { | ||
| connectVideoElement(node) | ||
| isReadyRef.current = true | ||
| logger.debug('非HLS视频元素已连接到播放器引擎') | ||
| } | ||
| // HLS 视频会在 loadedmetadata 事件后连接 | ||
| } | ||
| }, | ||
| [connectVideoElement] | ||
| ) |
There was a problem hiding this comment.
基于 actualSrc 判断 HLS,避免首次挂载误连引擎
问题:handleVideoRef/handleLoadedMetadata 依赖 currentSrcRef 判断是否 HLS。首次挂载时该值为空字符串,会把 HLS 源当作“非 HLS”提前 connect,导致 loadedmetadata 不再连接,破坏预期流程并可能引发状态漂移。
修复:基于 actualSrc 计算 isHlsSrc,并在两个回调里使用它;相应地更新依赖。
@@
- // 稳定的 video 元素引用处理,延迟连接直到准备完成
+ // 稳定的 video 元素引用处理,延迟连接直到准备完成
const handleVideoRef = useCallback(
(node: HTMLVideoElement | null) => {
videoRef.current = node
isReadyRef.current = false
if (node && isMountedRef.current) {
- const currentSrc = currentSrcRef.current
- // 对于非 HLS 视频,立即连接
- if (!currentSrc?.includes('.m3u8')) {
+ const isHlsSrc = Boolean(actualSrc?.includes('.m3u8'))
+ // 对于非 HLS 视频,立即连接
+ if (!isHlsSrc) {
connectVideoElement(node)
isReadyRef.current = true
logger.debug('非HLS视频元素已连接到播放器引擎')
}
// HLS 视频会在 loadedmetadata 事件后连接
}
},
- [connectVideoElement]
+ [connectVideoElement, actualSrc]
)
@@
- const handleLoadedMetadata = useCallback(() => {
+ const handleLoadedMetadata = useCallback(() => {
logger.debug('HLS视频元数据加载完成')
- // 对于 HLS 视频,在元数据加载完成后才连接到引擎
- const currentSrc = currentSrcRef.current
- if (videoRef.current && !isReadyRef.current && currentSrc?.includes('.m3u8')) {
+ // 对于 HLS 视频,在元数据加载完成后才连接到引擎
+ const isHlsSrc = Boolean(actualSrc?.includes('.m3u8'))
+ if (videoRef.current && !isReadyRef.current && isHlsSrc) {
connectVideoElement(videoRef.current)
isReadyRef.current = true
logger.debug('HLS视频元素已连接到播放器引擎')
}
onLoadedMetadata?.()
- }, [onLoadedMetadata, connectVideoElement])
+ }, [onLoadedMetadata, connectVideoElement, actualSrc])Also applies to: 56-69
| function HLSVideoSurface({ src, onError }: HLSVideoSurfaceProps) { | ||
| const surfaceRef = useRef<HTMLDivElement>(null) |
There was a problem hiding this comment.
声明的 onLoadedMetadata 未透传到 HLSPlayer
接口声明了 onLoadedMetadata,但组件未接收/转发,导致上层无法获知元数据加载完成。
-function HLSVideoSurface({ src, onError }: HLSVideoSurfaceProps) {
+function HLSVideoSurface({ src, onLoadedMetadata, onError }: HLSVideoSurfaceProps) {
@@
- <HLSPlayer src={src} onError={handleVideoError} />
+ <HLSPlayer src={src} onLoadedMetadata={onLoadedMetadata} onError={handleVideoError} />Also applies to: 53-54
🤖 Prompt for AI Agents
In src/renderer/src/pages/player/components/HLSVideoSurface.tsx around lines
25-26 and 53-54, the prop onLoadedMetadata is declared in the interface but not
accepted or forwarded by HLSVideoSurface, so parent components never receive
metadata load events; update the HLSVideoSurfaceProps type if needed to include
onLoadedMetadata, add onLoadedMetadata to the component destructured props
(alongside src and onError), and forward that handler to the underlying
HLSPlayer / video element where metadata is handled (the same location
referenced at lines 53-54) so the event is propagated to callers.
| const Surface = styled.div` | ||
| position: absolute; | ||
| inset: 0; | ||
| display: flex; | ||
| align-items: center; | ||
| justify-content: center; | ||
| background: #000; | ||
| cursor: pointer; | ||
|
|
||
| /* 为字幕覆盖层提供定位上下文 */ | ||
| overflow: hidden; | ||
| ` |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
用主题 CSS 变量替换硬编码背景色
遵循 renderer 样式规范,避免硬编码颜色值。
(依据样式规范)
const Surface = styled.div`
@@
- background: #000;
+ background: var(--ant-color-bg-elevated, #000);
cursor: pointer;📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const Surface = styled.div` | |
| position: absolute; | |
| inset: 0; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| background: #000; | |
| cursor: pointer; | |
| /* 为字幕覆盖层提供定位上下文 */ | |
| overflow: hidden; | |
| ` | |
| const Surface = styled.div` | |
| position: absolute; | |
| inset: 0; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| background: var(--ant-color-bg-elevated, #000); | |
| cursor: pointer; | |
| /* 为字幕覆盖层提供定位上下文 */ | |
| overflow: hidden; | |
| ` |
| color: var(--ant-color-primary, #007aff); | ||
| ` |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
避免颜色硬编码回退值
var(--ant-color-primary, #007aff)、var(--ant-color-text, #ffffff) 含硬编码回退色。优先纯 CSS 变量或以项目内变量作为回退,避免直接 hex。
示例:
- color: var(--ant-color-primary, #007aff);
+ color: var(--ant-color-primary);
- color: var(--ant-color-text, #ffffff);
+ color: var(--ant-color-text);As per coding guidelines
Also applies to: 177-178
🤖 Prompt for AI Agents
In src/renderer/src/pages/player/components/VideoStatusIndicator.tsx around
lines 170-171 (and also 177-178), the CSS uses hard-coded hex fallbacks like
var(--ant-color-primary, #007aff) and var(--ant-color-text, #ffffff); remove
direct hex literals and use project CSS variables as fallbacks instead (e.g.,
var(--ant-color-primary, var(--project-primary)) or simply
var(--ant-color-primary) if a safe project-level default exists), or map to
existing theme variables imported from the codebase; update all instances to
reference only CSS variables or project variables for fallbacks to comply with
the coding guideline.
| // 监听音量变化,显示指示器并在 1 秒后自动隐藏 | ||
| useEffect(() => { | ||
| setShowIndicator(true) | ||
|
|
||
| const timer = setTimeout(() => { | ||
| setShowIndicator(false) | ||
| }, 1000) | ||
|
|
||
| return () => clearTimeout(timer) | ||
| }, [volume, muted]) |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
可选:避免首次渲染即显示指示器的闪烁
当前组件在挂载时也会显示 1s 指示器。可用 ref 跳过首次 effect,优化体验。
-import { useEffect, useState } from 'react'
+import { useEffect, useRef, useState } from 'react'
@@
useEffect(() => {
- setShowIndicator(true)
+ // 跳过首次渲染,避免进场闪烁
+ const didMountRef = (VolumeIndicator as any)._didMountRef ??= { current: false }
+ if (!didMountRef.current) {
+ didMountRef.current = true
+ return
+ }
+ setShowIndicator(true)Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In src/renderer/src/pages/player/components/VolumeIndicator.tsx around lines 17
to 26, the effect always shows the indicator on mount causing a flash; use a ref
to skip the first effect run: add a useRef<boolean>(true) called isFirstRender,
then inside the useEffect check if isFirstRender.current is true — if so set it
to false and return early; otherwise run the existing
setShowIndicator(true)/setTimeout logic and clear the timer in the cleanup.
Ensure isFirstRender is not added to the effect dependencies.
| const playListUrl = `http://127.0.0.1:8799${sessionResult.playlist_url}` | ||
|
|
There was a problem hiding this comment.
避免硬编码 http://127.0.0.1:8799,使用服务端返回的绝对 URL 或统一解析方法
当前将 playlist_url 拼接到固定主机与端口,环境切换(生产/测试/IPv6/代理)会失效,也与 SessionService 的 API_BASE_URL 不一致。建议:
- 让后端返回绝对的 playlist_url;或
- 在 SessionService 暴露一个 resolveUrl/absoluteUrl 工具统一生成基准 URL;前端此处仅使用该工具。
我可以补一个 SessionService.resolveUrl(…) 并改造这里的调用,要我起个 PR 片段吗?
🤖 Prompt for AI Agents
In src/renderer/src/pages/player/PlayerPage.tsx around lines 180-181, avoid
hardcoding "http://127.0.0.1:8799" when building playListUrl; instead use an
absolute URL returned by the backend or a unified resolver on SessionService.
Replace the string concatenation with a call to
SessionService.resolveUrl(sessionResult.playlist_url) (or use
sessionResult.playlist_url if backend returns an absolute URL), ensuring
SessionService uses the app's API_BASE_URL logic so environments
(prod/test/IPv6/proxy) remain consistent.
| name: 'hevc', | ||
| aliases: ['h265', 'hev1', 'hvc1'], | ||
| mimeTypes: ['video/mp4; codecs="hev1"', 'video/mp4; codecs="hvc1"'], | ||
| supported: true, | ||
| category: 'advanced', | ||
| displayName: 'H.265/HEVC' | ||
| } |
There was a problem hiding this comment.
HEVC 默认置为 supported=true 风险很高,建议改为 false(并依赖动态检测)
多数桌面浏览器对 HEVC(h265/hev1/hvc1)原生支持并不稳定/统一。将其静态标记为支持会导致误判并跳过转码。建议至少将该规则的 supported 设为 false,以配合上方的动态检测逻辑。
{
name: 'hevc',
aliases: ['h265', 'hev1', 'hvc1'],
mimeTypes: ['video/mp4; codecs="hev1"', 'video/mp4; codecs="hvc1"'],
- supported: true,
+ supported: false,
category: 'advanced',
displayName: 'H.265/HEVC'
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| name: 'hevc', | |
| aliases: ['h265', 'hev1', 'hvc1'], | |
| mimeTypes: ['video/mp4; codecs="hev1"', 'video/mp4; codecs="hvc1"'], | |
| supported: true, | |
| category: 'advanced', | |
| displayName: 'H.265/HEVC' | |
| } | |
| name: 'hevc', | |
| aliases: ['h265', 'hev1', 'hvc1'], | |
| mimeTypes: ['video/mp4; codecs="hev1"', 'video/mp4; codecs="hvc1"'], | |
| supported: false, | |
| category: 'advanced', | |
| displayName: 'H.265/HEVC' | |
| } |
🤖 Prompt for AI Agents
In src/renderer/src/services/CodecCompatibilityChecker.ts around lines 72 to 78,
the HEVC codec entry currently sets supported: true which risks false positives;
change supported to false and rely on the existing dynamic detection logic
instead, i.e., update the HEVC rule to set supported: false (and leave aliases,
mimeTypes, category, displayName intact) so runtime checks determine true
support and avoid skipping necessary transcodes.
| switchToHlsSource: (hlsSrc: string, transcodeInfo: Partial<TranscodeInfo>) => | ||
| set((s: Draft<PlayerStore>) => { | ||
| s.hlsMode = true | ||
| Object.assign(s.transcodeInfo, { | ||
| ...transcodeInfo, | ||
| hlsSrc, | ||
| status: 'completed' | ||
| }) | ||
| }), |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
建议在切换到 HLS 时兜底写入 endTime
若上层未提前写 endTime,这里可兜底填充,便于统计时长与状态统一。
switchToHlsSource: (hlsSrc: string, transcodeInfo: Partial<TranscodeInfo>) =>
set((s: Draft<PlayerStore>) => {
s.hlsMode = true
Object.assign(s.transcodeInfo, {
...transcodeInfo,
hlsSrc,
- status: 'completed'
+ status: 'completed',
+ endTime: s.transcodeInfo.endTime ?? Date.now()
})
}),📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| switchToHlsSource: (hlsSrc: string, transcodeInfo: Partial<TranscodeInfo>) => | |
| set((s: Draft<PlayerStore>) => { | |
| s.hlsMode = true | |
| Object.assign(s.transcodeInfo, { | |
| ...transcodeInfo, | |
| hlsSrc, | |
| status: 'completed' | |
| }) | |
| }), | |
| switchToHlsSource: (hlsSrc: string, transcodeInfo: Partial<TranscodeInfo>) => | |
| set((s: Draft<PlayerStore>) => { | |
| s.hlsMode = true | |
| Object.assign(s.transcodeInfo, { | |
| ...transcodeInfo, | |
| hlsSrc, | |
| status: 'completed', | |
| endTime: s.transcodeInfo.endTime ?? Date.now() | |
| }) | |
| }), |
🤖 Prompt for AI Agents
In src/renderer/src/state/stores/player.store.ts around lines 581–589, when
switching to an HLS source the code sets hlsMode and merges transcodeInfo but
doesn't ensure an endTime; update switchToHlsSource to populate
transcodeInfo.endTime with the current timestamp (in the same format used
elsewhere in the store) only if endTime is not already provided, so you don’t
overwrite existing values and callers who forgot to set endTime get a sensible
fallback for duration/statistics.
* feat(player): implement proactive transcoding with codec compatibility detection - Integrate codec compatibility checking in PlayerPage loadData stage for early format detection - Add TranscodeService client for backend FastAPI JIT transcoding API communication - Implement CodecCompatibilityChecker with browser codec support detection using HTMLVideoElement.canPlayType() - Create TranscodeCacheManager for caching transcoding results and performance optimization - Add TranscodeIndicator UI component with Ant Design styling for transcoding status feedback - Extend player state management with HLS mode support and transcoding status tracking - Refactor VideoSurface to simplify responsibilities, removing active codec detection logic - Update VideoErrorRecovery component to support extended error types including codec-specific errors - Add Task Master integration with comprehensive PRD parsing and task management Changes: - PlayerPage.tsx: Add proactive transcoding detection in loadData with fallback error handling - VideoSurface.tsx: Simplify to focus on playback control, trust pre-processed src from PlayerPage - TranscodeService.ts: Implement HTTP client with retry logic, request deduplication, and error handling - CodecCompatibilityChecker.ts: Browser codec compatibility detection with H.265/HEVC support mapping - TranscodeCacheManager.ts: Local caching with expiration, memory optimization, and batch processing - TranscodeIndicator.tsx: Status indicator component with theme compatibility and loading animations - player.store.ts: Extend state management for HLS mode, transcode status, and source switching This implementation enables seamless video format handling where incompatible formats (H.265, unsupported audio codecs) are automatically transcoded to HLS during data loading, providing users with immediate playback capability when the player interface loads. * feat(player): implement HLS playback error handling with user-friendly notifications - Add 'hls-player-missing' error type to ExtendedErrorType for HLS player unavailability - Enhance VideoSurface error detection to identify HLS URL playback failures - Implement HLS-specific error recognition for DEMUXER_ERROR_COULD_NOT_OPEN and FFmpegDemuxer errors - Add user-friendly error dialog in VideoErrorRecovery with transcode status information - Update TranscodeIndicator to show "waiting for HLS player" state when transcode completes - Fix PlayerSettingsLoader type compatibility by adding missing hlsMode and transcodeInfo fields Key Features: - Smart distinction between genuine transcode failures and HLS player absence - Comprehensive status display showing transcode completion and HLS player readiness - Contextual error messages explaining that transcoding succeeded but HLS playback unavailable - Seamless integration preparing for future HLS player implementation (Task 2) Technical Implementation: - Enhanced handleVideoError in VideoSurface with HLS URL pattern detection (.m3u8, playlist) - Extended VideoErrorRecovery with HLS-specific status panel and styled components - Updated TranscodeIndicator logic to show warning state for completed HLS transcodes - Maintained type safety across all player state management components This enhancement bridges the gap between successful transcoding and pending HLS player implementation, providing clear user feedback about system capabilities and limitations. Completes Task 1.9: HLS playback error state handling and user notifications * feat(player): implement comprehensive HLS player architecture with session management - Add HLSPlayer component with hls.js integration and engine compatibility - Create HLSVideoSurface as complete replacement for VideoSurface with subtitle overlay - Implement PlayerSelector for intelligent switching between native and HLS players - Add SessionService for HLS session management and transcoding coordination - Introduce VideoFormatDetector for comprehensive format analysis and compatibility checking - Refactor TranscodeService architecture by removing legacy cache management - Enhance CodecCompatibilityChecker with extended error handling - Update player store with improved HLS state management - Add file utilities for video format detection and validation Key Features: - Seamless player switching based on codec compatibility - Automatic HLS fallback for unsupported native formats - Unified interface maintaining VideoSurface API compatibility - Session-based HLS streaming with window management - Comprehensive error handling and recovery mechanisms - Integration with existing player engine and state management Technical Implementation: - HLSPlayer: Direct hls.js integration with player engine bridging - PlayerSelector: Smart format detection and automatic player selection - SessionService: RESTful session management for HLS streaming - VideoFormatDetector: Multi-level format analysis (quick/standard/deep) - Enhanced transcoding workflow with session-based architecture This implementation provides a robust foundation for handling diverse video formats while maintaining seamless user experience and backward compatibility. * Update src/renderer/src/pages/player/components/TranscodeIndicator.tsx Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * refactor(codec): replace hardcoded codec detection with configuration-driven architecture Replace hardcoded codec support lists with elegant CodecRegistry system: - Introduce CodecRule interface for declarative codec configuration - Create CodecRegistry with centralized video/audio codec rules - Remove duplicate detection logic with unified checkCodecSupportByRule method - Add user-friendly codec display names (e.g., "H.265/HEVC" instead of "hevc") - Remove h265-unsupported error type (now handled generically) - Mark H.265 as supported (via HLS transcoding) instead of unsupported MediaParserService improvements: - Fix codec parsing to prioritize top-level fields (videoCodec, audioCodec) - Add fallback to tracks array for codec information - Add comprehensive debug logging for Remotion result structure - Return null when no valid codec detected to trigger FFmpeg fallback Benefits: - New codecs can be added via configuration without code changes - Eliminates hardcoded Set collections and scattered string matching - Better maintainability and extensibility - Improved error messages with friendly codec names - Cleaner architecture with single responsibility principle This refactoring addresses the limitation where codec support logic was scattered across hardcoded Sets and duplicate checking methods, making it difficult to maintain and extend when adding new codec support. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * refactor(player): remove excessive logging and simplify HLS error handling - Remove redundant debug logs in HLS error event handler - Remove bufferStalledError special handling and recovery strategies - Remove render-time side-effect logs from PlayerSelector component - Simplify non-fatal HLS error logging to essential information only Changes: - HLSPlayer: Remove detailed HLS error debug logs and buffer stall recovery logic - PlayerSelector: Remove debug logs in render path to follow React pure render principles This refactoring reduces console noise during HLS playback and aligns with React best practices by eliminating side effects from component render functions 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * fix(player): 修复转码会话资源泄漏问题 问题描述: - PlayerPage 在创建转码会话后没有在组件卸载时释放 - 导致后端持续保留会话,长时间使用会造成资源泄漏 修复内容: - 添加 sessionIdRef 保存会话 ID - 在组件清理时调用 SessionService.deleteSession 释放会话 - 添加完整的错误处理和日志记录 - 确保 ref 在清理后重置为 null 影响范围: - PlayerPage.tsx 组件清理逻辑 - 转码会话生命周期管理 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * refactor(deps): move @types/hls.js from dependencies to devDependencies - Move @types/hls.js from dependencies to devDependencies section - Type definitions are only needed during development and build time - Reduces production bundle size by excluding unnecessary type packages This change aligns with TypeScript best practices where @types/* packages should be in devDependencies as they are not required at runtime. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * fix(player): clear stale HLS state to prevent wrong player selection - Add resetTranscodeInfo() at the start of loadData to clear previous HLS state - Add resetTranscodeInfo() in component cleanup to ensure state reset on unmount - Prevent PlayerSelector from rendering old HLS player for new non-HLS videos The player store could retain hlsMode and stale hlsSrc from previous HLS sessions, causing PlayerSelector to incorrectly render the HLS player for new non-HLS videos. This fix ensures all transcode state is cleared before loading new videos and on component unmount, preventing cross-session state pollution. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * refactor(player): remove TranscodeIndicator component Remove the TranscodeIndicator UI component as transcode failures should be handled through the default error recovery flow via VideoErrorRecovery component. Changes: - Delete TranscodeIndicator.tsx component file - Remove TranscodeIndicator from PlayerPage imports and JSX - Remove TranscodeIndicator export from components/index.ts 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * feat(player): add video status indicator with transcode loading feedback - Implement universal VideoStatusIndicator component with framer-motion animations * Support multiple status types: loading, volume, custom * Configurable card display (loading type shows icon-only by default) * Smooth fade-in/fade-out and scale transitions with 0.2s duration - Add TranscodeLoadingIndicator for HLS transcoding wait states * Display loading spinner during seeking to untranscoded segments * Monitor transcoding, seeking, and waiting states via player store * Prevent user confusion during transcode buffer periods - Add VolumeIndicator example component (ready for future integration) * Auto-hide after 1s delay on volume changes * Dynamic icon selection based on volume level (0-100) - Extend player state management with video loading states * Add isVideoSeeking and isVideoWaiting to player store * Wire onWaiting/onCanPlay events through PlayerOrchestrator * Connect video element events to state updater - Integrate with HLSPlayer and HLSVideoSurface components * Listen to waiting/canPlay events in HLSPlayer * Mount TranscodeLoadingIndicator in HLSVideoSurface Changes: - VideoStatusIndicator: Universal status overlay with glass morphism effects - TranscodeLoadingIndicator: Specialized loading state for HLS transcoding - VolumeIndicator: Example implementation for volume feedback - player.store: Add isVideoSeeking, isVideoWaiting state fields - PlayerOrchestrator: Add onWaiting/onCanPlay event handlers - usePlayerEngine: Wire waiting/canPlay events to state management - HLSPlayer: Listen to video waiting/canPlay events - HLSVideoSurface: Integrate TranscodeLoadingIndicator component This enhancement provides clear visual feedback during HLS transcoding operations, preventing users from thinking the application has frozen when seeking to untranscoded video segments. The universal indicator design enables future extensions for volume, playback speed, and other status displays. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * fix(player): add missing PlayerState properties in PlayerSettingsLoader Add isVideoSeeking and isVideoWaiting properties to PlayerState objects in PlayerSettingsLoader to fix TypeScript compilation errors. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> Co-authored-by: Claude <noreply@anthropic.com>
* feat(player): implement proactive transcoding with codec compatibility detection - Integrate codec compatibility checking in PlayerPage loadData stage for early format detection - Add TranscodeService client for backend FastAPI JIT transcoding API communication - Implement CodecCompatibilityChecker with browser codec support detection using HTMLVideoElement.canPlayType() - Create TranscodeCacheManager for caching transcoding results and performance optimization - Add TranscodeIndicator UI component with Ant Design styling for transcoding status feedback - Extend player state management with HLS mode support and transcoding status tracking - Refactor VideoSurface to simplify responsibilities, removing active codec detection logic - Update VideoErrorRecovery component to support extended error types including codec-specific errors - Add Task Master integration with comprehensive PRD parsing and task management Changes: - PlayerPage.tsx: Add proactive transcoding detection in loadData with fallback error handling - VideoSurface.tsx: Simplify to focus on playback control, trust pre-processed src from PlayerPage - TranscodeService.ts: Implement HTTP client with retry logic, request deduplication, and error handling - CodecCompatibilityChecker.ts: Browser codec compatibility detection with H.265/HEVC support mapping - TranscodeCacheManager.ts: Local caching with expiration, memory optimization, and batch processing - TranscodeIndicator.tsx: Status indicator component with theme compatibility and loading animations - player.store.ts: Extend state management for HLS mode, transcode status, and source switching This implementation enables seamless video format handling where incompatible formats (H.265, unsupported audio codecs) are automatically transcoded to HLS during data loading, providing users with immediate playback capability when the player interface loads. * feat(player): implement HLS playback error handling with user-friendly notifications - Add 'hls-player-missing' error type to ExtendedErrorType for HLS player unavailability - Enhance VideoSurface error detection to identify HLS URL playback failures - Implement HLS-specific error recognition for DEMUXER_ERROR_COULD_NOT_OPEN and FFmpegDemuxer errors - Add user-friendly error dialog in VideoErrorRecovery with transcode status information - Update TranscodeIndicator to show "waiting for HLS player" state when transcode completes - Fix PlayerSettingsLoader type compatibility by adding missing hlsMode and transcodeInfo fields Key Features: - Smart distinction between genuine transcode failures and HLS player absence - Comprehensive status display showing transcode completion and HLS player readiness - Contextual error messages explaining that transcoding succeeded but HLS playback unavailable - Seamless integration preparing for future HLS player implementation (Task 2) Technical Implementation: - Enhanced handleVideoError in VideoSurface with HLS URL pattern detection (.m3u8, playlist) - Extended VideoErrorRecovery with HLS-specific status panel and styled components - Updated TranscodeIndicator logic to show warning state for completed HLS transcodes - Maintained type safety across all player state management components This enhancement bridges the gap between successful transcoding and pending HLS player implementation, providing clear user feedback about system capabilities and limitations. Completes Task 1.9: HLS playback error state handling and user notifications * feat(player): implement comprehensive HLS player architecture with session management - Add HLSPlayer component with hls.js integration and engine compatibility - Create HLSVideoSurface as complete replacement for VideoSurface with subtitle overlay - Implement PlayerSelector for intelligent switching between native and HLS players - Add SessionService for HLS session management and transcoding coordination - Introduce VideoFormatDetector for comprehensive format analysis and compatibility checking - Refactor TranscodeService architecture by removing legacy cache management - Enhance CodecCompatibilityChecker with extended error handling - Update player store with improved HLS state management - Add file utilities for video format detection and validation Key Features: - Seamless player switching based on codec compatibility - Automatic HLS fallback for unsupported native formats - Unified interface maintaining VideoSurface API compatibility - Session-based HLS streaming with window management - Comprehensive error handling and recovery mechanisms - Integration with existing player engine and state management Technical Implementation: - HLSPlayer: Direct hls.js integration with player engine bridging - PlayerSelector: Smart format detection and automatic player selection - SessionService: RESTful session management for HLS streaming - VideoFormatDetector: Multi-level format analysis (quick/standard/deep) - Enhanced transcoding workflow with session-based architecture This implementation provides a robust foundation for handling diverse video formats while maintaining seamless user experience and backward compatibility. * Update src/renderer/src/pages/player/components/TranscodeIndicator.tsx Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * refactor(codec): replace hardcoded codec detection with configuration-driven architecture Replace hardcoded codec support lists with elegant CodecRegistry system: - Introduce CodecRule interface for declarative codec configuration - Create CodecRegistry with centralized video/audio codec rules - Remove duplicate detection logic with unified checkCodecSupportByRule method - Add user-friendly codec display names (e.g., "H.265/HEVC" instead of "hevc") - Remove h265-unsupported error type (now handled generically) - Mark H.265 as supported (via HLS transcoding) instead of unsupported MediaParserService improvements: - Fix codec parsing to prioritize top-level fields (videoCodec, audioCodec) - Add fallback to tracks array for codec information - Add comprehensive debug logging for Remotion result structure - Return null when no valid codec detected to trigger FFmpeg fallback Benefits: - New codecs can be added via configuration without code changes - Eliminates hardcoded Set collections and scattered string matching - Better maintainability and extensibility - Improved error messages with friendly codec names - Cleaner architecture with single responsibility principle This refactoring addresses the limitation where codec support logic was scattered across hardcoded Sets and duplicate checking methods, making it difficult to maintain and extend when adding new codec support. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * refactor(player): remove excessive logging and simplify HLS error handling - Remove redundant debug logs in HLS error event handler - Remove bufferStalledError special handling and recovery strategies - Remove render-time side-effect logs from PlayerSelector component - Simplify non-fatal HLS error logging to essential information only Changes: - HLSPlayer: Remove detailed HLS error debug logs and buffer stall recovery logic - PlayerSelector: Remove debug logs in render path to follow React pure render principles This refactoring reduces console noise during HLS playback and aligns with React best practices by eliminating side effects from component render functions 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * fix(player): 修复转码会话资源泄漏问题 问题描述: - PlayerPage 在创建转码会话后没有在组件卸载时释放 - 导致后端持续保留会话,长时间使用会造成资源泄漏 修复内容: - 添加 sessionIdRef 保存会话 ID - 在组件清理时调用 SessionService.deleteSession 释放会话 - 添加完整的错误处理和日志记录 - 确保 ref 在清理后重置为 null 影响范围: - PlayerPage.tsx 组件清理逻辑 - 转码会话生命周期管理 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * refactor(deps): move @types/hls.js from dependencies to devDependencies - Move @types/hls.js from dependencies to devDependencies section - Type definitions are only needed during development and build time - Reduces production bundle size by excluding unnecessary type packages This change aligns with TypeScript best practices where @types/* packages should be in devDependencies as they are not required at runtime. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * fix(player): clear stale HLS state to prevent wrong player selection - Add resetTranscodeInfo() at the start of loadData to clear previous HLS state - Add resetTranscodeInfo() in component cleanup to ensure state reset on unmount - Prevent PlayerSelector from rendering old HLS player for new non-HLS videos The player store could retain hlsMode and stale hlsSrc from previous HLS sessions, causing PlayerSelector to incorrectly render the HLS player for new non-HLS videos. This fix ensures all transcode state is cleared before loading new videos and on component unmount, preventing cross-session state pollution. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * refactor(player): remove TranscodeIndicator component Remove the TranscodeIndicator UI component as transcode failures should be handled through the default error recovery flow via VideoErrorRecovery component. Changes: - Delete TranscodeIndicator.tsx component file - Remove TranscodeIndicator from PlayerPage imports and JSX - Remove TranscodeIndicator export from components/index.ts 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * feat(player): add video status indicator with transcode loading feedback - Implement universal VideoStatusIndicator component with framer-motion animations * Support multiple status types: loading, volume, custom * Configurable card display (loading type shows icon-only by default) * Smooth fade-in/fade-out and scale transitions with 0.2s duration - Add TranscodeLoadingIndicator for HLS transcoding wait states * Display loading spinner during seeking to untranscoded segments * Monitor transcoding, seeking, and waiting states via player store * Prevent user confusion during transcode buffer periods - Add VolumeIndicator example component (ready for future integration) * Auto-hide after 1s delay on volume changes * Dynamic icon selection based on volume level (0-100) - Extend player state management with video loading states * Add isVideoSeeking and isVideoWaiting to player store * Wire onWaiting/onCanPlay events through PlayerOrchestrator * Connect video element events to state updater - Integrate with HLSPlayer and HLSVideoSurface components * Listen to waiting/canPlay events in HLSPlayer * Mount TranscodeLoadingIndicator in HLSVideoSurface Changes: - VideoStatusIndicator: Universal status overlay with glass morphism effects - TranscodeLoadingIndicator: Specialized loading state for HLS transcoding - VolumeIndicator: Example implementation for volume feedback - player.store: Add isVideoSeeking, isVideoWaiting state fields - PlayerOrchestrator: Add onWaiting/onCanPlay event handlers - usePlayerEngine: Wire waiting/canPlay events to state management - HLSPlayer: Listen to video waiting/canPlay events - HLSVideoSurface: Integrate TranscodeLoadingIndicator component This enhancement provides clear visual feedback during HLS transcoding operations, preventing users from thinking the application has frozen when seeking to untranscoded video segments. The universal indicator design enables future extensions for volume, playback speed, and other status displays. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * fix(player): add missing PlayerState properties in PlayerSettingsLoader Add isVideoSeeking and isVideoWaiting properties to PlayerState objects in PlayerSettingsLoader to fix TypeScript compilation errors. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> Co-authored-by: Claude <noreply@anthropic.com>
# [1.1.0-alpha.1](v1.0.0...v1.1.0-alpha.1) (2025-10-11) ### Bug Fixes * **homepage:** improve bottom spacing for card grid ([#194](#194)) ([801b6cd](801b6cd)) * remove green glow effect from progress bar ([#196](#196)) ([abc6f3e](abc6f3e)), closes [#e50914](https://github.com/mkdir700/EchoPlayer/issues/e50914) [#00b96](https://github.com/mkdir700/EchoPlayer/issues/00b96) * **semantic-release:** enhance version increment rules for prerelease branches ([#199](#199)) ([5d1e533](5d1e533)) * **theme:** resolve theme color not updating immediately for Switch components and progress bars ([#197](#197)) ([eed9ea2](eed9ea2)) ### Features * integrate session-backed HLS playback flow ([#200](#200)) ([ee972d1](ee972d1)) * intro backend for hls player ([2d34e7b](2d34e7b)) * optimize media-server build output to resources directory ([#201](#201)) ([1b8c28e](1b8c28e)) * **player:** update seek button icons from rewind/fastforward to undo/redo ([#193](#193)) ([1612c43](1612c43))
# [1.1.0-beta.1](v1.0.0...v1.1.0-beta.1) (2025-10-15) ### Bug Fixes * **AppUpdater, FFmpegDownloadService:** update default mirror source to global ([83194b3](83194b3)) * **build:** adjust resource handling for media-server in packaging ([086bd1b](086bd1b)) * **codec-compatibility:** handle missing codec information gracefully ([ea29f21](ea29f21)) * **FFmpegSection:** manage completion timeout for download process ([39b43c0](39b43c0)) * **FFprobeSection:** add return statement to download progress polling function ([49636cf](49636cf)) * **FFprobeSection:** ensure timeout cleanup after download success ([81a1431](81a1431)) * **FFprobeSection:** manage success timeout for download completion ([ce55d49](ce55d49)) * **FFprobeSection:** standardize font size using theme constants ([6387445](6387445)) * **FFprobeSection:** standardize spacing in styled components ([ba3c3d4](ba3c3d4)) * **homepage:** improve bottom spacing for card grid ([#194](#194)) ([801b6cd](801b6cd)) * make subtitle overlay container semantic ([2d6ae60](2d6ae60)) * **MediaServerService:** enhance error handling for file existence check ([11b74ef](11b74ef)) * **MediaServerService:** replace fs.existsSync with async stat for file existence check ([c9c98da](c9c98da)) * **player:** apply playback rate change through orchestrator when cycling speeds ([#210](#210)) ([fa9aa09](fa9aa09)) * **player:** remove HLS player missing error handling ([c7b593e](c7b593e)) * remove green glow effect from progress bar ([#196](#196)) ([abc6f3e](abc6f3e)), closes [#e50914](https://github.com/mkdir700/EchoPlayer/issues/e50914) [#00b96](https://github.com/mkdir700/EchoPlayer/issues/00b96) * **semantic-release:** enhance version increment rules for prerelease branches ([#199](#199)) ([5d1e533](5d1e533)) * **theme:** resolve theme color not updating immediately for Switch components and progress bars ([#197](#197)) ([eed9ea2](eed9ea2)) * **TranscodeLoadingIndicator:** remove logging for loading indicator display ([085db44](085db44)) * **useSubtitleScrollStateMachine:** start auto-return timer on user interactions ([8496ae0](8496ae0)) * **UvBootstrapperService:** enhance UV download logic with cached path checks ([fc0791a](fc0791a)) * **UvBootstrapperService:** ensure temp directory cleanup after download ([02c7b16](02c7b16)) * **UvBootstrapperService:** prevent concurrent downloads by checking download controllers ([19d31e7](19d31e7)) * **VolumeIndicator:** skip indicator display on initial render ([82d2281](82d2281)) * **workflow:** update artifact listing command for better compatibility ([dfb6ee4](dfb6ee4)) ### Features * integrate session-backed HLS playback flow ([#200](#200)) ([ee972d1](ee972d1)) * intro backend for hls player ([2d34e7b](2d34e7b)) * **media-server:** add transcode cache cleanup for deleted videos ([e2de9ad](e2de9ad)) * **media-server:** implement runtime runtime management system ([#204](#204)) ([f5f68b0](f5f68b0)) * optimize media-server build output to resources directory ([#201](#201)) ([1b8c28e](1b8c28e)) * **player:** add animated loading progress bar to PlayerPage ([#206](#206)) ([8ba6f7f](8ba6f7f)) * **player:** add media server recommendation prompt for incompatible videos ([#205](#205)) ([63221a2](63221a2)) * **player:** add subtitle search functionality ([c3228c3](c3228c3)) * **player:** add toggle auto-pause functionality ([98b59ef](98b59ef)) * **player:** HLS session progress polling with media server integration ([#209](#209)) ([a76e8c2](a76e8c2)) * **PlayerSettingsLoader:** add mask mode to subtitle overlay ([56e4f65](56e4f65)) * **player:** update seek button icons from rewind/fastforward to undo/redo ([#193](#193)) ([1612c43](1612c43)) * **RegionDetection:** integrate region detection service for IP-based country identification ([dbeb077](dbeb077)) * **subtitle:** introduce mask mode for subtitle overlay ([e1fb3eb](e1fb3eb)) * **SubtitleOverlay:** enhance positioning and collision handling ([92b061a](92b061a)) * **UvBootstrapperService:** enhance download management with concurrency control ([20522e9](20522e9)) ### Reverts * "fix(build): adjust resource handling for media-server in packaging" ([2133401](2133401))
# [1.1.0](v1.0.0...v1.1.0) (2025-10-16) ### Bug Fixes * **AppUpdater, FFmpegDownloadService:** update default mirror source to global ([83194b3](83194b3)) * **build:** adjust resource handling for media-server in packaging ([086bd1b](086bd1b)) * **codec-compatibility:** handle missing codec information gracefully ([ea29f21](ea29f21)) * **FFmpegSection:** manage completion timeout for download process ([39b43c0](39b43c0)) * **FFprobeSection:** add return statement to download progress polling function ([49636cf](49636cf)) * **FFprobeSection:** ensure timeout cleanup after download success ([81a1431](81a1431)) * **FFprobeSection:** manage success timeout for download completion ([ce55d49](ce55d49)) * **FFprobeSection:** standardize font size using theme constants ([6387445](6387445)) * **FFprobeSection:** standardize spacing in styled components ([ba3c3d4](ba3c3d4)) * **homepage:** improve bottom spacing for card grid ([#194](#194)) ([801b6cd](801b6cd)) * make subtitle overlay container semantic ([2d6ae60](2d6ae60)) * **MediaServerService:** enhance error handling for file existence check ([11b74ef](11b74ef)) * **MediaServerService:** replace fs.existsSync with async stat for file existence check ([c9c98da](c9c98da)) * **player:** apply playback rate change through orchestrator when cycling speeds ([#210](#210)) ([fa9aa09](fa9aa09)) * **player:** remove HLS player missing error handling ([c7b593e](c7b593e)) * **player:** restore muted state when re-entering player page ([1ec5c56](1ec5c56)) * remove green glow effect from progress bar ([#196](#196)) ([abc6f3e](abc6f3e)), closes [#e50914](https://github.com/mkdir700/EchoPlayer/issues/e50914) [#00b96](https://github.com/mkdir700/EchoPlayer/issues/00b96) * **semantic-release:** enhance version increment rules for prerelease branches ([#199](#199)) ([5d1e533](5d1e533)) * **theme:** resolve theme color not updating immediately for Switch components and progress bars ([#197](#197)) ([eed9ea2](eed9ea2)) * **TranscodeLoadingIndicator:** remove logging for loading indicator display ([085db44](085db44)) * **useSubtitleScrollStateMachine:** start auto-return timer on user interactions ([8496ae0](8496ae0)) * **UvBootstrapperService:** enhance UV download logic with cached path checks ([fc0791a](fc0791a)) * **UvBootstrapperService:** ensure temp directory cleanup after download ([02c7b16](02c7b16)) * **UvBootstrapperService:** prevent concurrent downloads by checking download controllers ([19d31e7](19d31e7)) * **VolumeIndicator:** skip indicator display on initial render ([82d2281](82d2281)) * **workflow:** update artifact listing command for better compatibility ([dfb6ee4](dfb6ee4)) ### Features * integrate session-backed HLS playback flow ([#200](#200)) ([ee972d1](ee972d1)) * intro backend for hls player ([2d34e7b](2d34e7b)) * **media-server:** add transcode cache cleanup for deleted videos ([e2de9ad](e2de9ad)) * **media-server:** implement runtime runtime management system ([#204](#204)) ([f5f68b0](f5f68b0)) * optimize media-server build output to resources directory ([#201](#201)) ([1b8c28e](1b8c28e)) * **player:** add animated loading progress bar to PlayerPage ([#206](#206)) ([8ba6f7f](8ba6f7f)) * **player:** add media server recommendation prompt for incompatible videos ([#205](#205)) ([63221a2](63221a2)) * **player:** add subtitle search functionality ([c3228c3](c3228c3)) * **player:** add toggle auto-pause functionality ([98b59ef](98b59ef)) * **player:** HLS session progress polling with media server integration ([#209](#209)) ([a76e8c2](a76e8c2)) * **PlayerSettingsLoader:** add mask mode to subtitle overlay ([56e4f65](56e4f65)) * **player:** update seek button icons from rewind/fastforward to undo/redo ([#193](#193)) ([1612c43](1612c43)) * **RegionDetection:** integrate region detection service for IP-based country identification ([dbeb077](dbeb077)) * **subtitle:** introduce mask mode for subtitle overlay ([e1fb3eb](e1fb3eb)) * **SubtitleOverlay:** enhance positioning and collision handling ([92b061a](92b061a)) * **UvBootstrapperService:** enhance download management with concurrency control ([20522e9](20522e9)) ### Reverts * "fix(build): adjust resource handling for media-server in packaging" ([2133401](2133401))
Summary
Testing
pnpm lintpnpm testSummary by CodeRabbit