Skip to content

feat: read and set the play queue through the API #435

Open

Description

@juthrbog

I am opening this first, as CONTRIBUTING.md suggests for a new feature, before I send a PR.

Problem

An API client can add a track to the queue with add_to_queue, but it cannot see the queue or change it. /status shows only the next track. To remove, reorder or clear queued tracks, a client has only one way: the Spotify app sends the set_queue dealer command. Then handlePlayerCommand calls p.setQueue.

Motivation

I am building spotui, a terminal Spotify player. It embeds the daemon in-process and sends ApiRequests over the channel. I want a queue view where the user sees the upcoming tracks, plays a track next, removes one, reorders them, or clears the queue. The same requests also help HTTP clients of the daemon.

Proposal

Two new API request types, with HTTP endpoints and api-spec.yml entries:

  1. queue, GET /player/queue. It returns prev_tracks and next_tracks as last published, so at most 32 on each side (tracks.MaxTracksInContext). Each entry has uri, uid and provider (queue, context or autoplay). It also has the track metadata in the same shape that /status uses, if the metadata is in the cache. If no session is active, it returns 204.
  2. set_queue, POST /player/queue with {"tracks": [{"uri": "...", "uid": "..."}]}. It replaces the queued tracks, in order. An empty list clears the queue. It goes through the existing setQueue / tracks.List.SetQueue path, so the state publish, invalidateUpcoming and the prefetch reset happen as they do for the dealer command. The track that plays now and the context do not change.

Input checks for set_queue:

  • Track and episode URIs only.
  • 0 to 32 entries.
  • No uid twice.
  • A missing or null tracks field returns 400.
  • If no context is loaded, it returns 409.
  • A uid stays only if it names a queued entry with the same URI. Other entries get a new queue uid, the same way add_to_queue makes one.

The change is about 660 lines with tests and the regenerated api_gen.go. It has no new config, no new dependencies, and no change to add_to_queue. It has two commits:

  1. feat: let a list job answer a waiting caller. A loader job can carry a reply, so a request that the loader drops or never runs still gets an answer and does not hang.
  2. feat: read and set the play queue through the API.

Questions before I send the PR

  1. The queue has no size limit, but the published state holds at most 32 upcoming tracks. So set_queue takes at most 32, and if more tracks are in the queue, it drops the ones that are not in the list. The dealer command works the same way today, because tracks.List.SetQueue keeps only the queued tracks in the list that it gets. Is that acceptable, or do you want a different rule? For example, set_queue could keep the queued tracks after entry 32.
  2. Do you want a websocket event when the queue changes? I left it out to keep the PR small, but it is easy to add in this PR or a later one.
  3. Are GET and POST /player/queue good names, or do you prefer /player/set_queue next to /player/add_to_queue?

How I tested

Unit tests (test_unit) cover these cases:

  • The read lists queued tracks before context tracks, in order.
  • set_queue reorders, removes, adds and clears, also with 32 queued tracks.
  • If the user skips to another track before the job runs, the publish shows the skip.
  • A dropped job and a closed lane still answer.
  • A request with no context gets 409.
  • The HTTP handler rejects bad input.

gofmt, go vet, go build ./cmd/daemon and go test -tags "test_unit test_integration" ./... pass. I did not test it against a live account yet.

If this fits the project, I can send the PR. I can also change any of the choices above first.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions