Skip to content
This repository was archived by the owner on May 25, 2026. It is now read-only.

feat: filter unshuffleable rows from picker - #61

Merged
ivorycrayon merged 1 commit into
mainfrom
feat/filter-non-shuffleable-playlists
May 23, 2026
Merged

feat: filter unshuffleable rows from picker#61
ivorycrayon merged 1 commit into
mainfrom
feat/filter-non-shuffleable-playlists

Conversation

@ivorycrayon

Copy link
Copy Markdown
Contributor

What

  • New fetchCurrentUserId(accessToken) helper at src/lib/spotify/me.ts — calls /v1/me, projects out id, same boundary pattern as the other Spotify fetches (origin, bounded timeout, status-coded error mapping, schema validation). Reuses the existing error vocabulary from playlists.ts.
  • /api/me/playlists route fetches /v1/me in parallel with the existing playlist list + Liked Songs count, then filters playlistsResult.items to keep only ownerId === currentUserId || isCollaborative. Liked Songs sits outside the filter.

Why

Per the smoke test in #55 (comment), post-Feb-2026 /v1/playlists/{id}/items returns 403 Forbidden for any playlist the caller doesn't own and isn't a collaborator on. Without this filter, the picker shows rows the user can't shuffle — and even with #58's defense-in-depth, clicking one is a click that goes nowhere.

The 1st bullet of #55's product recommendation closes the loop: the picker only ever surfaces shuffleable playlists. #58's SpotifyPlaylistInaccessibleError → 400 unshuffleable_playlist is now a true safety net (covers picker-render → shuffle-time races) rather than the primary defense.

Collaborative branch is kept in based on Spotify's documented behaviour; the smoke test didn't exercise it (test account had no collaboratives). The defense-in-depth catches the case if a collaborative playlist also 403s in practice.

Test plan

Closes #55.

The picker was returning every playlist from /v1/me/playlists,
including ones the caller follows but doesn't own. Post-Feb-2026
those rows can't be shuffled — /v1/playlists/{id}/items returns
403 Forbidden against non-owned, non-collaborative playlists (see
the smoke test in #55).

/api/me/playlists now fetches /v1/me in parallel with the playlist
list and Liked Songs count, then filters the playlist list to keep
only items where the caller is the owner or a collaborator. The
parallel fetch keeps the picker's first paint at one round-trip's
worth of latency. Liked Songs sits outside the filter — the caller
owns their Saved Tracks library by definition.

New `fetchCurrentUserId` helper in src/lib/spotify/me.ts follows
the same boundary pattern as the other Spotify fetches: hard-coded
origin, bounded timeout, status-coded error mapping, schema
validation. Reuses the existing error classes from playlists.ts;
the route's existing catch branches cover every failure mode the
new fetch can throw.

The defense-in-depth from #58 (`SpotifyPlaylistInaccessibleError`
→ 400 unshuffleable_playlist) remains in /api/shuffle so a race
where the user loses access between picker render and shuffle
still produces a coherent envelope.

Closes #55.
@ivorycrayon
ivorycrayon merged commit dcfc3be into main May 23, 2026
1 check passed
@ivorycrayon
ivorycrayon deleted the feat/filter-non-shuffleable-playlists branch May 23, 2026 17:54
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Handle Feb-2026 playlist content visibility restriction

1 participant