Repository navigation
The listening profile the Spotify grant already pays for #36
robert-dean
started this conversation in
Ideas
Replies: 0 comments
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Written: 2026-08-09, after checking every claim below against Spotify's live docs and the
February/March/May/July 2026 changelogs.
State of the tree:
plugins/spotifyrequestsuser-top-readanduser-library-readatauthorize time and calls neither. The capability contract has nowhere to put the answer.
What is already paid for
SPOTIFY_SCOPESin spotify.manifest.ts was carriedover verbatim from the previous station and asks for more than this tree uses. Two of those scopes
are live money left on the table:
user-top-readGET /me/top/tracks,GET /me/top/artistsuser-library-readGET /me/tracks(saved)Every account that has ever connected the plugin holds a token that can read all three. There is no
re-consent, no manifest change and no new
permissions.networkentry:api.spotify.comis alreadyallowlisted.
/me/top/{type}takestime_rangeofshort_term,medium_termorlong_term, andlimit1–50.Three time ranges times two types is six distinct views of the same account's taste, which is more
signal than the rotation pool has from any other source.
The gap
music.provider.ts gives
MusicProviderCatalogexactly four methods:searchTracks,getTrack,listPlaylists,getPlaylistTracks. There is no shape for "top items" and nothing host-side asks for one.ProviderTrackhas no genre field either, which matters because the interesting half of this istop artists and their genre tags, not top tracks.
Two ways in
Synthetic playlists, no SDK change.
listPlaylistsemitstop:short_term,top:medium_term,top:long_termandsavedalongside the real ones;getPlaylistTracksroutes those ids to/me/top/tracksand/me/tracks. It rides the existing import path end to end and could land in anafternoon. The ids are namespaced so they cannot collide with a real Spotify playlist id, and their
permissionswould be['read']. It is a small lie: these are not playlists, and a second providerwould have to invent its own namespace to tell the same one.
A
profilesub-capability. An optionalMusicProviderProfilecarryinggetTopTracks(timeRange),getTopArtists(timeRange)andgetSavedTracks, plus a'profile'capability string. The manifest's capability list is
z.array(z.string().min(1)), deliberately open,so adding one costs nothing structurally. This is the shape to build if top artists and their genres
are going to feed selection or patter rather than just filling a pool, because it is the only one of
the two with somewhere to put an artist.
They are not exclusive and the first does not block the second. Pick the first for rotation, the
second for taste modelling.
What February 2026 did and did not take
Checked because the same round is why
getPlaylistTracksreads/itemsand whylistPlaylistsreports
permissionsat all. It did not touch this:GET /me/top/{type}— no removal, no deprecation,user-top-readstill current.GET /me/tracks— kept. Only the writes moved,PUT/DELETE /me/trackstoPUT/DELETE /me/library.external_ids— removed from Track and Album in February, reverted in the March 2026 changelog.So
ProviderTrack.isrc, the cross-provider join key, is intact. Worth remembering that it was gonefor a month: Spotify has shown it is willing to pull it.
genreson the Artist object — not on any removal list.What it took and did not give back:
popularityon Track, andfollowersandpopularityonArtist. There is no popularity signal available from Spotify any more, so a taste model has to rank
on the ordering
/me/topreturns and nothing else.Three findings from the same sweep, none of them about top listens
user-read-emailanduser-read-privateare now dead weight. February strippedcountry,email,explicit_content,followersandproductfromGET /me. Those two scopes exist tounlock exactly those fields. Under the dev-mode cap of five authorized users, asking a person to
consent to their email address for a field the response no longer carries is friction that buys
nothing. Delete both from
SPOTIFY_SCOPES.Recently-played is the one that costs a re-consent.
GET /me/player/recently-playedsurvivedFebruary and is the richest of the three signals, but
user-read-recently-playedis not inSPOTIFY_SCOPES. Adding it re-authorizes every connected account, so batch it with the two removalsabove rather than shipping it alone. listening-loop owns that batching and is
where the scope change is sequenced.
Everything else the sweep turned up is code the API has moved underneath, which is a different
kind of work from this file's: the search limit clamp, the SDK's removed batch overload,
account_id,and the Premium requirement on development mode. All of it is in
spotify-api-currency.
Sources
Moved from
docs/todo/spotify-listening-profile.md. The file as it last stood: https://github.com/robert-dean/deadair/blob/71e431d45ce7b35a189c7d226832ec4a6247e9dc/docs/todo/spotify-listening-profile.mdAll reactions