mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-12 20:16:15 -08:00
* 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
211 lines
8.4 KiB
Markdown
211 lines
8.4 KiB
Markdown
# 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_CONTROLS` rollout 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:
|
|
|
|
```ts
|
|
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`:
|
|
|
|
1. `SettingsStore` loads `webPlayerSharedControls`, defaulting to `false`.
|
|
2. The Settings page updates and persists it through the existing Save action.
|
|
3. Leaving Settings destroys that route; no player is active during the
|
|
change.
|
|
4. The next `WebPlayerViewComponent` resolves a component-scoped boolean
|
|
snapshot from `SettingsStore`.
|
|
5. HTML5, Video.js, or ArtPlayer receives that snapshot through
|
|
`WEB_PLAYER_SHARED_CONTROLS` and constructs exactly one controls system.
|
|
6. 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`, or `artplayer`.
|
|
- 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_CONTROLS` with `true` or `false`.
|
|
- 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 `true` value 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.
|
|
- `false` preserves the existing vendor/native UI.
|
|
- `true` mounts 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 `SettingsStore` tests.
|
|
- Run the complete `ui-playback` test 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.
|