What you see
the reported behaviour
A track finishes and it is gone from the queue. Pick a track further down the list and everything you skipped over goes with it. The queue only ever shows what is playing and what is coming — there is no way to look back at what just played, and no way to return to it.
That is not a gap in the app. It is the shape of the API underneath it.
Everything Roon's transport API can do
node-roon-api-transport, complete surface
- change_settings
- change_volume
- control
- convenience_switch
- get_outputs
- get_zones
- group_outputs
- mute
- mute_all
- pause_all
- play_from_here
- seek
- standby
- subscribe_outputs
- subscribe_queue
- subscribe_zones
- toggle_standby
- transfer_zone
- ungroup_outputs
- zone_by_object
- zone_by_output_id
- zone_by_zone_id
subscribe_queue reads the queue forward from the current track.
play_from_here jumps to an item that is still in it. That is the whole of it.
What is not there
- No queue history. Nothing reports a track that has already left.
- No remove. A queued track cannot be taken out.
- No insert or reorder. Nothing can place a track at a position.
- No clear. The queue is only ever replaced, as a side effect of Play Now.
The only way to put anything into a queue is the browse hierarchy: navigate to the album, open it, find the track, open its action menu, invoke Play Now, Add Next or Queue. Every one of those is a round trip to the Core.
Where the tracks go
the queue, as the API sees it
Not in the subscription. Their queue ids are spent.
What subscribe_queue returns, and all it returns.
play_from_here has nothing to aim at.
What rewinding would cost
counted from the browse path, per track
With no queue-write verb, restoring the queue around a played track means playing that track and then re-queueing every track that followed it — each one a full browse navigation:
- navigate to the album list2
- resolve the album at its offset1
- open the album1
- load its tracks1
- open the track's action menu2
- invoke the action1
- per track8 round trips
| Rewinding into a queue of… | Tracks to rebuild | Core calls |
|---|---|---|
| 20 tracks | 19 | 152 |
| 40 tracks | 39 | 312 |
| 100 tracks | 99 | 792 |
| 250 tracks | 249 | 1,992 |
The call count is not even the worst of it. The rebuild has to open with Play Now, which destroys the live queue before anything refills it — so an interruption anywhere in the middle leaves the zone worse off than when you started. The app already runs this pattern for playlist import, one track at a time behind a progress toast, and that is with a queue nobody is standing in the middle of.
What shipped instead
v1.7.77 — v1.7.80
Played tracks are retained and folded away
A collapsed “N played earlier” disclosure above the Now playing
divider, newest sitting against it. A skipped track shows how far it got
(0:12 / 3:20) so a skip reads as a skip and not as a very short track.
Built from a signal already arriving
Departures are recorded from the zone push the extension already handles, at the one moment the outgoing track and how much of it played are both known. It costs no extra Core calls at all. Resolving a track back to its library album is likewise free — an index built from ordinary album opens.
Tapping one plays it next, not from there
A single browse navigation that inserts the track after the current one and leaves the queue standing. It is honestly a different action from Roon's own, and the screen says so rather than pretending to rewind.
Several at once, in the order you choose
Selecting turns each row into a numbered pick, because the order you tap is not the order the rows are shown in — and it is the order they will play in. The whole selection goes as one request: separate ones would race and interleave into an arbitrary queue order, which is the thing the feature exists to get right.
No queue rebuilding
Hundreds of Core calls, minutes of traffic, and a destroyed queue if it is interrupted. The arithmetic above is the reason, and it does not improve with cleverness.
The record is per zone and in memory. It describes a listening session; one that outlived a restart would offer tracks the zone's queue has no relationship to any more. A zone that disappears and comes back starts clean.
The open question
one experiment, decisive either way
Will play_from_here accept a queue id that has already been consumed?
Nothing in the API documents an answer, and it cannot be settled without a live Core. If it works, retaining the original queue ids turns a perfect rewind into a single call and everything above becomes unnecessary. If it errors — the likelier outcome, since the queue item is gone — the design here stands as the honest ceiling.
Worth running once before any further work on this.
Does a repeated Add Next stack, or keep its own order?
A multi-track “play next” only lands in the right order if the sends go out in the order that arrives right. Add Next puts an item immediately after the current track, so issued repeatedly it should stack each new one in front of the last — meaning the sends have to go out backwards. That is what ships.
It cannot be settled by probing, because finding out costs a real insert and there is no verb to remove one again. So it is an assumption in one named function, and if a queue ever comes out reversed the fix is to stop reversing — one line, nothing else to change.