- Dual mode (separate video + audio, used by adaptive streams and the iPhone EQ
copies): going to the background now makes the audio the master, so progress,
Now Playing and auto-advance keep working while iOS has the video suspended
(the paused video used to be the only source of "ended", so playback stopped
after the current song). The picture is re-synced on return.
- A track that starts while the app is in the background (auto-advance, the
lock-screen Next button) starts as audio — iOS will not start a <video> there —
and gets its picture back at the same position when the app returns.
- The handoff also runs on pagehide and when iOS pauses the master video while
the page is hidden, for when visibilitychange arrives too late.
- Classic: playlist cards place their three actions as a 2x2 block (note + add
on top, remove below) instead of the note button wrapping to the bottom-left.
- Save to this device: hide the full progress bar once the file is ready; it
read as a squashed, broken button above "Save to Photos / Files".
iOS stops Web Audio when the screen locks, so live processing can't reach a
locked phone. For saved songs, EqRender renders the same chain as the live EQ
(5 bands -> level gain -> limiter) offline into a 32 kHz stereo WAV in OPFS
(eq-renders/<id>.wav, written by the OPFS worker's new write op), one song at a
time in the background. Playback then takes the picture from the saved video
(muted) and the sound from that copy in dual mode — plain media playback, so it
continues with the screen locked; live gains are zeroed so nothing is applied
twice. Copies are re-rendered when the EQ curve or a song's loudness changes
and removed when nothing needs applying.
- Level volume is now on by default everywhere (on iPhone it reaches saved
songs through these copies).
- New iPhone-only Settings -> Playback -> Live sound processing (off by
default): live Web Audio EQ/levelling for streamed songs, stops when locked.
- The EQ dialog works on iPhone and explains where the curve applies.
Settings → Offline cache lists every saved video; it now sits in a collapsible
'Saved videos' section showing the count and total size, closed by default,
remembering how it was left.
Queue reviewable Whisper drafts from the song list and lyrics editor. Preserve line breaks within one timed cue across editing, saving, reporting, and service views.
Add storage and listening analytics with a durable metadata collector, related-search depth, video limits, thumbnail storage, and a browsable metadata library.
The CH badge by the scrubber is now a button opening a list of every chapter
(number, title, start time); the playing one is highlighted and scrolled into
view, a tap seeks there (and resumes if paused). Works with shared chapters and
YouTube's.
- frontend/wasm/loudness.c → loudness-wasm.js (1.5 KB, base64-embedded; built by
scripts/build-loudness-wasm.sh with clang or Zig's clang): K-weighting + 100 ms
energy steps. loudness.js does the 400 ms gating (abs -70 LUFS, rel -10 LU)
with an identical JS fallback. EBU Tech 3341 sine cases measure -23.0/-33.0
on both engines; a real track reads -21.7 vs ffmpeg ebur128 -21.8.
- The device measures from audio it already has (saved copy or the server
cache's m4a sidecar, songs up to 12 min, decoded natively at 22.05 kHz) and
reports the number to /api/loudness, so each song is measured once for
everyone; the server stores numbers only, no audio processing.
- The EQ graph gains a level stage (gain -> limiter) that eases each track
toward -14 LUFS (-12..+9 dB). Settings -> Playback -> Level volume: on by
default on Android/desktop, opt-in on iPhone (Web Audio stops sound when the
screen locks there).