Skip to content

feat(embedded-mpv): adopt shared controls, immersive overlay on macOS [3/7]#5

Closed
larsemig wants to merge 1 commit into
pr/02-embedded-mpv-decomposefrom
pr/03-embedded-mpv-immersive
Closed

feat(embedded-mpv): adopt shared controls, immersive overlay on macOS [3/7]#5
larsemig wants to merge 1 commit into
pr/02-embedded-mpv-decomposefrom
pr/03-embedded-mpv-immersive

Conversation

@larsemig

@larsemig larsemig commented Jul 7, 2026

Copy link
Copy Markdown
Owner

Note

Sidenote from the author: My review focus for this series was the architecture, not every implementation detail. If there are specific contribution guidelines I should follow (commit style, PR conventions, test expectations, etc.), please let me know and I will adapt the series accordingly.

Step 3 of the 7-PR series implementing the direction approved in 4gray#1105 — the core of the proposal.

What

The embedded-MPV player adopts the shared controls layer and becomes an immersive overlay on macOS:

  • Native compositing flip (embedded_mpv.mm): the video surface moves from NSWindowAbove to NSWindowBelow the WebContents.
  • Transparent main window (macOS, app.ts): the web layer composites on top of the native video; the app stays opaque via a single global backdrop component (app-embedded-mpv-immersive-backdrop) that paints an opaque field with one transparent hole at the measured video rect — no per-route panel repainting.
  • EmbeddedMpvControlsAdapter: the embedded player now drives the shared app-player-controls (step 1) over the decomposed session controller (step 2). Localized via the existing i18n keys.
  • Old vendor controls deleted: component shrinks ~826 → ~150 lines; format.utils, overlay-visibility, shortcuts, ui-state modules removed (superseded by the shared layer).
  • DOM fullscreen with chrome-hide body classes (native fullscreen follows in step 4).

Why atomically coupled

NSWindowBelow breaks the old docked component (video would sit below an opaque web layer), so the compositing switch, the transparent window + backdrop, the shared-controls adoption, and the old-controls deletion must ship together. Nothing here is separable without breaking the compositing invariant.

⚠️ Risks / gating / rollback — please read

  • The macOS main window becomes transparent: true and drops titleBarOverlay for ALL macOS users, regardless of the IPTVNATOR_ENABLE_EMBEDDED_MPV_EXPERIMENT flag — transparent is fixed at window creation and cannot be toggled at runtime. This is the trade-off approved in Proposal: unified player-controls architecture (docs only, for direction) 4gray/iptvnator#1105 (titleBarOverlay was unused; native traffic lights remain). Documented in code comments and player-controls-refactor.md §7.
  • Windows/Linux unaffected: they use the --wid path and keep opaque windows; all transparency logic is macOS-only.
  • A rendering bug could expose the desktop through the app — the backdrop architecture exists precisely to make transparency owned by one service; see the decision log in docs/architecture/player-controls-refactor.md.
  • Needs on-device macOS verification (hole/video congruence, traffic lights, controls reveal) — not coverable by unit tests.
  • Rollback = clean single-PR revert (self-contained; no new IPC surface — that lands in step 4).

Net −1286 lines. Lint + ui-playback/web/electron-backend suites green.

🗺️ Series roadmap (proposal 4gray#1105)

Step Branch Purpose
1 pr/01-player-controls-core Shared engine-agnostic controls layer (lib only, flag OFF)
2 pr/02-embedded-mpv-decompose Decompose embedded-MPV session controller (no behavior change)
➡️ 3 pr/03-embedded-mpv-immersive Embedded MPV adopts shared controls + immersive overlay (macOS)
4 pr/04-embedded-mpv-native-fullscreen Real macOS native fullscreen for embedded MPV
5 pr/05-html5-shared-controls HTML5 player on shared controls (flag OFF)
6 pr/06-vjs-shared-controls Video.js on shared controls (flag OFF)
7 pr/07-art-player-shared-controls ArtPlayer on shared controls (flag OFF)

Stacked series: each PR is based on the previous one, so every diff shows only its own step.

🤖 Generated with Claude Code

@larsemig
larsemig force-pushed the pr/02-embedded-mpv-decompose branch from 2b1f5e5 to b73b54f Compare July 7, 2026 19:00
@larsemig
larsemig force-pushed the pr/03-embedded-mpv-immersive branch from d16774c to 6b7cb92 Compare July 7, 2026 19:00
The embedded-MPV player drops its bespoke vendor controls and binds the
shared app-player-controls layer through EmbeddedMpvControlsAdapter, a
PlayerController implementation over the decomposed session controller
(command runner, session factory, stalled tracker). Label/format helpers
move to embedded-mpv-labels (TranslateService-backed, existing i18n keys);
the old embedded-mpv-format.utils, embedded-mpv-shortcuts,
embedded-mpv-ui-state, and embedded-mpv-overlay-visibility.service are
deleted.

Immersive compositing (macOS only; the player itself stays gated behind
the existing IPTVNATOR_ENABLE_EMBEDDED_MPV_EXPERIMENT env flag): the
native libmpv surface is inserted BELOW the WebContents sibling
(NSWindowBelow) and the main BrowserWindow becomes transparent
(transparent: true, backgroundColor '#00000000'), so the web layer opens
a transparency tunnel over the video rect while
EmbeddedMpvImmersiveService + the global immersive backdrop keep the rest
of the app opaque (one opaque field with a transparent hole at the
measured video rect). DOM controls, dialogs, and popovers float over
full-bleed video with no docked strip and no off-screen surface tricks.

Trade-off: titleBarOverlay is dropped on macOS because the web-drawn
window-controls overlay does not compose with a transparent window there;
native traffic lights still render via titleBarStyle 'hidden'.
handleRendererNavigation/getPackagedRendererIndexPath become
public/exported and window-state events broadcast on all platforms to
support the transparent-window path.

Test coverage extended per Codecov patch report.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@larsemig
larsemig force-pushed the pr/03-embedded-mpv-immersive branch from 6b7cb92 to 6a2c1c0 Compare July 7, 2026 19:56
@larsemig
larsemig force-pushed the pr/02-embedded-mpv-decompose branch from b73b54f to 2f9a144 Compare July 7, 2026 19:56
@larsemig

larsemig commented Jul 7, 2026

Copy link
Copy Markdown
Owner Author

Used as template only

@larsemig larsemig closed this Jul 7, 2026
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