[PR #1677] [MERGED] Emit SetQueue event #1492

Closed
opened 2026-02-27 20:02:39 +03:00 by kerem · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/librespot-org/librespot/pull/1677
Author: @ralph
Created: 1/22/2026
Status: Merged
Merged: 2/9/2026
Merged by: @photovoltex

Base: devHead: emit-set-queue-event


📝 Commits (10+)

  • 065ca87 Emit Player::SetQueue event
  • d0dd9a7 Remve AddToQueue, switch to SetQueue
  • 6a07e0c Add CHANGELOG, reformat
  • 22c7bb3 Emit SetQueue once when a track collection is added
  • 8042798 Clarify wording
  • 32b0000 Just call emit_set_queue_event() instead of duplicating
  • 64edaf8 Use QueueTrack struct instead of (String, String)
  • 5bf1c5b Make SetQueue an opt-in event
  • 2a5f698 Refactor to use ConnectConfig for opting in to SetQueue events
  • 8fb664f Use explicit initializer value

📊 Changes

6 files changed (+146 additions, -27 deletions)

View changed files

📝 CHANGELOG.md (+1 -1)
📝 connect/src/spirc.rs (+56 -10)
📝 connect/src/state.rs (+3 -0)
📝 playback/src/player.rs (+54 -12)
📝 src/main.rs (+1 -0)
📝 src/player_event_handler.rs (+31 -4)

📄 Description

What does this do?

This emits a new universal Player::SetQueue event.

Background info: How is the Spotify Connect queue organised?

The Spotify connect queue basically has the following data structure:

context_uri: SpotifyUri
current_track: Track
prev_tracks: []Track
next_tracks: []Track

Each Track has at least a track URI and a provider which can be autoplay, context, queue or unavailable.

  • autoplay: Added by Spotify to keep playing
  • context: part of the current context identified by context_uri
  • queue: manually queued while a context was loaded
  • unavailable: self explanatory

Here you can see an example of a queue:

539394739-65d47a85-9dd3-4b21-9343-da1503151c20

Why is this event "universal"?

There are multiple actions that manipulate the queue in different ways. Examples:

  • Loading a new context: manipulates context_uri, current_track, prev_tracks, next_tracks
  • Queuing a new track/album/playlist while a context is loaded: manipulates next_tracks
  • Reordering tracks in the queue: next_tracks
  • Removing tracks from the queue: next_tracks

If a GUI application wants to adjust its state when any of these actions are performed remote or local, it just needs to listen to this one universal event. The queue displayed in the GUI is always correct and always up to date.

Why does this remove AddToQueue that was just added in 87d37c3e18 ?

AddToQueue is a special case of SetQueue, and SetQueue covers all cases of AddToQueue usage. Also, it's less error prone for app developers. Say a GUI developer wants to display the current queue from Spirc, and the queue looks like this:

`context` Track 1 <- currently playing
`context` Track 2
`context` Track 3
`context` Track 4

When another track is queued, it will be placed after the currently playing track:

`context` Track 1 <- currently playing
`queue` Track 1
`context` Track 2
`context` Track 3
`context` Track 4

In this case, AddToQueue will be emitted for Track 1, and the GUI developer will be responsible to add it in the right position of the app's state.

When yet another track (or collection of tracks, e.g. an album with 2 tracks) is queued, those will be placed after the currently playing track AND after all the other queue tracks:

`context` Track 1 <- currently playing
`queue` Track 1
`queue` Track 2
`queue` Track 3
`context` Track 2
`context` Track 3
`context` Track 4

In this case, AddToQueue will be emitted with Track 2, then with Track 3. Again, the developer of the consuming app is responsible for placing the new tracks in the right position.

In contrast to this, SetQueue always emits the whole queue with the tracks in the right order, so the developer of the consuming app cannot place the tracks in the wrong position.

Also, AddToQueue was never in a release, so removing it is ok imho.

Are there downsides?

As we're emitting ALL THE QUEUE FIELDS \o/ with every change, we do sometimes emit more than necessary. But we have to emit next_tracks most of the time, and the remaining queue fields are not that large compared to next_tracks. current_track and context_uri is negligible, and prev_tracks is capped at 20 tracks.

How to go from here?

I'd love to hear your input on this. Do you think a universal SetQueue event is a good thing? Or would you rather have more fine grained events emitting less data, e.g. one for prev_tracks, one for next_tracks one for current_track and current_track? Or should we keep the universal SetQueue for actions that manipulate all of next_tracks, but keep AddToTrack when adding tracks to the queue, manipulating only small parts of next_tracks?

The current approach works well for me and my reactive SwiftUI app, and makes updating the queue incredibly easy. But I'm still pretty new to this and there might be use cases that I didn't think of.


🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/librespot-org/librespot/pull/1677 **Author:** [@ralph](https://github.com/ralph) **Created:** 1/22/2026 **Status:** ✅ Merged **Merged:** 2/9/2026 **Merged by:** [@photovoltex](https://github.com/photovoltex) **Base:** `dev` ← **Head:** `emit-set-queue-event` --- ### 📝 Commits (10+) - [`065ca87`](https://github.com/librespot-org/librespot/commit/065ca870187487ec053c22d5b639053053d67a7b) Emit `Player::SetQueue` event - [`d0dd9a7`](https://github.com/librespot-org/librespot/commit/d0dd9a7184696b088d8822a338ca194735c5b1ad) Remve AddToQueue, switch to SetQueue - [`6a07e0c`](https://github.com/librespot-org/librespot/commit/6a07e0c40f413f013c7307cb7dd469c31f96f867) Add CHANGELOG, reformat - [`22c7bb3`](https://github.com/librespot-org/librespot/commit/22c7bb37d5d38ee7e6b6d69c652adb513ead5324) Emit `SetQueue` once when a track collection is added - [`8042798`](https://github.com/librespot-org/librespot/commit/8042798ef9128fe0981b20f1d93c78c2b57d1a55) Clarify wording - [`32b0000`](https://github.com/librespot-org/librespot/commit/32b00004dee1cf8d2fec9230bb4f554d526c63dd) Just call `emit_set_queue_event()` instead of duplicating - [`64edaf8`](https://github.com/librespot-org/librespot/commit/64edaf842c3709984502fcc1765b939198013577) Use QueueTrack struct instead of `(String, String)` - [`5bf1c5b`](https://github.com/librespot-org/librespot/commit/5bf1c5b7eca080e1f8cf801a625af04821444eac) Make SetQueue an opt-in event - [`2a5f698`](https://github.com/librespot-org/librespot/commit/2a5f698502032d9d2488000a4de9177afa7c3553) Refactor to use `ConnectConfig` for opting in to `SetQueue` events - [`8fb664f`](https://github.com/librespot-org/librespot/commit/8fb664f9068df9a5f30f4899916d1c07fac71ea1) Use explicit initializer value ### 📊 Changes **6 files changed** (+146 additions, -27 deletions) <details> <summary>View changed files</summary> 📝 `CHANGELOG.md` (+1 -1) 📝 `connect/src/spirc.rs` (+56 -10) 📝 `connect/src/state.rs` (+3 -0) 📝 `playback/src/player.rs` (+54 -12) 📝 `src/main.rs` (+1 -0) 📝 `src/player_event_handler.rs` (+31 -4) </details> ### 📄 Description ## What does this do? This emits a new universal `Player::SetQueue` event. ### Background info: How is the Spotify Connect queue organised? The Spotify connect queue basically has the following data structure: ``` context_uri: SpotifyUri current_track: Track prev_tracks: []Track next_tracks: []Track ``` Each Track has at least a track URI and a provider which can be `autoplay`, `context`, `queue` or `unavailable`. * `autoplay`: Added by Spotify to keep playing * `context`: part of the current context identified by `context_uri` * `queue`: manually queued while a context was loaded * `unavailable`: self explanatory Here you can see an example of a queue: ![539394739-65d47a85-9dd3-4b21-9343-da1503151c20](https://github.com/user-attachments/assets/b8beb9f5-8ab6-42f1-aaa5-27f3d000e3b1) ### Why is this event "universal"? There are multiple actions that manipulate the queue in different ways. Examples: * Loading a new context: manipulates `context_uri`, `current_track`, `prev_tracks`, `next_tracks` * Queuing a new track/album/playlist while a context is loaded: manipulates `next_tracks` * Reordering tracks in the queue: `next_tracks` * Removing tracks from the queue: `next_tracks` If a GUI application wants to adjust its state when any of these actions are performed remote or local, it just needs to listen to this one universal event. The queue displayed in the GUI is always correct and always up to date. ### Why does this remove AddToQueue that was just added in 87d37c3e18e7c712618cd04fad8303e4da9b9c7a ? `AddToQueue` is a special case of `SetQueue`, and `SetQueue` covers all cases of `AddToQueue` usage. Also, it's less error prone for app developers. Say a GUI developer wants to display the current queue from Spirc, and the queue looks like this: ``` `context` Track 1 <- currently playing `context` Track 2 `context` Track 3 `context` Track 4 ``` When another track is queued, it will be placed after the currently playing track: ``` `context` Track 1 <- currently playing `queue` Track 1 `context` Track 2 `context` Track 3 `context` Track 4 ``` In this case, `AddToQueue` will be emitted for Track 1, and the GUI developer will be responsible to add it in the right position of the app's state. When yet another track (or collection of tracks, e.g. an album with 2 tracks) is queued, those will be placed after the currently playing track AND after all the other `queue` tracks: ``` `context` Track 1 <- currently playing `queue` Track 1 `queue` Track 2 `queue` Track 3 `context` Track 2 `context` Track 3 `context` Track 4 ``` In this case, `AddToQueue` will be emitted with Track 2, then with Track 3. Again, the developer of the consuming app is responsible for placing the new tracks in the right position. In contrast to this, `SetQueue` always emits the whole queue with the tracks in the right order, so the developer of the consuming app cannot place the tracks in the wrong position. Also, `AddToQueue` was never in a release, so removing it is ok imho. ### Are there downsides? As we're emitting ALL THE QUEUE FIELDS \o/ with every change, we do sometimes emit more than necessary. But we have to emit `next_tracks` most of the time, and the remaining queue fields are not that large compared to `next_tracks`. `current_track` and `context_uri` is negligible, and `prev_tracks` is capped at 20 tracks. ### How to go from here? I'd love to hear your input on this. Do you think a universal `SetQueue` event is a good thing? Or would you rather have more fine grained events emitting less data, e.g. one for `prev_tracks`, one for `next_tracks` one for `current_track` and `current_track`? Or should we keep the universal `SetQueue` for actions that manipulate all of `next_tracks`, but keep `AddToTrack` when adding tracks to the queue, manipulating only small parts of `next_tracks`? The current approach works well for me and my reactive SwiftUI app, and makes updating the queue incredibly easy. But I'm still pretty new to this and there might be use cases that I didn't think of. --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
kerem 2026-02-27 20:02:39 +03:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
starred/librespot#1492
No description provided.