feat(playback): stream buffering UI, M4A moov-at-end streaming, hot-cache spill (#737)

* feat(playback): stream buffering UI, ranged M4A tail prefetch, demuxer fix

Defer seekbar/progress until HTTP stream is armed for both legacy and
RangedHttpSource; show buffering overlay on cover art. Add MP4 tail
prefetch and Symphonia isomp4 bounded-mdat/moov-at-EOF probing so
moov-at-end M4A can start without reading the full mdat.

* feat(hot-cache): spill large ranged streams to disk for promote

When a ranged HTTP download completes above the 64 MiB RAM promote cap,
write the existing buffer once to app-data stream-spill/ and register it
for hot-cache promote (rename) and replay via fetch_data. Analysis seeds
from the spill file up to the local-file cap (512 MiB).

* fix(ui): stream buffering — grayscale cover and static clock icon

Desaturate player and queue cover art while isPlaybackBuffering; keep a
non-animated clock overlay for visibility without the spinning animation.

* fix(playback): review follow-up — tests, i18n, spill cleanup, changelog

Clippy and test layout fixes; stream spill orphan cleanup on startup;
buffering flag guard in progress handler; bufferingStream in all player
locales; CHANGELOG and contributor credits for stream/M4A work.

* docs: attribute stream buffering and M4A streaming to PR #737

* test(audio): avoid create_engine in stream spill unit test

CI runners have no audio output device; test spill take/consume via
the Mutex slot only, matching install_stream_completed_spill tests.
This commit is contained in:
cucadmuh
2026-05-16 22:56:47 +03:00
committed by GitHub
parent 1ac354fb67
commit 6ea0acede5
45 changed files with 1280 additions and 105 deletions
+23
View File
@@ -335,6 +335,20 @@ Foundational work: faster reviews, narrower diffs, and a safety net under the pa
* Rolled out across the main **card-grid library surfaces****Albums**, **Random Albums**, **New Releases**, **Lossless Albums**, **Playlists**, **Composers**, **Composer detail**, **Genre detail**, **Label albums**, **Album detail** (similar / same-artist rails), **Artist detail** (album / appearance / compilation grids), **Internet Radio**, **Offline Library** (albums + playlists), and **Artists** grid mode (virtual rows driven by the same column metrics).
* **Settings → Appearance → Library card grids:** persisted **maximum columns** (**412**, default **6**) with translated copy calling out **performance** trade-offs. Settings search index updated.
### Playback — stream buffering indicator on cover art
**By [@cucadmuh](https://github.com/cucadmuh), PR [#737](https://github.com/Psychotoxical/psysonic/pull/737)**
* While an **HTTP stream** is still opening, the **player bar** and **queue** cover art is **greyscaled** with a static **clock** overlay; the seekbar and timer stay at **0** until the Rust engine arms playback (no optimistic drift).
* **`audio:progress`** carries an optional **`buffering`** flag; the UI only updates **`isPlaybackBuffering`** when the flag changes to avoid redundant store writes.
### Hot cache — promote completed ranged streams larger than 64 MiB
**By [@cucadmuh](https://github.com/cucadmuh), PR [#737](https://github.com/Psychotoxical/psysonic/pull/737)**
* Completed **ranged HTTP** downloads above the in-RAM promote cap (including long **M4A** / **ALAC** albums) are **spilled to disk** under app-data **`stream-spill/`**, then **renamed into hot cache** on promote instead of being skipped.
* Orphan spill files from prior sessions are **removed on startup** (best-effort).
## Removed
### Settings — Animations 3-state setting under Seekbar Style
@@ -346,6 +360,15 @@ Foundational work: faster reviews, narrower diffs, and a safety net under the pa
## Fixed
### Playback — M4A / MP4 streaming (moov-at-end) and seekbar during buffer
**By [@cucadmuh](https://github.com/cucadmuh), PR [#737](https://github.com/Psychotoxical/psysonic/pull/737)**
* **M4A** and **MP4** tracks streamed from the server (including **AAC** and **ALAC** in `.m4a` / `.mp4` containers) with **`moov` at the end of the file** — typical for many Navidrome / iTunes-style encoders — **start audibly sooner**: playback no longer waits for the entire **`mdat`** blob to download before Symphonia can open the file.
* **Tail prefetch** on the ranged HTTP path pulls the end of the file first so metadata is available while the linear download still fills from byte 0.
* **Symphonia `isomp4` patch** (vendored): when a top-level **`mdat`** atom spans to EOF, the demuxer **scans the file tail for `moov`** instead of seeking past end-of-stream during probe — fixes failures such as **`format probe failed: end of stream`** and long stalls before the first sample on large moov-at-end files.
* While the stream is still opening, the **seekbar and elapsed time stay at 0** (and the cover shows the buffering state — see **Added** above) instead of advancing ahead of decoded audio — the same guard applies to **legacy** HTTP readers and **`RangedHttpSource`**.
### Mixes — rating filter and Lucky Mix queue fill
**By [@cucadmuh](https://github.com/cucadmuh), PR [#714](https://github.com/Psychotoxical/psysonic/pull/714)**