docs: add BPM enrichment design spec
This commit is contained in:
76
docs/superpowers/specs/2026-05-25-bpm-enrichment-design.md
Normal file
76
docs/superpowers/specs/2026-05-25-bpm-enrichment-design.md
Normal file
@@ -0,0 +1,76 @@
|
||||
# BPM Enrichment Design
|
||||
|
||||
**Date:** 2026-05-25
|
||||
**Status:** Approved
|
||||
|
||||
## Goal
|
||||
|
||||
Add a `bpm` field to every track object returned by the MCP server, sourced from Spotify's `/audio-features` endpoint.
|
||||
|
||||
## Scope
|
||||
|
||||
Three tools return track objects and must be updated:
|
||||
- `get_playlist_tracks`
|
||||
- `list_saved_tracks`
|
||||
- `search_tracks`
|
||||
|
||||
No new MCP tools. No changes to `server.py`.
|
||||
|
||||
## Architecture
|
||||
|
||||
### New helper: `_enrich_with_bpm(sp, tracks)`
|
||||
|
||||
Location: `spotify_client.py`
|
||||
|
||||
- Takes a `spotipy.Spotify` client and a list of already-parsed track dicts.
|
||||
- Extracts track IDs from each dict's `"id"` field.
|
||||
- Slices into chunks of 100 (API limit) and calls `sp.audio_features(ids)` once per chunk.
|
||||
- Matches results back by position and sets `track["bpm"]` to the rounded tempo float.
|
||||
- If the API returns `None` for a track (features unavailable), sets `bpm` to `null`.
|
||||
- Mutates the list in place; returns nothing.
|
||||
|
||||
### Changes to track parsers
|
||||
|
||||
Each of the three functions adds `"id": track["id"]` to the dict it builds (currently absent from the output). After the full track list is assembled, each function calls `_enrich_with_bpm(sp, results)` before returning.
|
||||
|
||||
## Data Shape
|
||||
|
||||
Before:
|
||||
```json
|
||||
{
|
||||
"name": "oh baby",
|
||||
"artists": ["LCD Soundsystem"],
|
||||
"album": "american dream",
|
||||
"duration_ms": 349452,
|
||||
"duration": "5:49",
|
||||
"popularity": null,
|
||||
"uri": "spotify:track:53PkA8aXiwH4ppa0V0iO7o",
|
||||
"added_at": "2026-05-22T17:41:18Z"
|
||||
}
|
||||
```
|
||||
|
||||
After:
|
||||
```json
|
||||
{
|
||||
"name": "oh baby",
|
||||
"artists": ["LCD Soundsystem"],
|
||||
"album": "american dream",
|
||||
"duration_ms": 349452,
|
||||
"duration": "5:49",
|
||||
"popularity": null,
|
||||
"uri": "spotify:track:53PkA8aXiwH4ppa0V0iO7o",
|
||||
"id": "53PkA8aXiwH4ppa0V0iO7o",
|
||||
"added_at": "2026-05-22T17:41:18Z",
|
||||
"bpm": 128.0
|
||||
}
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
- `sp.audio_features()` may return `None` entries for tracks with no features (rare, e.g. local files, some podcasts). These get `bpm: null`.
|
||||
- Any exception from `sp.audio_features()` propagates naturally — same behaviour as the rest of the client.
|
||||
|
||||
## API Cost
|
||||
|
||||
- `get_playlist_tracks` / `list_saved_tracks`: 1 extra API call per 100 tracks (batched).
|
||||
- `search_tracks`: at most 1 extra call (max 50 results per search).
|
||||
Reference in New Issue
Block a user