Repository navigation
feat: list the play order in GET /player/queue, and step back without rewinding - #421
Conversation
4048efa to
3483411
Compare
3483411 to
b0c5f51
Compare
The fork's window moved out of /status (devgianlu/go-librespot#421, reworked for any client): /status is back to upstream's shape, and the play order is GET /player/queue, {prev_tracks, track, next_tracks}, entries {uri, uid, provider, track}. The `queue` event now says when that answer changed. SpotifySource reads it in the burst that carries `queue` or a connection (the event is raised only on change, so one missed while /events was down is never said again), and reads it even when /status fails in that burst. A 204 or a 404 (stock 0.10.3) lists nothing; any other failed read keeps the last order (E10). details.queue keeps its shape: uid, provider and artwork have no consumer yet (the carousel slides text only). The fake daemon raises `queue` ahead of the events it comes with whenever its answer changes, as go-librespot compares the answer itself, and its docstring records the provider/uid values measured on 2026-10-07. Smoke, cut to Spotify (no shared layer touched): item 1 (no failed unit, without a reboot), item 3 (iPhone handoff, a track queued from the phone, the phone's mini-bar swipe both ways), item 9 (no new ERROR/WARNING of the backend's own). Item 6 (radio -> spotify -> radio) was NOT run: music was playing. Unit binary: the fork's feat/status-queue (#420 + #421) at 8a92995. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
b268b5e to
17dc5ad
Compare
30315ab to
f64f57e
Compare
devgianlu
left a comment
There was a problem hiding this comment.
🤖 This review was generated by Claude (Claude Code), not written by hand. Please verify before acting on it.
None of these crash the daemon or play the wrong thing. The ones I'd fix before merging are the null entries after LRU eviction, the missed "gone" event, and the POST /player/prev 400 on bodies that aren't JSON. The API.md note is a quick docs fix.
Checked and fine: the req.Data.(ApiPrev) assertion can't panic (only built in PlayerPrev), provider values are always queue/context/autoplay, and peek handles a nil cache when metadata is disabled.
newApiResponseStatusMedia dereferenced every metadata field. The playing track always has them, but next_track and /context/tracks describe whatever the metadata cache holds, and a cached track missing its album, an artist's name or its gid panicked the daemon on a /status read. GET /player/queue, later in this series, describes up to 65 of them. Read the fields through the generated nil-safe getters, so that missing metadata is described as empty, a missing album date included. An episode without a named show lists no artist instead of one blank name. A track keeps a nameless artist's "" so that artist_names stays paired with artist_uris, and a track without artists lists [] instead of null. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
POST /player/prev rewinds a track played past three seconds. That suits
a previous button, but not a client that already shows the previous
track and wants to go to it, such as a swipe over a carousel. Spotify
Connect's skip_prev command has an option for this, which the daemon
already honors.
Accept an optional {"allow_seeking": bool} body, defaulting to true.
With false, prev goes to the previous track, or restarts the current one
when there is none. Prev used to ignore its body, so a body that does
not parse gets the default rather than a 400.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A client that shows what comes before and after the current track has
only next_track to go on, while the daemon already holds the window
Spotify Connect publishes: up to 32 tracks on each side in play order,
with the user's queue and shuffling applied.
GET /player/queue returns {prev_tracks, track, next_tracks}. Each entry
is {uri, uid, provider, track}, where track is the cached metadata or
null, and the current entry carries the playing position. A queue event
without data is raised where the answer can change: when the window is
published or cleared, when its metadata is cached or the product info
arrives, and when a session that listed entries ends. Each site compares
a signature of the answer, position aside, with the last one announced.
The signature reads the window from the metadata cache with get, and
/context/tracks marks it used again after a listing, so that sweeping
other contexts evicts around it. Run's loop moves into serve so that
tests can drive it without a session.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
f64f57e to
150b9c0
Compare
|
Thanks for the review. I rebased on master (9085364) and folded the fixes into the same three commits: bdcc739, 19de8e9 and 150b9c0. The queue event is now emitted where the play order or its metadata changes, and only when the response's signature differs from the last one announced; removing any of those calls fails a test. The only thing I kept as it was is the |
Lets a client show the tracks before and after the current one, like the queue view and the mini player swipe in the Spotify apps, and go back to the track it showed.
The daemon already holds that play order: the window Spotify Connect publishes, up to 32 tracks on each side with the user's queue and shuffling applied, and the metadata cache already prefetches it. Only
next_trackis exposed today. The Web API'sGET /me/player/queueis not an option: with the daemon's token it answered 429.Change
GET /player/queuereturns{prev_tracks, track, next_tracks}, each entry{uri, uid, provider, track}:providerisqueue,contextorautoplay, which is how the apps split their queue view.uidtells two copies of a track apart, and isnullwhere Spotify gives none, as for an album's tracks.trackhas the/statusshape, or isnulluntil its metadata is cached (alwaysnullwithoutmetadata.enabled). The current entry carries the playing position, the others 0.prev_tracksis the play order, not a history. Without a session the endpoint answers 204.A
queueevent, with no data, says the response changed: the order moved, entries got their metadata, or the session ended. The position alone raises none.POST /player/prevtakes an optional{"allow_seeking": bool}, the option of Connect's ownskip_prev.falseskips back from a track played past three seconds instead of rewinding it. The default istrue, and a body that doesn't parse gets it too, since prev used to ignore its body.The first commit, independent of the others, makes track descriptions tolerate incomplete cached metadata: a cached track missing its album, an artist's name or its gid panicked the daemon on a
/statusread.GET /statusis unchanged: listing the window there would make every read about twenty times larger.While a queued track plays,
prev_tracksmisses the context track played just before it:GoNextenters the queue without moving the context position, so a prev from the queued track lands one context track earlier and the queued track stays queued. Connect receives the same window. The PR doesn't change this; the/player/prevdescription now says where prev lands.Tested
gofmt,go vetandgo test -count=1 -tags "test_unit test_integration" ./daemon/... ./spclient/...on each commit; oapi-codegen v2.5.0 regeneratesdaemon/api_gen.gowithout a diff.TestApiPlayerQueueWireFormatandTestPlayerQueue*cover the response (wire format, positions, local files, no metadata).TestQueueEvent*cover every emit site, the session end included: removing any one call fails a test.TestQueueKeepsItsEntriesNamedcovers eviction during sweeps and listings,TestApiPrevthe/player/prevbody, andTestApiTrackToleratesIncompleteMetadataincomplete track and episode metadata. Theallow_seekingdispatch has no unit test, since the three-second check needs a player with an output; the device run below covers it.contextwith a uid and album entries have none; queued tracks arequeue(q1,q2, …, two uids for a track queued twice) and stay queued when a new context starts; an album running out with autoplay on continued as a station with 32autoplayentries; 32 to 37 named entries are 21 to 25 KB, read in 1 to 5 ms, against about 1 KB for/status;{"allow_seeking": false}7 s into a track went to the previous one. On the current revision (150b9c0 on master 9085364), going back from a carousel with{"allow_seeking": false}went to the previous track rather than rewinding the current one. Thequeueevent wiring as changed by the review has not been re-checked case by case on the device.Not in this PR: playing or removing an entry by
uid, and reordering the queue.🤖 Generated with Claude Code