You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This commit was created on GitHub.com and signed with GitHub’s verified signature.
New features
MCP server (mcp.php) — expose this account's data as tools an MCP-compatible AI client (Claude, ChatGPT/OpenAI, or anything else speaking the standard) can call: get_now_playing, list_scrobbles (individual listening history with exact dates/times), top_artists, top_tracks, genre_breakdown, lifetime_stats, and one widget_* tool per insight widget. Gated behind a bearer API key (mcp_api_key in config.php, blank/disabled by default). Falls back honestly — "available": false plus current backfill coverage — rather than fabricating data when the local history doesn't reach far enough back yet.
Local library sync — cron.php now builds a compressed local snapshot of your full scrobble history (artist + track + timestamp), paced in bounded batches across runs. Once a period is covered, Favourite Tracks/Trending/Genre Breakdown compute from it directly with exact calendar boundaries (real Jan 1, real 1st-of-month, real Monday) instead of Last.fm's approximate rolling windows, and with zero live API calls.
Genre Breakdown, rebuilt: once local history covers a period, it scores every distinct artist scrobbled (not a capped sample) with no "Other" catch-all. Optionally filtered down to Spotify's own genre vocabulary when Spotify credentials are configured, collapsing near-duplicate spellings (Rnb/R&B, Dnb/Drum N Bass/Drum & Bass) onto one entry. A "Show: all / above 1% / 2% / 5%" dropdown keeps a long genre list from making the page too tall.
Before They Were Famous, verified — each candidate is checked against Spotify's own artist search before inclusion, filtering out soundtrack/compilation scrobbles where the "artist" is really an album or production title (e.g. "Monster High the Movie"). Both this widget and Obscurity Index also now filter out multi-artist scrobble credits ("Artist A, Artist B & Artist C"), which Last.fm otherwise treats as one low-listener "artist".
Distance Listened — each comparison now shows a progress bar for the fractional remainder (e.g. "~74 flights, 64%") instead of a bare decimal.
BPM Average — the plain pulsing dot is now an animated ECG-style heart-monitor trace, scrolling at the real average-BPM's beat interval.
YouTube quota protection — a local 24-hour call counter (youtube_daily_limit, default 100) so quick-listen lookups can't exceed the free API quota.
Fixes
ListenLinks caching bug: a failed Spotify/YouTube request (auth hiccup, network error) was being cached as a permanent "no match" for 30 days — indistinguishable from a genuine miss. Now only a completed API response gets cached, so a transient failure retries fresh next time instead of freezing a track's button on "Search" for a month.
Architecture
Genre tag scoring and Spotify's Client Credentials auth are now shared, reusable pieces (lib/Spotify.php), used by both quick-listen links and the new artist/genre verification.
LibrarySync's artist/track query methods are now public with an optional upper time bound, plus new rawScrobbles()/coverage() methods for arbitrary date-range queries — the foundation the MCP server's list_scrobbles tool is built on.
Genre tag coverage (for the uncapped local Genre Breakdown) and scrobble history backfill both grow via their own paced background jobs in cron.php, never a live lookup burst.
Documentation
README additions: Local library sync, Genre verification (including an honest note that Spotify's genre vocabulary is a fixed, dated list since Spotify retired the live endpoint), and a full MCP server section with step-by-step connection instructions for the Claude app, Claude Code, the OpenAI API, and ChatGPT's current OAuth-only limitation.