Skip to content

feat: integrate session-backed HLS playback flow#200

Merged
mkdir700 merged 12 commits intodevfrom
feat/hls-player
Oct 8, 2025
Merged

feat: integrate session-backed HLS playback flow#200
mkdir700 merged 12 commits intodevfrom
feat/hls-player

Conversation

@mkdir700
Copy link
Owner

@mkdir700 mkdir700 commented Oct 8, 2025

Summary

  • detect codec compatibility when loading the player and spin up a session-backed HLS source when the native codecs are unsupported
  • add HLS-specific playback components, UI affordances, and error recovery paths while keeping native playback as a fallback
  • track transcode/session state in the player store, surface it through indicators, and bundle the hls.js dependency plus the updated backend submodule

Testing

  • pnpm lint
  • pnpm test
  • Manual playback against the session service

Summary by CodeRabbit

  • 新功能
    • 引入 HLS 播放与实时转码回退,原生解码失败时自动切换,播放保持无缝;新增播放器自动选择、转码状态指示、加载指示与音量提示,改进错误提示与恢复并可回退到原始源。
  • 依赖
    • 添加 hls.js(及类型)并升级 react-player。
  • 文档
    • 更新播放架构、回退与任务/使用说明。
  • 杂务
    • 后端子模块指针更新;配置语言改为 zh-CN。

…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.
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 8, 2025

Caution

Review failed

The pull request is closed.

Walkthrough

此变更引入基于 HLS 的转码播放路径:在 PlayerPage 中进行编解码兼容性检测,必要时通过 SessionService 启动转码会话并切换到 HLS 播放;新增 HLSPlayer/HLSVideoSurface/PlayerSelector 等组件与状态管理;扩展错误类型为 ExtendedErrorType;并补充格式检测、会话客户端、UI 指示器与工具函数等。

Changes

Cohort / File(s) 摘要
配置与依赖
/.coderabbit.yaml, package.json, backend
切换语言配置为 zh-CN;新增依赖 hls.js@types/hls.js,升级 react-player;更新 backend 子模块指针。
产品与任务文档
/.taskmaster/docs/prd.txt, /.taskmaster/tasks/tasks.json, CLAUDE.md
移除 SQLite 方案,记录 JIT 转码与 HLS 回退策略;新增详尽任务清单与 Task Master AI 指南条目。
播放页与选择器
src/renderer/src/pages/player/PlayerPage.tsx, src/renderer/src/pages/player/components/PlayerSelector.tsx, src/renderer/src/pages/player/components/index.ts
PlayerPage 集成 CodecCompatibilityChecker 与 SessionService,按需创建转码会话并切换数据源;新增 PlayerSelector 公共导出以在原生/ HLS 播放间切换并转发错误。
HLS 播放栈
src/renderer/src/pages/player/components/HLSPlayer.tsx, src/renderer/src/pages/player/components/HLSVideoSurface.tsx
新增 HLSPlayer(hls.js 与原生两路逻辑、事件/错误恢复)和 HLSVideoSurface(承载字幕、转码加载指示、点击切换播放/暂停)。
原生表面与错误恢复
src/renderer/src/pages/player/components/VideoSurface.tsx, src/renderer/src/pages/player/components/VideoErrorRecovery.tsx
简化 VideoSurface 签名并移除 onLoadedMetadata 恢复逻辑,改用 ExtendedErrorType 并对 file:// 源做异步存在性校验;VideoErrorRecovery 增加 hls-player-missing 分支与对应 UI。
播放器状态与动作
src/renderer/src/state/stores/player.store.ts, src/renderer/src/services/PlayerSettingsLoader.ts
扩展 PlayerState 与动作:新增 hlsModetranscodeInfo、TranscodeStatus 类型及相关 setter/切换动作;PlayerSettingsLoader 将运行态字段初始化为非持久化默认值。
会话与检测服务
src/renderer/src/services/SessionService.ts, src/renderer/src/services/CodecCompatibilityChecker.ts, src/renderer/src/services/VideoFormatDetector.ts, src/renderer/src/services/index.ts
新增 SessionService(会话创建/playlist 获取/seek/update/delete、重试与去重)、CodecCompatibilityChecker(编解码支持检测与错误类型映射)、VideoFormatDetector(多级格式检测与错误分析);并在服务索引中导出这些模块。
UI 指示器与状态组件
src/renderer/src/pages/player/components/TranscodeLoadingIndicator.tsx, src/renderer/src/pages/player/components/VideoStatusIndicator.tsx, src/renderer/src/pages/player/components/VolumeIndicator.tsx
新增通用视频状态组件与音量指示器;TranscodeLoadingIndicator 基于 hlsMode 与 transcode/等待/寻址状态显示加载。
引擎事件与 Hook
src/renderer/src/pages/player/engine/PlayerOrchestrator.ts, src/renderer/src/pages/player/hooks/usePlayerEngine.ts
PlayerOrchestrator 与 StateUpdater 增加等待态接口(setWaiting/onWaiting/onCanPlay);usePlayerEngine 增加等待/可播放事件同步到 store。
工具与媒体解析
src/renderer/src/utils/file.ts, src/main/services/MediaParserService.ts
新增 fileUrlToPath 工具函数(处理 file:// -> 本地路径);MediaParserService 改进 Remotion 解析逻辑、分辨率/codec 推断与回退判定并增强日志。

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
Loading
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)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60–90 minutes

Possibly related PRs

  • feat: integrate session-backed HLS playback flow #200 — 与本 PR 在 HLS / 转码路径(SessionService、PlayerSelector、HLSPlayer 等)有高度重叠,可能影响相同逻辑。
  • Dev2alpha #183 — 涉及 VideoSurface 改动(事件/错误处理/签名),与本次对 VideoSurface 与错误类型的更改直接相关。
  • Alpha2beta #185 — 修改 PlayerOrchestrator/编排逻辑,与本次新增的 waiting/onCanPlay 同步接口存在交叉点。

Poem

我把云朵裁成 .m3u8 的形状☁️
在不肯解码的风里轻跃一跳
后台悄悄转码,前台无缝接棒
指示灯眨眼,播放器安心呼吸
小兔子欢喜地按下播放键 🐇🎶

Pre-merge checks and finishing touches and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed 标题明确指出了集成基于会话的 HLS 播放流程这一主要变更,与 PR 目标及实际代码改动高度契合,描述简洁且能让阅读者快速理解新增功能方向。
Docstring Coverage ✅ Passed Docstring coverage is 88.89% which is sufficient. The required threshold is 80.00%.

📜 Recent review details

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 22ebc12 and 78a06b4.

📒 Files selected for processing (15)
  • 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/services/PlayerSettingsLoader.ts (2 hunks)
  • src/renderer/src/state/stores/player.store.ts (5 hunks)

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

📥 Commits

Reviewing files that changed from the base of the PR and between 6144fbe and d91a161.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is 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.ts
  • src/renderer/src/pages/player/components/index.ts
  • src/renderer/src/pages/player/components/VideoErrorRecovery.tsx
  • src/renderer/src/pages/player/components/HLSVideoSurface.tsx
  • src/renderer/src/pages/player/components/HLSPlayer.tsx
  • src/renderer/src/utils/file.ts
  • src/renderer/src/pages/player/components/PlayerSelector.tsx
  • src/renderer/src/services/VideoFormatDetector.ts
  • src/renderer/src/state/stores/player.store.ts
  • src/renderer/src/pages/player/components/TranscodeIndicator.tsx
  • src/renderer/src/services/CodecCompatibilityChecker.ts
  • src/renderer/src/services/PlayerSettingsLoader.ts
  • src/renderer/src/pages/player/components/VideoSurface.tsx
  • src/renderer/src/services/SessionService.ts
  • src/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.ts
  • src/renderer/src/pages/player/components/index.ts
  • src/renderer/src/pages/player/components/VideoErrorRecovery.tsx
  • src/renderer/src/pages/player/components/HLSVideoSurface.tsx
  • src/renderer/src/pages/player/components/HLSPlayer.tsx
  • src/renderer/src/utils/file.ts
  • src/renderer/src/pages/player/components/PlayerSelector.tsx
  • src/renderer/src/services/VideoFormatDetector.ts
  • src/renderer/src/state/stores/player.store.ts
  • src/renderer/src/pages/player/components/TranscodeIndicator.tsx
  • src/renderer/src/services/CodecCompatibilityChecker.ts
  • src/renderer/src/services/PlayerSettingsLoader.ts
  • src/renderer/src/pages/player/components/VideoSurface.tsx
  • src/renderer/src/services/SessionService.ts
  • src/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.ts
  • src/renderer/src/pages/player/components/index.ts
  • src/renderer/src/pages/player/components/VideoErrorRecovery.tsx
  • src/renderer/src/pages/player/components/HLSVideoSurface.tsx
  • src/renderer/src/pages/player/components/HLSPlayer.tsx
  • src/renderer/src/utils/file.ts
  • src/renderer/src/pages/player/components/PlayerSelector.tsx
  • src/renderer/src/services/VideoFormatDetector.ts
  • src/renderer/src/state/stores/player.store.ts
  • src/renderer/src/pages/player/components/TranscodeIndicator.tsx
  • src/renderer/src/services/CodecCompatibilityChecker.ts
  • src/renderer/src/services/PlayerSettingsLoader.ts
  • src/renderer/src/pages/player/components/VideoSurface.tsx
  • src/renderer/src/services/SessionService.ts
  • src/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.ts
  • src/renderer/src/pages/player/components/VideoErrorRecovery.tsx
  • src/renderer/src/pages/player/components/HLSVideoSurface.tsx
  • src/renderer/src/pages/player/components/HLSPlayer.tsx
  • src/renderer/src/pages/player/components/PlayerSelector.tsx
  • src/renderer/src/pages/player/components/TranscodeIndicator.tsx
  • src/renderer/src/pages/player/components/VideoSurface.tsx
  • src/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, run task-master generate to 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.tsx
  • src/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 工具生成的,请忽略此评论。否则,建议通过以下步骤修正:

  1. 使用适当的 Task Master CLI 命令添加/修改任务
  2. 运行 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: 新组件导出正确,与转码功能集成良好。

添加的 PlayerSelectorTranscodeIndicator 导出符合项目结构,支持新的转码和 HLS 播放流程。注意:AI 摘要中提到 VideoSurface 被移除,但实际上第 14 行仍保留了该导出,这与实际代码不一致。

.taskmaster/docs/prd.txt (1)

1-3: 文档清晰描述了转码架构和 HLS 回退策略。

文档准确说明了 JIT 转码后端的集成和运行时格式回退机制,与 PR 中的其他实现变更保持一致。

src/renderer/src/services/index.ts (1)

2-7: 服务模块导出扩展正确,支持新的转码功能。

新增的 CodecCompatibilityCheckerSessionServiceVideoFormatDetector 导出与 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 组件设计良好:

  • 正确组合 HLSPlayerSubtitleOverlayAutoResumeCountdown
  • 错误处理和事件转发实现正确
  • 接口与原 VideoSurface 兼容
  • 使用 usePlayerCommands hook 符合项目规范

Comment on lines +297 to +307
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"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

补发 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.

Comment on lines +45 to +51
<Surface
ref={surfaceRef}
role="button"
data-testid="hls-video-surface"
onClick={handleSurfaceClick}
tabIndex={0}
>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

可访问性实现合理,静态分析提示可忽略。

第 47 行使用 role="button" 配合 onClicktabIndex={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.

Suggested change
<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.

Comment on lines 151 to 167
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('视频数据停滞')
// }}
/>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

补发 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>
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d91a161 and 22ebc12.

📒 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?

mkdir700 and others added 8 commits October 8, 2025 16:58
…-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>
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

convertFileUrlToLocalPathPathConverter.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

📥 Commits

Reviewing files that changed from the base of the PR and between 22ebc12 and b4d5196.

📒 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.ts
  • src/renderer/src/pages/player/PlayerPage.tsx
  • src/renderer/src/pages/player/components/index.ts
  • src/renderer/src/pages/player/components/TranscodeLoadingIndicator.tsx
  • src/renderer/src/pages/player/engine/PlayerOrchestrator.ts
  • src/renderer/src/pages/player/components/HLSPlayer.tsx
  • src/renderer/src/pages/player/components/VideoStatusIndicator.tsx
  • src/renderer/src/pages/player/components/HLSVideoSurface.tsx
  • src/renderer/src/state/stores/player.store.ts
  • src/renderer/src/pages/player/hooks/usePlayerEngine.ts
  • src/renderer/src/pages/player/components/VolumeIndicator.tsx
  • src/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.ts
  • src/renderer/src/pages/player/PlayerPage.tsx
  • src/renderer/src/pages/player/components/index.ts
  • src/renderer/src/pages/player/components/TranscodeLoadingIndicator.tsx
  • src/renderer/src/pages/player/engine/PlayerOrchestrator.ts
  • src/renderer/src/pages/player/components/HLSPlayer.tsx
  • src/renderer/src/pages/player/components/VideoStatusIndicator.tsx
  • src/renderer/src/pages/player/components/HLSVideoSurface.tsx
  • src/renderer/src/state/stores/player.store.ts
  • src/renderer/src/pages/player/hooks/usePlayerEngine.ts
  • src/renderer/src/pages/player/components/VolumeIndicator.tsx
  • src/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.ts
  • src/renderer/src/pages/player/PlayerPage.tsx
  • src/renderer/src/pages/player/components/index.ts
  • src/renderer/src/pages/player/components/TranscodeLoadingIndicator.tsx
  • src/renderer/src/pages/player/engine/PlayerOrchestrator.ts
  • src/renderer/src/pages/player/components/HLSPlayer.tsx
  • src/main/services/MediaParserService.ts
  • src/renderer/src/pages/player/components/VideoStatusIndicator.tsx
  • src/renderer/src/pages/player/components/HLSVideoSurface.tsx
  • src/renderer/src/state/stores/player.store.ts
  • src/renderer/src/pages/player/hooks/usePlayerEngine.ts
  • src/renderer/src/pages/player/components/VolumeIndicator.tsx
  • src/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.tsx
  • src/renderer/src/pages/player/components/index.ts
  • src/renderer/src/pages/player/components/TranscodeLoadingIndicator.tsx
  • src/renderer/src/pages/player/engine/PlayerOrchestrator.ts
  • src/renderer/src/pages/player/components/HLSPlayer.tsx
  • src/renderer/src/pages/player/components/VideoStatusIndicator.tsx
  • src/renderer/src/pages/player/components/HLSVideoSurface.tsx
  • src/renderer/src/pages/player/hooks/usePlayerEngine.ts
  • src/renderer/src/pages/player/components/VolumeIndicator.tsx
  • src/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.tsx
  • src/renderer/src/state/stores/player.store.ts
  • src/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",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 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.

Suggested change
"@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.

Comment on lines +67 to +77
// 记录完整的原始结果结构以便调试
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)
})
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 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.

