# Music Services Architecture Analysis ## Current Services Overview ### 🎵 **Spotify (Web Player API) - PRIMARY METADATA SOURCE** **Purpose**: Core track, album, artist, playlist metadata **Status**: ✅ Working - No account required **Used for**: - Track names, artists, albums, durations - Playlist information and track listings - Artist discography and top tracks - Album details and tracklists - Preview URLs (when available) **Implementation**: SpotiFLAC-style Web Player API - TOTP authentication (no account needed) - GraphQL persisted queries - Client token requirement - Rate limiting with retries --- ### 🎼 **MusicBrainz - ENRICHMENT METADATA** **Purpose**: Comprehensive music database enrichment **Status**: ✅ Working - Free API **Used for**: - **Genre information** (primary use case) - **ISRC codes** for cross-platform matching - **Release dates and detailed discography** - **Artist relationships and aliases** - **Cover art URLs** (via Cover Art Archive) - **Track positioning and numbering** - **Country-specific release information** - **User-generated tags and ratings** **Key Benefits**: - **Free and open** (no API keys needed) - **Comprehensive database** with 1.5M+ artists - **Genre data** that Spotify doesn't provide - **ISRC codes** for streaming service matching --- ### 📻 **Last.fm - SOCIAL & LISTENING DATA** **Purpose**: Social music features and listening statistics **Status**: ⚠️ Optional - Requires user API key **Used for**: - **Scrobbling** (track what users listen to) - **Play counts** and listening statistics - **User recommendations** and similar artists - **Social features** (friends, groups) - **Charts** and trending data - **Personalized recommendations** **Current Issues**: - Requires user API key and authentication - Optional dependency (can be disabled) - Used mainly for social features --- ## 🎯 **Optimal Architecture Recommendation** ### **Core Required Services** 1. **Spotify Web Player API** (Primary metadata) 2. **MusicBrainz** (Genre enrichment + ISRC matching) ### **Optional Services** 3. **Song.link** (Cross-platform streaming URLs) 4. **Last.fm** (Social features - can be disabled) --- ## 💡 **Your Question: Listening Count Implementation** You're absolutely right! Here's how we can handle listening counts: ### **Option 1: Use Spotify Data (Recommended)** ```python # Spotify provides playcount in Web Player API track_data = { "playcount": 1234567, # Real Spotify play count "popularity": 85, # Spotify popularity score } ``` **Pros**: - Real-time data from Spotify - No additional API calls needed - Accurate and up-to-date **Cons**: - Depends on Spotify API availability ### **Option 2: Use MusicBrainz Data** ```python # MusicBrainz has rating and tag data mb_data = { "rating": 4.2, # User rating (0-5) "tagList": ["rock", "popular"], # User tags "playCount": None, # Not directly available } ``` **Pros**: - Free and always available - Community-driven data - Genre information included **Cons**: - No direct play count - Less accurate for popularity ### **Option 3: Local Tracking (Hybrid Approach)** ```python # Track local plays in database + enrich with external data local_stats = { "localPlayCount": 156, # Times played in SwingMusic "spotifyPlayCount": 1234567, # From Spotify API "lastfmPlayCount": 98765, # From Last.fm (if available) } ``` --- ## 🚀 **Recommended Implementation** ### **Required Services (Always On)** ```python class UnifiedMetadataClient: def __init__(self): self.spotify = SpotifyWebPlayerClient() # Primary metadata self.musicbrainz = MusicBrainzClient() # Genre enrichment self.songlink = SongLinkClient() # Cross-platform URLs def get_track(self, track_id): # Get core data from Spotify spotify_data = self.spotify.get_track(track_id) # Enrich with MusicBrainz genre data if spotify_data.isrc: mb_data = self.musicbrainz.get_by_isrc(spotify_data.isrc) spotify_data.genres = mb_data.genres # Get cross-platform URLs cross_platform = self.songlink.get_links(track_id) spotify_data.streaming_urls = cross_platform return spotify_data ``` ### **Optional Services (User Configurable)** ```python class OptionalFeatures: def __init__(self): self.lastfm_enabled = user_config.get("lastfm_enabled", False) self.lastfm = LastFmClient() if self.lastfm_enabled else None def get_play_counts(self, track_id): counts = { "spotify": self.spotify.get_playcount(track_id), "local": self.local_db.get_playcount(track_id), } if self.lastfm_enabled: counts["lastfm"] = self.lastfm.get_playcount(track_id) return counts ``` --- ## 📊 **Summary Table** | Service | Purpose | Required? | Data Provided | |---------|---------|-----------|---------------| | **Spotify** | Core metadata | ✅ Yes | Names, artists, albums, durations, play counts | | **MusicBrainz** | Genre enrichment | ✅ Yes | Genres, ISRC, cover art, release info | | **Song.link** | Cross-platform URLs | ✅ Yes | Tidal, Qobuz, Amazon, Deezer links | | **Last.fm** | Social features | ⚠️ Optional | Scrobbles, social stats, recommendations | --- ## 🎯 **Final Recommendation** **Keep Last.fm optional** as you suggested. Use **Spotify play counts** as the primary listening count source, with **local tracking** as backup. This gives you: 1. **Real Spotify data** (most accurate) 2. **Local statistics** (always available) 3. **Optional social features** (user choice) 4. **Genre enrichment** (from MusicBrainz) 5. **Cross-platform matching** (from Song.link) This architecture is **robust, free, and user-controllable**! 🎉