* docs(playback): design shared controls setting * docs(playback): plan shared controls setting * feat(settings): persist shared web controls preference * test(settings): harden shared controls normalization coverage * feat(settings): expose shared web controls toggle * fix(settings): label shared controls toggle * feat(playback): resolve shared controls from settings * test(playback): cover shared controls setting * docs(playback): document shared controls preference * fix(playback): await settings before host creation * fix(settings): normalize shared controls updates
8.4 KiB
Web-Player Shared Controls Setting Design
Context
The shared app-player-controls presentation is already integrated with four
playback paths:
- Embedded MPV frame-copy uses it unconditionally because its video is rendered into a DOM canvas.
- HTML5, Video.js, and ArtPlayer can use it behind the default-off
WEB_PLAYER_SHARED_CONTROLSrollout token.
The web-player token currently resolves from the compile-time
WEB_PLAYER_SHARED_CONTROLS_ENABLED = false constant. Enabling the integrations
therefore requires a code change, rebuild, and application restart. The
integrations are ready for opt-in testing, so the rollout choice should be a
persisted user setting instead.
The Settings route does not keep an active player mounted. The setting can therefore be applied when the next playback session is created; changing an already-running engine or preserving an active playback position is not required.
Goals
- Add an experimental Playback checkbox for opting HTML5, Video.js, and ArtPlayer into the shared controls.
- Persist the choice in the canonical
SettingsStore. - Apply the saved choice to the next web-player session without restarting or reloading the application.
- Keep the controls mode immutable for the lifetime of a player session.
- Preserve default-off behavior and compatibility with existing stored settings.
- Keep Embedded MPV and external-player behavior unchanged.
Non-goals
- Make shared web controls the default.
- Change an active player between legacy and shared controls.
- Add separate preferences for HTML5, Video.js, and ArtPlayer.
- Make Embedded MPV native-view use a DOM overlay.
- Allow frame-copy Embedded MPV to fall back to the legacy controls dock.
- Control the native UI of external MPV or VLC processes.
Embedded MPV Semantics
The new checkbox is intentionally web-player-specific.
Embedded MPV has two rendering paths:
- Frame-copy: video is uploaded to a renderer canvas. It always uses
app-player-controls; a second controls toggle would only duplicate an already-required part of this engine. - Native-view: video is hosted in a platform-native surface. DOM controls cannot reliably stack above that surface, so the compositor-safe legacy dock remains required.
The existing embeddedMpvFrameCopy setting continues to select the rendering
engine and continues to require an application restart. It is independent from
the new web-player controls preference.
External MPV and VLC processes continue to own their own controls UI.
Considered Approaches
1. Conditional global web-player preference
Selected.
One setting controls HTML5, Video.js, and ArtPlayer together. Its checkbox is shown only while one of those players is selected. This matches the existing single rollout token, keeps the Settings UI relevant to the selected player, and avoids suggesting that the option changes Embedded MPV or external players.
2. Always-visible "shared controls where available" preference
Rejected because it would be a no-op for frame-copy Embedded MPV, unsupported for native-view, and outside the app for external players. The resulting conditional semantics would be harder to explain than a web-player-specific label.
3. Separate preference for every playback engine
Rejected because the three web integrations deliberately share one rollout contract. Per-engine preferences would add settings and test combinations without a current product requirement.
Settings Contract
Add an optional boolean to the shared settings interface:
webPlayerSharedControls?: boolean;
The canonical default is false. Loading an older settings object without the
field must normalize to false, and saving settings must write the complete
resolved value back to storage.
The settings form adds a webPlayerSharedControls control with a false
default. Form hydration, serialization, reset behavior, backup/export, and the
existing Save workflow continue to use the normal complete Settings object.
This is renderer-only state; Electron main-process behavior and preload IPC do
not need a new command.
Runtime Data Flow
The existing WEB_PLAYER_SHARED_CONTROLS injection token remains the
immutable, test-overridable session seam used by the three engine components.
The compile-time constant remains only as the default-off fallback for direct
component use and focused tests.
Normal runtime resolution moves to WebPlayerViewComponent:
SettingsStoreloadswebPlayerSharedControls, defaulting tofalse.- The Settings page updates and persists it through the existing Save action.
- Leaving Settings destroys that route; no player is active during the change.
- The next
WebPlayerViewComponentresolves a component-scoped boolean snapshot fromSettingsStore. - HTML5, Video.js, or ArtPlayer receives that snapshot through
WEB_PLAYER_SHARED_CONTROLSand constructs exactly one controls system. - The snapshot does not change until that player host is destroyed and a new playback session is created.
This preserves the construction-time requirements of Video.js and ArtPlayer: vendor controls, hotkeys, gestures, plugins, source bridges, and the shared adapter are configured atomically. A reactive boolean must not flip only the template while leaving the engine configured for the other mode.
Embedded MPV does not consume the setting. Its component continues to choose
shared controls for engine === 'frame-copy' and the legacy dock for the
native-view engine.
Settings UI
Add one standard setting-item to the Playback section, using the existing
Material checkbox and layout styles.
- Label meaning: Use unified controls for web players (experimental).
- Description meaning: use IPTVnator's common controls in HTML5, Video.js, and ArtPlayer.
- Visibility: selected player is
videojs,html5, orartplayer. - Hidden for Embedded MPV, external MPV, and VLC.
- Add a stable test selector to the row and checkbox.
- Add the label and description keys to every locale file so the i18n key sets remain aligned.
No new colors, spacing rules, or one-off UI components are required.
Failure and Compatibility Behavior
- Missing or malformed non-boolean stored values resolve to
false. - A storage write failure is reported through the existing
SettingsStore.updateSettings()rejection and must not require player-specific recovery. - Legacy mode remains the safe fallback whenever the setting cannot be resolved.
- Direct engine tests can continue overriding
WEB_PLAYER_SHARED_CONTROLSwithtrueorfalse. - The setting does not relax Electron sandboxing and does not alter the frame-copy engine opt-in.
Testing Strategy
Shared interfaces and settings store
- Default is
false. - Older stored settings without the field load as
false. - A stored
truevalue is restored. updateSettings()persists the complete resolved value.
Settings form and UI
- Form creation and serialization include the new field.
- The checkbox hydrates from stored settings and is included in the Save payload.
- The row is visible for HTML5, Video.js, and ArtPlayer.
- The row is hidden for Embedded MPV, external MPV, and VLC.
- Translation key parity remains valid across all locales.
Playback host
- A newly created HTML5, Video.js, or ArtPlayer session receives the saved boolean snapshot.
falsepreserves the existing vendor/native UI.truemounts the shared controls path.- Embedded MPV selection ignores the web-player preference.
- One session never renders or initializes both controls systems.
Validation
- Run focused Settings form/component and
SettingsStoretests. - Run the complete
ui-playbacktest target because all three web engines share the rollout seam. - Run affected lint targets and the workspace typecheck.
- Run the settings E2E flow for checkbox visibility, persistence, and use after returning to playback.
- Run the closest Electron playback smoke/E2E coverage because the same Settings surface is used in the desktop app.
Documentation Impact
Update docs/architecture/player-controls-contract.md and the shared-controls
sections of AGENTS.md and CLAUDE.md to replace the compile-time-only
default-off rollout description with the persisted experimental preference.
The Embedded MPV architecture documentation only needs clarification if its
existing distinction between frame-copy shared controls and the native-view
legacy dock would otherwise become ambiguous.