docs(playback): document shared controls preference

This commit is contained in:
4gray committed 2026-07-17 08:50:17 +02:00
1 parent c51f8b89c2
commit 96a2c2e33f
4 files changed
+62 -46

No files matched your search

+19 -13
View File
@@ -132,13 +132,19 @@ Key files:
- `libs/ui/playback/src/lib/player-controls/` contains the additive,
engine-neutral `PlayerController` contract, standalone
`app-player-controls`, generic web-video adapter/helper, and default-off web
rollout token.
- Embedded MPV frame-copy is the first runtime consumer. Its component-scoped
`EmbeddedMpvControlsAdapter` maps the active session into
`app-player-controls`; the native-view engine retains the legacy
compositor-safe dock. The host must render exactly one controls system for
the reported engine.
`app-player-controls`, generic web-video adapter/helper, and component-scoped
`WEB_PLAYER_SHARED_CONTROLS` rollout token.
- Persisted `Settings.webPlayerSharedControls` is default-off, and its checkbox
appears only when HTML5, Video.js, or ArtPlayer is selected.
`WebPlayerViewComponent` snapshots the preference into
`WEB_PLAYER_SHARED_CONTROLS` for each new player host. Saving applies to the
next host without an application restart; an existing session never changes
controls mode in place.
- Embedded MPV ignores the web-player preference. Frame-copy always uses shared
DOM controls through its component-scoped `EmbeddedMpvControlsAdapter`, while
native-view retains the legacy compositor-safe dock and external MPV/VLC
retain their own UI. The host must render exactly one controls system for the
reported Embedded MPV engine.
- Frame-copy shared controls own DOM surface interactions, shortcuts,
fullscreen, and recording feedback. `showControls=false` detaches the shared
surface, modal overlays gate playback shortcuts, fullscreen still triggers
@@ -158,8 +164,8 @@ Key files:
`WebPlayerViewComponent.resolvedIsLive` supplies authoritative live/VOD
metadata, while a visible playback diagnostic disables both shared surface
interaction and shortcuts and exits the HTML5 shell's own fullscreen so the
diagnostic actions remain visible. The flag-off path keeps native controls
and legacy series navigation unchanged.
diagnostic actions remain visible. The preference-off path keeps native
controls and legacy series navigation unchanged.
- Video.js is the third guarded consumer. `VjsPlayerComponent` provides a
component-scoped `WebVideoControlsAdapter`; its bridge binds the current Tech
video, rebinds after `playerreset`, exposes source-stable audio/subtitle IDs,
@@ -167,10 +173,10 @@ Key files:
duration from Video.js. Reset-driven raw MPEG-TS changes pause first,
coalesce to the latest desired source, preserve actual volume across
Video.js's reset, and restart when authoritative live/VOD metadata changes.
The flag-on path disables native controls, Video.js
The shared-controls path disables native controls, Video.js
click/double-click/hotkey actions, and spatial navigation;
diagnostic gating and owned-fullscreen exit match HTML5. The flag-off path
keeps the existing Video.js skin and legacy series navigation unchanged.
diagnostic gating and owned-fullscreen exit match HTML5. The preference-off
path keeps the existing Video.js skin and legacy series navigation unchanged.
- ArtPlayer is the fourth guarded consumer. `ArtPlayerComponent` provides a
component-scoped `WebVideoControlsAdapter`; `ArtPlayerSourceSession` owns
HLS/MPEG-TS/native sources, the neutral web-video bridge, exact cleanup, and
@@ -181,7 +187,7 @@ Key files:
ArtPlayer restores its own stored volume. Vendor chrome/hotkeys are disabled,
and a transparent capture layer gives shared controls exclusive click and
double-click ownership. Diagnostic interaction gating and owned-fullscreen
exit match the other web players. The default-off flag keeps the legacy
exit match the other web players. The preference-off path keeps the legacy
ArtPlayer skin, source behavior, and series navigation unchanged.
- Canonical docs: `docs/architecture/player-controls-contract.md` and
`docs/architecture/embedded-mpv-native.md`
+1 -1
View File
@@ -618,7 +618,7 @@ This project uses modern Angular signal-based APIs and patterns. **ALWAYS** use
- External players: MPV, VLC (via IPC to Electron backend)
- Embedded MPV (experimental, macOS/Windows/Linux): renders mpv video inside the Electron window through a native addon. macOS uses the libmpv render API in an `NSOpenGLView`; Windows uses in-process libmpv with `--wid` against an app-owned child `HWND`; Linux spawns an out-of-process `mpv --wid=<x11-window>` controlled over a JSON IPC socket (X11/XWayland only, requires system `mpv` on PATH; subtitles/speed/aspect/recording are not exported there). mpv's own screensaver inhibition does not apply to any of these paths, so `EmbeddedMpvNativeService` holds an Electron `powerSaveBlocker` (`prevent-display-sleep`) whenever any session's status is `playing`, and releases it on pause, dispose, or shutdown. Service: `apps/electron-backend/src/app/services/embedded-mpv-native.service.ts`; full architecture: `docs/architecture/embedded-mpv-native.md`.
- Embedded MPV frame-copy engine (experimental, macOS Apple Silicon + Linux + Windows; enabled via `Settings > Playback > Embedded MPV: frame-copy engine` (restart required) or `IPTVNATOR_ENABLE_EMBEDDED_MPV_FRAME_COPY=1` on top of the embedded MPV experiment flag): a per-session helper renders mpv offscreen at viewport size (headless CGL on macOS, headless EGL on Linux, WGL against a hidden window on Windows) and publishes BGRA frames into a shm ring (POSIX shm; a `Local\` named file mapping on Windows); the preload frame pump uploads them onto a renderer `<canvas data-embedded-mpv-frame>`, so controls/dialogs are ordinary DOM above the video. Frame-copy is the first runtime consumer of shared `app-player-controls`: `PlayerControlsComponent` and its surface/shortcut/fullscreen collaborators own the DOM UI interactions, while the component-scoped `EmbeddedMpvControlsAdapter` maps session state and commands and coordinates correlated recording state; native-view retains the legacy fixed dock. Stored and explicit opt-ins relax the sandbox only while the base embedded-MPV feature is enabled and a platform-supported packaged runtime contains both the regular-file helper (`iptvnator_mpv_helper` / `.exe`) and readable regular frame-reader addon; packaged discovery is restricted to packaged resources. A disabled base experiment keeps embedded MPV unavailable with the sandbox intact, while a missing, mode-stripped, or incomplete frame-copy runtime falls back to the native engine without relaxing the sandbox. On Linux the engine is dev-build-only for now: the helper links system libmpv (build deps: `libmpv-dev`, `libegl-dev`, `libgl-dev`, `libopengl-dev`, `libgbm-dev`) and is stripped from packages until bundled-runtime staging lands. On Windows the helper links vendored libmpv and package validation requires the exact MPV DLL named in the helper's PE import table beside the executable. Backend process adapter: `apps/electron-backend/src/app/services/embedded-mpv-frame-copy.adapter.ts`; shared-controls adapter: `libs/ui/playback/src/lib/embedded-mpv-player/embedded-mpv-controls.adapter.ts`; helper: `apps/electron-backend/native/helper/`; details in `docs/architecture/embedded-mpv-native.md` ("Frame-Copy Engine").
- Shared player-controls layer: `libs/ui/playback/src/lib/player-controls/` exports the engine-neutral `PlayerController` contract, standalone `app-player-controls`, a generic web-video adapter/helper, and a default-off web rollout token. Embedded MPV frame-copy consumes it through `EmbeddedMpvControlsAdapter`; the host selects exactly one UI, so native-view retains its compositor-safe dock. `showControls=false` detaches the shared surface, modal overlays gate frame-copy playback shortcuts, fullscreen remains DOM-based with Embedded MPV bounds sync, and a playback/session transition key prevents engine or session handoff from presenting stale recording feedback while timers and pending commands are cancelled. Same-session IPC replies yield to a broadcast snapshot received while the command was pending, so a successful recording acknowledgement cannot be rolled back by a stale reply. The built-in HTML5/hls.js player is the second guarded consumer: `HtmlVideoPlayerComponent` provides a component-scoped `WebVideoControlsAdapter`, while its neutral `web-video-support` bridge is shared with ArtPlayer and owns HLS/native tracks, MPEG-TS VOD duration correction, caption preference, and source cleanup. `HtmlVideoElementSession` owns native video-event lifecycle, persisted volume, start-time/time/ended propagation, and legacy post-play caption suppression. Video.js is the third guarded consumer: `VjsPlayerComponent` provides a component-scoped `WebVideoControlsAdapter`; its bridge rebinds the current Tech video after `playerreset`, exposes source-stable audio/subtitle IDs, preserves caption preference and explicit subtitle-off state, and reads Video.js duration. Reset-driven raw MPEG-TS changes pause first, coalesce to the latest desired source, preserve actual volume across Video.js's reset, and restart when authoritative live/VOD metadata changes. With the flag on, Video.js native controls, click/double-click/hotkey actions, and spatial navigation are disabled. ArtPlayer is the fourth guarded consumer: `ArtPlayerComponent` provides a component-scoped `WebVideoControlsAdapter`; `ArtPlayerSourceSession` owns HLS/MPEG-TS/native sources, the neutral web-video bridge, exact cleanup, and a destroyed-session guard for delayed `customType` callbacks, while `ArtPlayerVideoSession` owns native media/ArtPlayer events. Shared ArtPlayer mode uses authoritative live/VOD metadata, HLS/native tracks and caption preference, MPEG-TS VOD duration correction, and reapplies app volume directly after ArtPlayer restores its own stored volume. Vendor chrome/hotkeys are disabled, and a transparent capture layer gives shared controls exclusive click and double-click ownership. `WebPlayerViewComponent.resolvedIsLive` supplies authoritative metadata; visible playback diagnostics disable shared pointer/keyboard ownership and exit only the active HTML5, Video.js, or ArtPlayer shell's own fullscreen so retry/fallback actions remain visible. The rollout flag remains default-off, and all three web players retain their existing controls, source behavior, and legacy series navigation on that path. Contract: `docs/architecture/player-controls-contract.md`.
- Shared player-controls layer: `libs/ui/playback/src/lib/player-controls/` exports the engine-neutral `PlayerController` contract, standalone `app-player-controls`, a generic web-video adapter/helper, and component-scoped `WEB_PLAYER_SHARED_CONTROLS` rollout token. Persisted `Settings.webPlayerSharedControls` is default-off, and its checkbox appears only when HTML5, Video.js, or ArtPlayer is selected. `WebPlayerViewComponent` snapshots the preference into the immutable token for each new player host, so saving applies to the next host without an application restart; an existing session never changes controls mode in place. Embedded MPV ignores the web-player preference: frame-copy always uses shared DOM controls through `EmbeddedMpvControlsAdapter`, native-view retains its compositor-safe legacy dock, and external MPV/VLC retain their own UI. The Embedded MPV host selects exactly one controls UI for its reported engine. `showControls=false` detaches the shared surface, modal overlays gate frame-copy playback shortcuts, fullscreen remains DOM-based with Embedded MPV bounds sync, and a playback/session transition key prevents engine or session handoff from presenting stale recording feedback while timers and pending commands are cancelled. Same-session IPC replies yield to a broadcast snapshot received while the command was pending, so a successful recording acknowledgement cannot be rolled back by a stale reply. The built-in HTML5/hls.js player is the second guarded consumer: `HtmlVideoPlayerComponent` provides a component-scoped `WebVideoControlsAdapter`, while its neutral `web-video-support` bridge is shared with ArtPlayer and owns HLS/native tracks, MPEG-TS VOD duration correction, caption preference, and source cleanup. `HtmlVideoElementSession` owns native video-event lifecycle, persisted volume, start-time/time/ended propagation, and legacy post-play caption suppression. Video.js is the third guarded consumer: `VjsPlayerComponent` provides a component-scoped `WebVideoControlsAdapter`; its bridge rebinds the current Tech video after `playerreset`, exposes source-stable audio/subtitle IDs, preserves caption preference and explicit subtitle-off state, and reads Video.js duration. Reset-driven raw MPEG-TS changes pause first, coalesce to the latest desired source, preserve actual volume across Video.js's reset, and restart when authoritative live/VOD metadata changes. In shared-controls mode, Video.js native controls, click/double-click/hotkey actions, and spatial navigation are disabled. ArtPlayer is the fourth guarded consumer: `ArtPlayerComponent` provides a component-scoped `WebVideoControlsAdapter`; `ArtPlayerSourceSession` owns HLS/MPEG-TS/native sources, the neutral web-video bridge, exact cleanup, and a destroyed-session guard for delayed `customType` callbacks, while `ArtPlayerVideoSession` owns native media/ArtPlayer events. Shared ArtPlayer mode uses authoritative live/VOD metadata, HLS/native tracks and caption preference, MPEG-TS VOD duration correction, and reapplies app volume directly after ArtPlayer restores its own stored volume. Vendor chrome/hotkeys are disabled, and a transparent capture layer gives shared controls exclusive click and double-click ownership. `WebPlayerViewComponent.resolvedIsLive` supplies authoritative metadata; visible playback diagnostics disable shared pointer/keyboard ownership and exit only the active HTML5, Video.js, or ArtPlayer shell's own fullscreen so retry/fallback actions remain visible. On the preference-off path, all three web players retain their existing controls, source behavior, and legacy series navigation. Contract: `docs/architecture/player-controls-contract.md`.
**VOD/Series Detail Pages (two-state layout)**:
+1
View File
@@ -33,6 +33,7 @@ The application is a cross-platform, open-source project built with Electron and
**Playback**
- Built-in HTML5 player (HLS.js or Video.js) with a resizable, resumable inline view
- Optional unified IPTVnator controls for HTML5, Video.js, and ArtPlayer, enabled in **Settings → Playback** _(experimental)_
- External players — MPV, VLC, and IINA on macOS (`mpv.app` / `VLC.app` bundle paths supported) _(desktop)_
- Embedded MPV — native mpv rendered inside the app window on macOS, Windows & Linux 🖥️ _(experimental · desktop)_
- Dedicated radio player for `radio="true"` streams 📻
+41 -32
View File
@@ -14,14 +14,15 @@ consumers and includes:
- the standalone `app-player-controls` presentation component and its
transient-state collaborators;
- a generic `WebVideoControlsAdapter` plus small host helpers;
- a default-off web rollout token;
- a persisted, default-off web-player preference resolved through an immutable
per-host rollout token;
- the component-scoped `EmbeddedMpvControlsAdapter`;
- an `EmbeddedMpvPlayerComponent` host integration for the frame-copy engine;
- a feature-flagged `HtmlVideoPlayerComponent` integration backed by
- a preference-guarded `HtmlVideoPlayerComponent` integration backed by
`WebVideoControlsAdapter` and a player-local engine bridge;
- a feature-flagged `VjsPlayerComponent` integration backed by a
- a preference-guarded `VjsPlayerComponent` integration backed by a
component-scoped `WebVideoControlsAdapter` and Video.js bridge;
- a feature-flagged `ArtPlayerComponent` integration backed by a
- a preference-guarded `ArtPlayerComponent` integration backed by a
component-scoped `WebVideoControlsAdapter`, neutral web-video source bridge,
and player-local source/video sessions; and
- focused unit/component tests.
@@ -36,14 +37,14 @@ When `WEB_PLAYER_SHARED_CONTROLS` is enabled, the built-in HTML5 player mounts
the same presentation component over its real player shell and disables the
native video controls. Its neutral source bridge supplies HLS/native tracks,
corrected MPEG-TS VOD duration, and authoritative live/VOD metadata to the
generic web adapter. When the flag is disabled, the native controls and legacy
series navigation remain unchanged and the adapter is not attached.
generic web adapter. When the host token resolves to false, the native controls
and legacy series navigation remain unchanged and the adapter is not attached.
Video.js consumes the same token and shared presentation atomically. Its bridge
binds the adapter to the current Video.js Tech `<video>` and rebinds after
`playerreset`, while focused collaborators expose Video.js audio/text tracks
and manage raw MPEG-TS playback. With the flag disabled, Video.js keeps its
existing skin and legacy series navigation.
and manage raw MPEG-TS playback. When the host token resolves to false, Video.js
keeps its existing skin and legacy series navigation.
ArtPlayer is the fourth consumer. Its source session owns HLS, MPEG-TS, native
source selection, and delayed `customType` callbacks, while the neutral
@@ -56,17 +57,19 @@ event-capture layer over ArtPlayer so shared controls exclusively own surface
clicks and double-clicks. Playback diagnostics gate shared interaction and exit
only the ArtPlayer shell's own fullscreen. Source replacement and teardown
remove exact listeners and engines, and destroyed sessions ignore stale delayed
`customType` callbacks. With the flag disabled, the existing ArtPlayer skin,
source behavior, and legacy series navigation remain unchanged.
`customType` callbacks. When the host token resolves to false, the existing
ArtPlayer skin, source behavior, and legacy series navigation remain unchanged.
`WEB_PLAYER_SHARED_CONTROLS_ENABLED` remains default-off, so none of the three
guarded web integrations changes normal runtime behavior.
`Settings.webPlayerSharedControls` remains default-off. `WebPlayerViewComponent`
snapshots it into `WEB_PLAYER_SHARED_CONTROLS` when a new player host is
created, so HTML5, Video.js, and ArtPlayer switch atomically without an
application restart. Existing sessions never change controls mode in place.
This rollout is intentionally engine-selective: frame-copy can use normal DOM
layering, while the native platform view cannot. The integration also includes
a recording coordinator that correlates asynchronous snapshots with the active
playback/session owner, serializes toggles, and cancels pending ownership when
the session, playback, engine, or component changes.
The shared-controls architecture remains engine-selective: frame-copy can use
normal DOM layering, while the native platform view cannot. The integration
also includes a recording coordinator that correlates asynchronous snapshots
with the active playback/session owner, serializes toggles, and cancels pending
ownership when the session, playback, engine, or component changes.
## Why this exists
@@ -111,10 +114,10 @@ contract does not make a native video surface behave like DOM content.
│ │
▼ ▼
┌──────────────────────────────┐ ┌──────────────────────────────┐
│ Flag-guarded web hosts │ │ EmbeddedMpvPlayerComponent │
│ Per-host preference snapshot │ │ EmbeddedMpvPlayerComponent │
│ HTML5 + Video.js + ArtPlayer │ │ frame-copy: shared controls │
│ flag on: shared controls │ │ native-view: legacy dock │
│ flag off: existing controls │ └──────────────────────────────┘
│ true: shared controls │ │ native-view: legacy dock │
│ false: existing controls │ └──────────────────────────────┘
└──────────────────────────────┘
```
@@ -122,7 +125,7 @@ The embedded host selects controls from the reported engine before rendering
them. It never mounts the shared overlay and legacy dock together.
The HTML5, Video.js, and ArtPlayer hosts likewise select their existing or
shared controls before rendering and never attach the web adapter while the
flag-off path is active.
preference-off path is active.
## The contract
@@ -375,12 +378,13 @@ attachment/projection helpers. Video.js uses its dedicated bridge directly.
HTML5 and ArtPlayer share the neutral source bridge and HLS/native-track
collaborators under `web-video-support/`.
The rollout symbols are:
The rollout symbols and setting are:
| Symbol | Default | Current effect |
| ------------------------------------ | ------------: | -------------------------------------------------------------------------------------------------------------------------------- |
| `WEB_PLAYER_SHARED_CONTROLS_ENABLED` | `false` | Keeps existing web-player skins active in normal runtime builds. |
| `WEB_PLAYER_SHARED_CONTROLS` | default above | Injectable/test-overridable view consumed by HTML5, Video.js, and ArtPlayer to switch atomically between existing and shared UI. |
| Symbol / setting | Default | Current effect |
| ------------------------------------ | ---------------: | ----------------------------------------------------------------------- |
| `Settings.webPlayerSharedControls` | `false` | Persisted experimental opt-in shown for HTML5, Video.js, and ArtPlayer. |
| `WEB_PLAYER_SHARED_CONTROLS_ENABLED` | `false` | Default-off fallback for direct component use and focused tests. |
| `WEB_PLAYER_SHARED_CONTROLS` | session snapshot | Component-scoped immutable value consumed by the three web engines. |
With the token enabled, Video.js also disables native controls, Video.js
single-click and double-click actions, Video.js hotkeys, and spatial navigation.
@@ -403,6 +407,10 @@ semantics, stored volume behavior, and series navigation remain unchanged.
The shared contract does not replace either Embedded MPV renderer. The host
uses the renderer's reported engine to choose the compatible controls UI.
The web-player preference does not affect Embedded MPV. Frame-copy always uses
the shared DOM controls, while native-view keeps its compositor-safe legacy
dock.
### Frame-copy engine
The experimental frame-copy engine uploads helper-produced frames to
@@ -528,8 +536,8 @@ libs/ui/playback/src/lib/html-video-player/
`WebVideoControlsAdapter`. Its bridge/helper filenames re-export the neutral
web-video support so existing imports and focused specs remain stable.
`HtmlVideoElementSession` separately owns native video-event attachment,
persisted volume, start-time/time/ended propagation, and the flag-off post-play
caption behavior.
persisted volume, start-time/time/ended propagation, and the preference-off
post-play caption behavior.
The guarded Video.js integration lives in:
@@ -571,11 +579,12 @@ libs/ui/playback/src/lib/art-player/
bridge, exact engine/listener cleanup, and a destroyed-session guard for
ArtPlayer's delayed `customType` dispatch. `ArtPlayerVideoSession` owns native
media errors, readiness, volume persistence, ended/time updates, and exact
event cleanup. The setup helper preserves the legacy option set when the flag
is off and disables vendor interaction owners when it is on; the component's
transparent capture layer blocks ArtPlayer's core surface handlers.
event cleanup. The setup helper preserves the legacy option set when the host
token resolves to false and disables vendor interaction owners when it resolves
to true; the component's transparent capture layer blocks ArtPlayer's core
surface handlers.
Focused specs cover each web engine's flag-off compatibility path,
Focused specs cover each web engine's preference-off compatibility path,
shared-controls rendering and diagnostic interaction gating, source/element
replacement, track-list lifecycle and stable IDs, caption preference and
explicit-off behavior, MPEG-TS live/VOD handling and duration projection,