* 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.
M4A files store the iTunSMPB value behind a 16-byte binary 'data' atom
header. The previous parser took bytes directly after the ASCII key,
landing in the binary header and producing a corrupted parts array
(total_valid=348 instead of ~13M samples), which caused take_duration
to exhaust the source in ~8ms — silent no-playback.
Fix: search for the " 00000000 " sentinel within 128 bytes of the key
to locate the actual value string, falling back to the old offset for
ID3v2/MP3 files where no binary header exists.
Also patches symphonia-format-isomp4 (local crate under patches/) to:
- Tolerate SL descriptor predefined=0x01 (older iTunes M4A)
- Convert descriptor.unwrap() panic to a proper decode error
- Gracefully skip malformed trak atoms (e.g. MJPEG cover-art streams)
instead of failing the entire probe
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>