Repository navigation
feat: save to Liked Songs and add to playlists - #403
Merged
Merged
Conversation
isnogudus
force-pushed
the
feature/library-writes
branch
from
September 27, 2026 16:40
a96c9d1 to
faa228c
Compare
devgianlu
reviewed
Oct 3, 2026
devgianlu
requested changes
Oct 3, 2026
Requests with a body always had their Content-Type overwritten with application/x-protobuf, so endpoints that expect another type could not be reached. Only default to protobuf when the caller set nothing. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Add the collection service's WriteRequest proto and
Spclient.CollectionWrite, which adds items to or removes them from a
collection set such as Liked Songs ("collection"), using the service's
own vendor content type.
Add Spclient.PlaylistRevision and Spclient.PlaylistAppend, which appends
items as an ADD change based on the playlist's current revision and
reports a 409 as ErrPlaylistConflict.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
/library/liked saves tracks to or removes them from Liked Songs; /library/playlists/add_tracks appends tracks or episodes to a playlist, retrying once when the playlist changed in between. Both validate their URIs up front and run off the player loop. /library/playlists now reports can_edit, so clients can offer only the playlists the user may add to. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Add the collection service's PageRequest/PageResponse and Spclient.CollectionPage, which reads one page of a set such as Liked Songs and hands back the token of the next page. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Tell whether tracks are in Liked Songs. The daemon reads the whole set through the collection service, keeps only tracks (the set also holds saved albums) and reuses it for a minute; writes through POST /library/liked are applied to it at once. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
/context/tracks reuses a listing for five minutes, so a track appended through /library/playlists/add_tracks, or saved to or removed from Liked Songs through /library/liked, stayed missing from the listing a client asks for right after. Drop the listing of the changed context after a successful write; an enumeration already running at that moment may have read the context before the change, so its result is not cached either. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A body without liked, or with liked: null, decoded to liked=false and silently removed the tracks from Liked Songs with a 200, although the spec marks the field required. Reject it with 400. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Requests went through innerRequestWith, which repeats them on network errors and 5xx. A playlist change is not idempotent: if the server applied an append and then answered 503/504, or the connection dropped before the answer, the same change was sent again and the tracks landed twice. Add Spclient.RequestOnce, which still renews the token on 401 (the server refused the request) but leaves other failures to the caller, and send playlist changes through it. appendToPlaylist only resends after a 409, which means the change was not applied. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The stale flag that kept an enumeration's possibly pre-change result out of the cache had two holes. It was only cleared in put, so an invalidated enumeration that failed, or an invalidation landing between put and endFetch, left it set and the next, correct enumeration was dropped. And put read the flag under the mutex but added to the LRU after unlocking, while invalidate removed from the LRU before locking, so a pre-change listing could still be cached after the invalidation. Track the generation each in-flight enumeration started at instead: invalidate bumps it, put caches only a result whose generation is current, and both change the LRU under the same mutex. The generation goes with the enumeration in endFetch, so nothing is left behind. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…re gone The collection and playlist services' 403 and 404 came back as generic "invalid status code" errors, so the API answered 500 and logged them at Error level; clients could not tell a permission problem from a fault. Return a spclient.StatusError carrying the status, map 403 and 404 to ErrForbidden and ErrNotFound for the library writes, and document both for /library/playlists/add_tracks. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
GET /library/liked paged through the whole collection, albums included, to answer for at most 50 tracks: up to 100 sequential pages, again after every minute, holding a lock across the fetch that also delayed the 200 of a successful like, and failing with 500 on libraries past the page cap. A track requested by its 21-character id was also reported as not liked, since the collection holds canonical URIs. Ask /collection/v2/contains for just the requested tracks instead, in one round trip, with their canonical URIs; the answer still names them as requested. The cache, its lock and the collection paging go, and changes made on other devices show at once. The request limit and timeout constants are renamed, as reads use them too. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
isnogudus
force-pushed
the
feature/library-writes
branch
from
October 3, 2026 13:03
dd8c754 to
0a90214
Compare
devgianlu
approved these changes
Oct 3, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Rebased onto master now that #402 is in; the five commits here are this PR only.
Lets clients save tracks to Liked Songs and add tracks to playlists through the daemon, via the internal collection and playlist services rather than the Web API:
GET /library/liked?uris=a,btells for up to 50 tracks whether they are in Liked Songs. The daemon pages through the collection set (/collection/v2/paging), keeps only tracks (the set also holds saved albums) and reuses it for a minute; writes made through the daemon apply to it at once.POST /library/liked{"uris": [...], "liked": true|false}saves or removes up to 50 tracks (/collection/v2/write).POST /library/playlists/add_tracks{"playlist_uri": ..., "uris": [...]}appends tracks or episodes as anADDchange on the playlist's current revision, retrying once on 409.GET /library/playlistsnow reportscan_edit(from the rootlist's capabilities) so clients can offer only playlists the user may add to.The collection service messages are a new
proto/spotify/collection/v2/collection.proto, generated with buf like the others.One fix was needed on the way: spclient overwrote the
Content-Typeof every request with a body withapplication/x-protobuf, but the collection service expectsapplication/vnd.collection-v2.spotify.proto. The first commit only sets the default when the caller set none (fix: keep a caller's Content-Type on spclient requests).Testing
GET /library/likedanswers correctly for a liked and an unliked track.go mod tidy,go generate ./...,gofmt, build andgo test -tags "test_unit test_integration" ./...are clean.🤖 Generated with Claude Code