Skip to content

Fix/playlist pagination - #55

Open
Danieloni1 wants to merge 11 commits into
varunneal:mainfrom
Danieloni1:fix/playlist-pagination
Open

Fix/playlist pagination#55
Danieloni1 wants to merge 11 commits into
varunneal:mainfrom
Danieloni1:fix/playlist-pagination

Conversation

@Danieloni1

Copy link
Copy Markdown

No description provided.

Danieloni1 and others added 11 commits April 20, 2026 13:24
Runtime scope string was narrower than the declared SCOPES list,
silently stripping user-top-read, user-read-recently-played,
user-read-playback-position, app-remote-control, streaming, and
user-library-modify. Also adds user-modify-playback-state to the
list itself (was missing, required for start/pause/skip/previous).

Derive the runtime scope from SCOPES so the two cannot drift again.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
pyproject.toml already pins spotipy==2.26.0 (upstream 8ee9844);
lockfile was still resolving 2.24.0.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Playback.action now accepts 'previous' and dispatches to
Client.previous_track() (already wraps spotipy.previous_track).
Closes the one-way navigation gap where callers could only skip
forward.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
✨ M1: Previous-track action + OAuth scope fix
Moves all ToolModel pydantic classes (Playback, Search, Queue, GetInfo,
Playlist) plus new ones (History, TasteProfile, SmartPlay) to
src/spotify_mcp/tool_models.py to keep server.py under the 500-line
project guideline once M2-M4 dispatcher cases land.

Adds utils.ensure_auth for read-only methods that need a fresh token
but not an assigned device (search, recently-played, top items).

Swaps search() from @Validate to @ensure_auth: search never touches a
device, so requiring one was spurious and broke SmartPlay callers with
no active device. Drops the now-unused device kwarg.

Adds owner_id to parse_playlist (non-breaking) so SmartPlay can detect
editorial playlists via owner.id == 'spotify'.

Also prunes a batch of unused imports from server.py.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Wraps current_user_recently_played with limit clamping (Spotify caps
at 50) and mutually-exclusive after/before cursors. Returns a compact
list of {track, played_at, context_uri, context_type} per item so the
caller can recover the source playlist/album of each play.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Exposes three actions:
- profile: top artists + top tracks + genre histogram.
- tracks: top tracks only.
- artists: top artists only.

Taste profile is cached per time_range for 30 days; callers bypass
the cache with refresh=True. The cache stores internal keys
(_top_artist_ids, _cached_at) that M4 SmartPlay consumes; those
keys are stripped before the public JSON response.

Adds parse_top_artist (keeps genres + popularity) and genre_histogram
helpers in utils.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
NL query -> Spotify search (track+album+playlist) -> score by
name-overlap, editorial-curation, prefer-bias, and taste-bonus
(match against cached top artists) -> auto-play top pick.

Graceful degrade:
- Taste fetch failure: rank without taste signal; taste_available=false.
- Auto-play failure (no device, etc): auto_played=false with
  auto_play_error populated, no crash.
- Empty query / invalid prefer: structured error string.

Ranking isolated in ranking.py (pure functions, no spotipy import).
Artist IDs are injected into candidate dicts in-memory from the
raw search response so existing parsers (parse_track, parse_album)
stay unchanged and no internal keys leak into Search/GetInfo output.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Documents fork-added features alongside existing tools with example
prompts for each:

- Playback `previous` action (new).
- SpotifyHistory (M2).
- SpotifyTasteProfile (M3).
- SpotifySmartPlay (M4, flagship).

OAuth re-auth note added for users migrating from upstream (scope
list expanded). Install snippets swapped to this fork's git URL.
Original upstream README preserved verbatim at README.original.md
with a snapshot banner pointing back to the active README.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
✨ M2/M3/M4: History, TasteProfile, SmartPlay tools
sp.playlist() caps at 100 items; large playlists (e.g. 260+ track
Dump) silently truncated. Paginate via sp.playlist_items() until
exhausted. parse_tracks now also preserves added_at when present.

- get_info playlist branch: extend tracks past first page
- get_playlist_tracks: loop with optional cap, limit=None = all
- parse_tracks: keep added_at on parsed dict

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant