feat(embedded-mpv): subtitles, playback speed, aspect override, retry on error

Native addon (apps/electron-backend/native/src/embedded_mpv.mm)
- Extend SessionSnapshot with subtitleTracks, selectedSubtitleTrackId,
  playbackSpeed, aspectOverride.
- Refactor track parsing into a shared updateTracksFromNode helper that
  filters by mpv "type" so audio and subtitle tracks share the code path.
- Observe sid, speed, video-aspect-override; clear sub state on
  MPV_EVENT_START_FILE.
- Export setSubtitleTrack (handles trackId === -1 as "no" to disable),
  setSpeed (clamped to 0.25–4.0), setAspect (passthrough string for
  video-aspect-override).
- Snapshot output now includes the new fields.

Service (embedded-mpv-native.service.ts) + IPC + preload
- Mirror methods on EmbeddedMpvNativeService with capability detection: each
  method throws a descriptive error if the loaded addon doesn't expose the
  underlying native function (i.e. user is running an older build).
- New IPC channels EMBEDDED_MPV_SET_SUBTITLE_TRACK, _SET_SPEED, _SET_ASPECT
  registered in events file and exposed via preload.
- Extend EmbeddedMpvSupport with a capabilities probe so the renderer can
  hide controls for features the current addon build doesn't ship.

Renderer (embedded-mpv-player.component.{ts,html})
- Three new popovers anchored above their buttons (subtitle / speed /
  aspect), gated by capabilities() and (for subtitles) by track count.
- Subtitle popover includes an Off entry; speed/aspect use fixed presets.
- Error state now surfaces the same overlay as the stalled state, with a
  Retry button that bumps the existing retryNonce signal — covers #8 from
  the audit.
- All session-payload defaults (loading stub, error stub, refresh fallback,
  dispose payload, native createSession default) updated for the new
  required fields.

NOTE: Existing addon binaries do not expose the new methods. Until the
addon is rebuilt (pnpm run serve:backend:embedded-mpv or the release
build script), capabilities will report subtitles/playbackSpeed/
aspectOverride as false and the new buttons will simply not appear.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Entire-Checkpoint: f957cd9849e0
This commit is contained in:
4grayandClaude Opus 4.7 committed 2026-05-05 09:53:32 +02:00
1 parent dea1d40c3d
commit 8e3f559033
10 files changed
+819 -26

No files matched your search

@@ -13,10 +13,18 @@ export interface EmbeddedMpvBounds {
height: number;
}
export interface EmbeddedMpvCapabilities {
subtitles: boolean;
playbackSpeed: boolean;
aspectOverride: boolean;
screenshot: boolean;
}
export interface EmbeddedMpvSupport {
supported: boolean;
platform: string;
reason?: string;
capabilities?: EmbeddedMpvCapabilities;
}
export interface EmbeddedMpvAudioTrack {
@@ -28,6 +36,8 @@ export interface EmbeddedMpvAudioTrack {
forced?: boolean;
}
export type EmbeddedMpvSubtitleTrack = EmbeddedMpvAudioTrack;
export interface EmbeddedMpvSession {
id: string;
title: string;
@@ -38,6 +48,10 @@ export interface EmbeddedMpvSession {
volume: number;
audioTracks: EmbeddedMpvAudioTrack[];
selectedAudioTrackId: number | null;
subtitleTracks: EmbeddedMpvSubtitleTrack[];
selectedSubtitleTrackId: number | null;
playbackSpeed: number;
aspectOverride: string;
startedAt: string;
updatedAt: string;
error?: string;
@@ -46,6 +46,10 @@ export const EMBEDDED_MPV_SET_PAUSED = 'EMBEDDED_MPV_SET_PAUSED';
export const EMBEDDED_MPV_SEEK = 'EMBEDDED_MPV_SEEK';
export const EMBEDDED_MPV_SET_VOLUME = 'EMBEDDED_MPV_SET_VOLUME';
export const EMBEDDED_MPV_SET_AUDIO_TRACK = 'EMBEDDED_MPV_SET_AUDIO_TRACK';
export const EMBEDDED_MPV_SET_SUBTITLE_TRACK =
'EMBEDDED_MPV_SET_SUBTITLE_TRACK';
export const EMBEDDED_MPV_SET_SPEED = 'EMBEDDED_MPV_SET_SPEED';
export const EMBEDDED_MPV_SET_ASPECT = 'EMBEDDED_MPV_SET_ASPECT';
export const EMBEDDED_MPV_DISPOSE_SESSION = 'EMBEDDED_MPV_DISPOSE_SESSION';
export const EMBEDDED_MPV_SESSION_UPDATE = 'EMBEDDED_MPV_SESSION_UPDATE';
export const EMBEDDED_MPV_PREPARE = 'EMBEDDED_MPV_PREPARE';