Commit Graph
2 Commits
Author SHA1 Message Date
Filippo Vicentini 7f9397f916 fix(qconnect): cold-load the current track on a state-only resume
A state-only SetState resume (playing_state=play, current_track=null)
can reach a renderer whose queue cursor is set but whose engine holds
no loaded audio — the session store restores the queue paused and
unloaded after a restart. The play arm issued a bare engine.resume(),
which dies in the audio thread ("cannot resume - no audio data
available") while the renderer keeps reporting paused 0:00, so a
mid-track handoff from a peer renderer wedges the controlling app
forever.

When the engine has no loaded audio and the cloud state names a
current track, force-stream that track at the reported position
(force_remote_track_stream — the plain track-id guard would skip,
since the restored cursor already reports the track). The
has_loaded_audio gate keeps cloud echoes and live playback on the
plain resume path, and the SetActive takeback fix already established
this exact pattern.

Repro: cast a queue to qbzd, restart the daemon, play on another
renderer, then hand off mid-track by selecting the qbzd renderer in
the app: it sticks at paused 0:00 until a CLI play primes the engine.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
(cherry picked from commit 9032e6e027c29b386042fce1b1e95388b20e4f01)
2026-08-20 10:40:59 +02:00
Filippo VicentiniandClaude Fable 5 83e6f92050 fix(player): surface live audio stream errors as PlaybackError
A mid-playback stream error (e.g. an ALSA buffer underrun/EPIPE) was
only visible through rodio's default error callback, which eprintln!s
and drops it: playback died with a bare PlaybackStateChanged(paused)
and no PlaybackError on the bus, so hook/SSE integrators (moOde) could
not tell a device hiccup from a user pause.

Install an error callback on the rodio output stream that records the
error on SharedState, where the playback driver already drains it into
the stream-error latch and the daemon bridges it to
CoreEvent::PlaybackError. Rate-limited to one recording per 5s since a
wedged ALSA device repeats EPIPE every period and each drained message
becomes one bus event (and one forked hook script).

Found while testing the moOde qbzevent.sh integration on-device: a
buffer underrun paused playback with no PlaybackError emitted.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-20 10:13:42 +02:00