Skip to content

feat(queue): native cross-source play queue#337

Merged
LargeModGames merged 6 commits into
mainfrom
feat/native-queue
Jul 5, 2026
Merged

feat(queue): native cross-source play queue#337
LargeModGames merged 6 commits into
mainfrom
feat/native-queue

Conversation

@LargeModGames

@LargeModGames LargeModGames commented Jul 5, 2026

Copy link
Copy Markdown
Owner

Summary

Adds a native play queue that works across every source (Spotify, Local Files, Subsonic, YouTube) in one FIFO, plus the reliability and responsiveness work that came out of live-testing the Spotify-to-YouTube handoff.

Feature

  • Press z on any track to queue it natively; queued tracks play before the underlying playlist/album context, then the context resumes where it left off.
  • Queue screen (Shift+Q): remove (x, rebindable), reorder (J/K), jump ahead (Enter), plus an up-next preview of what resumes after the queue drains.
  • Queued Spotify tracks play through native (librespot) streaming via a direct load with context suspend/resume; on an external Spotify Connect device, z keeps the Web API queue behavior.
  • The queue survives restarts (persisted in last_session.yml).

Handoff reliability and responsiveness

  • End-of-track handling no longer uses try_lock, so the queue handoff can never be silently dropped by a lock race with the render loop.
  • The queue slot is published before a download starts (with a loading indicator in the playbar), so skip/pause/playbar treat the queued track as current during the fetch, and a skip can no longer fire a second advance that dropped a queued item.
  • The playbar renders the queue slot for every source instead of showing the stale paused Spotify context.
  • A finished queued Spotify track clears its slot immediately, so the Spirc reload guard cannot resurrect it during the next item's download; librespot is also silenced on every decoded queue start and on queue drain, covering mid-play skips on device-reuse paths.
  • Queue downloads (YouTube/Subsonic) run off the IoEvent pump with a generation stamp; superseded fetches are discarded. Previously a fetch froze all event handling, delaying every skip for any source.
  • The next queued Spotify track is preloaded while the current slot plays, so back-to-back queued Spotify skips start near-instantly instead of paying a cold load each time.

Notable non-obvious finding: Spirc ignores player events from direct player.load calls (play_request_id mismatch), so it never self-advances off a queued track; the ghost-audio bugs were all missing pause/release calls in the queue engine.

Testing

  • cargo clippy -D warnings clean on slim (--no-default-features --features telemetry), default, and --features all-sources.
  • cargo test: 349 passing on slim, 568 with all sources, including regression tests for slot clearing and queue routing.
  • Live-verified: Spotify context to queued Spotify to queued YouTube handoff, mid-play skips, rapid consecutive skips, playbar loading states.

Closes #206

… and resume

Play queued Spotify tracks through the native streaming player and resume the
underlying context when the queue drains.

- try_play_queued Spotify arm: require a connected streaming player, silence any
  decoded audio, then play_uri the track and publish a Spotify queue slot.
- Suspend the native-Spotify context on end-of-track and manual skip, preempting
  Spirc self-advance with a pause() before handing the sink to the queue.
- ResumeSpotifyContext network handler re-loads the context (offset by the
  resume track) via the existing start_playback machinery.
- Spirc self-advance guard reissues the queued track if librespot switches away,
  bounded by a retry budget.
…ive queue

Add a dimmed, non-selectable 'Up next from context' preview to the Queue
screen showing what resumes once the native queue drains: the tail of the
suspended (or still-playing) per-source context, or the Spotify Web-API
mirror for a native-Spotify context, or the station name for radio. Selection
stays confined to the native-queue rows.

Render the Queue title hint with the configured remove_from_queue key, add
the missing Enter/jump help entry, document the queue in README and CHANGELOG
(issue #206), and suppress the empty-queue hint while the queue owns playback.
@LargeModGames LargeModGames merged commit 8182cf2 into main Jul 5, 2026
15 checks passed
@LargeModGames LargeModGames deleted the feat/native-queue branch July 5, 2026 15:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Remove item from queue

1 participant