Local tools for turning Spotify extended streaming-history JSON exports into normalized JSON, ranked summaries, Last.fm genre enrichment, and a static music report prototype.
The intended flow is:
Spotify raw JSON exports
-> npm run spotify:analyze
-> npm run lastfm:enrich (optional)
-> npm run music:report
-> output/music-report/index.html
- Node.js 20 or newer.
- Spotify extended streaming-history export JSON files.
- A Last.fm API key if you want genre enrichment.
No npm dependencies are currently required.
Create a raw/ folder at the project root and put your Spotify export JSON files in it:
mkdir -p rawExpected examples:
raw/Streaming_History_Audio_2024.json
raw/Streaming_History_Audio_2025.json
raw/Streaming_History_Video_2025.json
The output/ folder is created automatically by npm run spotify:analyze if it does not already exist.
These local folders/files are intentionally ignored by Git:
raw/
output/
.env.local
.DS_Store
Create .env.local only if you want Last.fm genre enrichment:
LASTFM_API_KEY=your_lastfm_api_keySpotify API keys are not needed. Spotify no longer provides useful genre data for this project, so genre enrichment uses Last.fm artist tags.
npm run spotify:analyzeThis reads JSON files from raw/, validates them against the schemas in schemas/, normalizes stream events, and writes summary files to output/.
Main outputs:
output/stream-events-0001.json
output/stream-events.ndjson
output/stream-events-manifest.json
output/top-artists.json
output/top-songs.json
output/top-albums.json
output/top-videos.json
output/yearly-trends.json
output/monthly-trends.json
output/genre-candidates.json
output/genre-candidates-top-1000.json
output/odd-findings.json
output/report.json
output/import.sql
output/validation-warnings.json
Optional flags:
npm run spotify:analyze -- ./raw --output output --chunk-size 10000Rankings are primarily sorted by total milliseconds played, with total streams as the tie-breaker.
npm run lastfm:enrichThis reads:
output/genre-candidates-top-1000.json
and writes:
output/genre-candidates-top-1000.enriched.json
output/artist-genres.lastfm.json
output/lastfm-artist-genre-cache.json
The cache lets the script resume without re-fetching artists it already looked up. If Last.fm rate-limits the run, the script stops and writes partial output from whatever is already cached.
Optional flags:
npm run lastfm:enrich -- --limit 1000 --delay-ms 250 --max-tags 8 --min-tag-count 5npm run music:reportThis reads the generated summary files and writes:
output/music-report/index.html
output/music-report/styles.css
Open output/music-report/index.html in a browser to view the static 80s-themed report prototype.
The report also reads output/stream-events.ndjson to build the “Past 3 Months” section, which shows the top 50 artists, songs, and albums by listen count for the three months ending at the latest play date in the export.
Last.fm enrichment is optional. If output/artist-genres.lastfm.json is absent, the report still builds with an empty genre map; genre-based sections will be empty or neutral while listening totals and rankings remain available.
npm run demoThis analyzes the small, fictional fixture in examples/ and writes a self-contained report to output/demo/music-report/index.html. The fixture contains no personal Spotify data.
The generated HTML currently includes these sections:
- Hero summary with total hours, streams, peak year, and genre match count.
- Yearly Signal: listening hours and skip rate by year.
- Recent Months: compact pulse chart for the latest 36 months.
- Peak Months: highest listening months by hours played.
- Genre Weather: Last.fm genre tags weighted by listening time.
- Eras: detected artist, album, and genre chapters where something suddenly dominated a month or season.
- Musical DNA: foundational artists, evolution artists, one-season wonders, comfort artists, and discovery artists.
- Past Three Months Mood Read: possible emotions, mental state, mood, explanation, and evidence.
- Whole Timeframe Mood Read: possible emotions, mental state, mood, explanation, and evidence.
- Past Three Months Mood Graph: daily low-to-high mood score graph.
- Whole Timeframe Mood Graph: monthly low-to-high mood score graph.
- Past 3 Months: top 50 artists, songs, and albums by listen count.
- All-time leaderboards: top artists, songs, albums, and videos.
- Long Lifespans: artists active across the most years.
- Repeat Track Fixations: strongest single-day song repeats.
- Album Fixation Weeks: strongest one-week album repeats.
- Video / Music Ratio: audio versus video listening split.
- Put Spotify export files in
raw/. - Run
npm run spotify:analyze. - Optionally add
LASTFM_API_KEYto.env.local. - Optionally run
npm run lastfm:enrichfor genre-aware sections. - Run
npm run music:report. - Review
output/music-report/index.html. - Keep the raw and generated personal data local; both
raw/andoutput/are Git-ignored.
Licensed under the MIT License.