Suggested 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)
})
// 记录完整的原始结果结构以便调试
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.

Comment on lines +35 to +53
// 稳定的 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]
)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

基于 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

Comment on lines +25 to +26
function HLSVideoSurface({ src, onError }: HLSVideoSurfaceProps) {
const surfaceRef = useRef<HTMLDivElement>(null)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

声明的 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.

Comment on lines +69 to +80
const Surface = styled.div`
position: absolute;
inset: 0;
display: flex;
align-items: center;
justify-content: center;
background: #000;
cursor: pointer;

/* 为字幕覆盖层提供定位上下文 */
overflow: hidden;
`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 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.

Suggested change
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;
`

Comment on lines +170 to +171
color: var(--ant-color-primary, #007aff);
`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 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.

Comment on lines +17 to +26
// 监听音量变化,显示指示器并在 1 秒后自动隐藏
useEffect(() => {
setShowIndicator(true)

const timer = setTimeout(() => {
setShowIndicator(false)
}, 1000)

return () => clearTimeout(timer)
}, [volume, muted])
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 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.

Comment on lines +180 to +181
const playListUrl = `http://127.0.0.1:8799${sessionResult.playlist_url}`

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

避免硬编码 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.

Comment on lines +72 to +78
name: 'hevc',
aliases: ['h265', 'hev1', 'hvc1'],
mimeTypes: ['video/mp4; codecs="hev1"', 'video/mp4; codecs="hvc1"'],
supported: true,
category: 'advanced',
displayName: 'H.265/HEVC'
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

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.

Suggested change
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.

Comment on lines +581 to +589
switchToHlsSource: (hlsSrc: string, transcodeInfo: Partial<TranscodeInfo>) =>
set((s: Draft<PlayerStore>) => {
s.hlsMode = true
Object.assign(s.transcodeInfo, {
...transcodeInfo,
hlsSrc,
status: 'completed'
})
}),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 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.

Suggested change
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.

@mkdir700 mkdir700 merged commit cea370f into dev Oct 8, 2025
4 checks passed
@mkdir700 mkdir700 deleted the feat/hls-player branch October 8, 2025 11:30
mkdir700 added a commit that referenced this pull request Oct 11, 2025
* 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>
mkdir700 added a commit that referenced this pull request Oct 11, 2025
* 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>
github-actions bot pushed a commit that referenced this pull request Oct 11, 2025
# [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))
github-actions bot pushed a commit that referenced this pull request Oct 15, 2025
# [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))
github-actions bot pushed a commit that referenced this pull request Oct 16, 2025
# [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))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant