feat(audio): graphic equalizer with presets and custom bands

Adds a 10-band graphic equalizer to AudioSettings (enabled flag +
per-band dB gains, normalised to 10 entries and clamped to range).
Presets return gain curves; the settings page gains EQ UI. libmpv
applies the filter on Linux (Android parity pending). Old persisted
settings without EQ fields load as disabled + flat.

Also includes the requirements/traceability/ux-flows doc updates for
this feature and the home long-press routing (UR-058/DR-087).

TRACES: UR-027 | IR-020, DR-030 | UT-079, UT-080, UT-081, UT-082
This commit is contained in:
2026-07-24 23:49:13 +02:00
parent 589f08b873
commit c543f90ad3
9 changed files with 1454 additions and 369 deletions
+38 -3
View File
@@ -609,9 +609,12 @@ flowchart TB
```
An episode is **never** browsed as a bare `Episode` item page. Clicking an
episode anywhere navigates to `/library/<seriesId>?episode=<episodeId>`, so the
episode is always shown in the context of its series and the series' full
episode list is already loaded.
episode anywhere — a series' season list, a Home carousel (§5B.5), etc. —
navigates to `/library/<seriesId>?episode=<episodeId>`, so the episode is always
shown in the context of its series and the series' full episode list is already
loaded. Should an episode ever arrive without a `seriesId` (deep link, stale
cache), the bare Episode page renders as a fallback and links back to its parent
series and season by title so the user is never stranded.
### 5B.2 Episode Focus View — section order
@@ -695,6 +698,38 @@ The same principle as §5B.2: **episodes come before cast and similar shows.**
The reason a user opens a series page is to pick an episode; discovery content
is secondary and sits underneath.
### 5B.5 Home-card interaction — tap opens, long-press plays
Cards on the Home screen carousels (Next Movie, Next Episode, Continue
Watching, Recently Added, …) **do not play on tap.** A plain tap opens the
item; playback is the deliberate, second gesture.
| Card kind | Tap (short) | Long-press (~500 ms hold) |
|-----------|-------------|---------------------------|
| Movie | Movie detail page (`/library/<id>`) | Confirm → play now (`/player/<id>`) |
| Episode | Series Episode Focus View (`/library/<seriesId>?episode=<id>`, per §5B.1) | Confirm → play now (`/player/<id>`) |
| Series / Season / Album / Artist / Playlist / Folder | Detail page (`/library/<id>`) | Same as tap (no single "play now" target) |
| Channel / live leaf | Player (`/player/<id>`) — no detail page exists | Confirm → play now |
Rationale and rules:
- **Tap is navigation, not commitment.** Previously a tap on a movie/episode
jumped straight into the player, which made it easy to lose your place in a
half-watched item or start a stream you only meant to inspect. Tap now lands
on the detail/focus page, where Play is an explicit button.
- **Long-press is the shortcut for "just play it."** It surfaces a native
confirm (`Play "<name>" now?`) before starting playback, so an accidental
hold never blows away a resume position silently.
- **The long-press must not fight the carousel.** Detection cancels if the
pointer moves more than ~10 px (a horizontal scroll of the row), so holding
to scroll never triggers play.
- **Episodes still obey §5B.1** — a home tap on an episode opens the series
Focus View, never a bare Episode page, so the series context loads.
This behavior lives in `MediaCard` (`onLongPress` prop + pointer-based
detection) so any surface can opt in; today the Home carousels are the only
opt-in. Grids and other surfaces keep tap-to-open with no long-press.
---
## 6. Search Flow