Files
metadata-agregator/docs/research/minim/analysis/INTEGRATIONS.md
T
Alexander a1f6701bac feat: initial implementation of metadata aggregator
- gRPC service with MusicBrainz provider
- PostgreSQL schema with migrations
- Service layer with database-first caching
- Repository pattern for data access
- YAML configuration support
- Research documentation for 17 music metadata projects
2026-04-28 16:28:53 +02:00

923 lines
25 KiB
Markdown

# minim: Service Integrations
## Overview
minim integrates with five music services, each with different authentication methods, API coverage, and capabilities:
| Service | Auth Method | API Type | Streaming | Lyrics | Credits | Rate Limit |
|---------|-------------|----------|-----------|--------|---------|------------|
| Discogs | OAuth 1.0a, Personal Token | Public | No | No | Yes | 60/min (auth), 25/min (unauth) |
| iTunes | None | Public | No | No | No | ~20/min |
| Qobuz | Password Grant | Private | Yes | No | No | Unknown |
| Spotify | OAuth 2.0 (4 flows) | Public + Private | No | Yes | No | 180/30sec |
| TIDAL | OAuth 2.0 (PKCE, Client Creds) | Public + Private | Yes | Yes | Yes | Unknown |
## Discogs Integration
### Service Overview
**Purpose:** Music database, marketplace, collection management
**Website:** https://www.discogs.com
**API Documentation:** https://www.discogs.com/developers
**API Type:** Public, documented, RESTful
### Authentication
**Method 1: OAuth 1.0a**
**Setup:**
1. Create app at https://www.discogs.com/settings/developers
2. Obtain consumer key and consumer secret
3. Implement OAuth 1.0a flow (request token → user authorization → access token)
**Implementation:**
```python
from minim import discogs
api = discogs.API(
consumer_key="Abcd1234Efgh5678",
consumer_secret="IjklMnopQrstUvwx"
)
# OAuth flow (opens browser)
api.set_access_token()
# Tokens saved to ~/minim.cfg
```
**Method 2: Personal Access Token**
**Setup:**
1. Generate token at https://www.discogs.com/settings/developers
2. Use token directly (no OAuth flow)
**Implementation:**
```python
api = discogs.API(personal_access_token="YourPersonalToken")
```
**Comparison:**
- OAuth 1.0a: Required for write operations (collection, wantlist), higher rate limit
- Personal Token: Read-only, simpler setup, same rate limit as OAuth
### API Coverage
**Database:**
- Search releases, artists, labels, masters
- Get detailed information (tracklist, credits, images, identifiers)
- Browse by genre, style, format, year, country
**Marketplace:**
- Search listings
- Get listing details (price, condition, seller)
- Not implemented in minim (read-only marketplace access)
**Collection:**
- Get user's collection folders
- List items in folder
- Add/remove releases
- Update notes and rating
**Wantlist:**
- Get user's wantlist
- Add/remove releases
- Not implemented: Update notes
**User:**
- Get user profile
- Get user submissions (releases, artists, labels)
- Not implemented in minim
### Data Model
**Release Object:**
```json
{
"id": 249504,
"title": "OK Computer",
"artists": [{"name": "Radiohead", "id": 3840}],
"labels": [{"name": "Parlophone", "catno": "7243 8 55229 2 5"}],
"formats": [{"name": "CD", "qty": "1", "descriptions": ["Album"]}],
"year": 1997,
"country": "UK",
"genres": ["Electronic", "Rock"],
"styles": ["Alternative Rock", "Experimental"],
"tracklist": [
{"position": "1", "title": "Airbag", "duration": "4:44"},
{"position": "2", "title": "Paranoid Android", "duration": "6:23"}
],
"identifiers": [
{"type": "Barcode", "value": "724385522925"}
],
"images": [
{"type": "primary", "uri": "https://...", "width": 600, "height": 600}
]
}
```
**Artist Object:**
```json
{
"id": 3840,
"name": "Radiohead",
"profile": "English rock band formed in 1985...",
"members": [
{"name": "Thom Yorke", "id": 239},
{"name": "Jonny Greenwood", "id": 240}
],
"urls": ["https://www.radiohead.com"],
"images": [...]
}
```
### Rate Limiting
**Authenticated:** 60 requests per minute
**Unauthenticated:** 25 requests per minute
**Headers:**
- `X-Discogs-Ratelimit`: Total requests allowed per minute
- `X-Discogs-Ratelimit-Remaining`: Requests remaining in current window
- `X-Discogs-Ratelimit-Used`: Requests used in current window
**Enforcement:** HTTP 429 (Too Many Requests) when limit exceeded.
**minim Implementation:** Does not check rate limit headers. Caller responsible for tracking.
### Use Cases
1. **Metadata Enrichment:** Get detailed release information (catalog numbers, barcodes, formats)
2. **Collection Management:** Sync local library with Discogs collection
3. **Credits Extraction:** Get producer, engineer, musician credits from tracklist
4. **Format Identification:** Determine pressing details (country, year, label, catalog number)
### Limitations
- No streaming or preview URLs
- No lyrics
- Marketplace write operations not implemented
- User-submitted data (quality varies)
- Rate limiting requires manual tracking
## iTunes Integration
### Service Overview
**Purpose:** Public music catalog search and lookup
**Website:** https://www.apple.com/itunes
**API Documentation:** https://developer.apple.com/library/archive/documentation/AudioVideo/Conceptual/iTuneSearchAPI
**API Type:** Public, documented, RESTful
### Authentication
**None required.** iTunes Search API is completely public.
**Implementation:**
```python
from minim import itunes
api = itunes.SearchAPI()
results = api.search("Radiohead", media="music", entity="musicArtist")
```
### API Coverage
**Search:**
- Search by term across all media types
- Filter by media (music, movie, podcast, audiobook, etc.)
- Filter by entity (song, album, artist, etc.)
- Filter by attribute (artist name, album name, song name, etc.)
**Lookup:**
- Lookup by iTunes ID
- Lookup by UPC (album barcode)
- Lookup by ISBN (books)
- Lookup by AMG (All Music Guide) ID
**Not Available:**
- Streaming URLs
- Lyrics
- User library access
- Playlist management
### Data Model
**Track Object:**
```json
{
"trackId": 1109731797,
"trackName": "Creep",
"artistName": "Radiohead",
"collectionName": "Pablo Honey",
"collectionId": 1109731533,
"artistId": 657515,
"trackNumber": 2,
"trackCount": 12,
"discNumber": 1,
"discCount": 1,
"releaseDate": "1993-02-22T08:00:00Z",
"primaryGenreName": "Alternative",
"trackTimeMillis": 238640,
"country": "USA",
"isrc": "GBAYE9200070",
"artworkUrl30": "https://.../30x30bb.jpg",
"artworkUrl60": "https://.../60x60bb.jpg",
"artworkUrl100": "https://.../100x100bb.jpg",
"previewUrl": "https://.../preview.m4a",
"trackViewUrl": "https://music.apple.com/us/album/creep/1109731533?i=1109731797"
}
```
**Album Object:**
```json
{
"collectionId": 1109731533,
"collectionName": "Pablo Honey",
"artistName": "Radiohead",
"artistId": 657515,
"trackCount": 12,
"releaseDate": "1993-02-22T08:00:00Z",
"primaryGenreName": "Alternative",
"copyright": "℗ 1993 XL Recordings Ltd.",
"country": "USA",
"artworkUrl100": "https://.../100x100bb.jpg",
"collectionViewUrl": "https://music.apple.com/us/album/pablo-honey/1109731533"
}
```
### Rate Limiting
**Limit:** Approximately 20 requests per minute (undocumented)
**Enforcement:** HTTP 403 (Forbidden) when limit exceeded
**Headers:** No rate limit headers provided
**Recommendation:** Implement exponential backoff on 403 errors.
### Use Cases
1. **Quick Metadata Lookup:** Get basic track/album info without authentication
2. **UPC Lookup:** Find albums by barcode
3. **Preview URLs:** Get 30-second preview clips (M4A format)
4. **Artwork:** Get album artwork in multiple sizes (30x30, 60x60, 100x100)
### Limitations
- No high-resolution artwork (max 100x100 pixels, can be scaled to 600x600 by changing URL)
- No streaming URLs (only 30-second previews)
- No lyrics
- No user-specific data
- Rate limit is undocumented and may change
- Search results limited to 200 items
## Qobuz Integration
### Service Overview
**Purpose:** High-resolution music streaming and downloads
**Website:** https://www.qobuz.com
**API Documentation:** None (private API)
**API Type:** Private, undocumented, reverse-engineered
### Authentication
**Method:** Password Grant OAuth 2.0
**Setup:**
1. Qobuz account with active subscription
2. App ID and secret auto-extracted from web player JavaScript
3. Email and password for authentication
**Implementation:**
```python
from minim import qobuz
api = qobuz.PrivateAPI(
email="user@example.com",
password="YourPassword"
)
# Automatic app_id/secret extraction and token acquisition
api.set_access_token()
```
**App ID/Secret Extraction:**
```python
def _get_app_credentials(self):
# Fetch Qobuz web player
response = requests.get("https://play.qobuz.com")
html = response.text
# Extract bundle URL from HTML
bundle_url_match = re.search(r'<script src="(/resources/\d+\.\d+\.\d+-[a-z]\d+/bundle\.js)"', html)
bundle_url = "https://play.qobuz.com" + bundle_url_match.group(1)
# Fetch bundle JavaScript
bundle_js = requests.get(bundle_url).text
# Extract app_id and secrets array
app_id = re.search(r'production:{api:{appId:"(\d+)"', bundle_js).group(1)
secrets = re.findall(r'[a-f0-9]{32}', bundle_js)
# Test secrets to find valid one
for secret in secrets:
if self._test_secret(app_id, secret):
return app_id, secret
```
**Security Note:** This method violates Qobuz terms of service. Use at your own risk.
### API Coverage
**Catalog:**
- Search tracks, albums, artists, playlists
- Get detailed information
- Browse by genre, new releases, charts
**Streaming:**
- Get streaming URLs with quality selection
- Quality levels: MP3 320kbps, FLAC 16/44.1, FLAC 24/96, FLAC Hi-Res (up to 24/192)
- Download tracks (within subscription terms)
**User Library:**
- Get user playlists
- Create, update, delete playlists
- Add/remove tracks from playlists
- Get favorites (tracks, albums, artists)
- Add/remove favorites
**Not Available:**
- Lyrics
- Credits (producer, engineer, etc.)
- User playback history
### Data Model
**Track Object:**
```json
{
"id": 12345678,
"title": "Creep",
"duration": 238,
"track_number": 2,
"media_number": 1,
"isrc": "GBAYE9200070",
"performer": {"name": "Radiohead", "id": 12345},
"album": {
"id": "0060254734729",
"title": "Pablo Honey",
"release_date_original": "1993-02-22",
"upc": "0060254734729",
"image": {
"small": "https://.../230x230.jpg",
"large": "https://.../600x600.jpg"
},
"label": {"name": "XL Recordings"}
},
"maximum_bit_depth": 16,
"maximum_sampling_rate": 44.1
}
```
**Streaming URL Response:**
```json
{
"url": "https://streaming.qobuz.com/...",
"format_id": 27,
"mime_type": "audio/flac",
"sampling_rate": 44.1,
"bit_depth": 16,
"restrictions": []
}
```
### Quality Levels
| Format ID | Quality | Codec | Bitrate/Depth | Subscription Tier |
|-----------|---------|-------|---------------|-------------------|
| 5 | MP3 | MP3 | 320 kbps | Studio |
| 6 | CD | FLAC | 16-bit/44.1kHz | Studio |
| 7 | Hi-Res | FLAC | 24-bit/96kHz | Studio Sublime |
| 27 | Hi-Res | FLAC | Up to 24-bit/192kHz | Studio Sublime |
**Availability:** Quality depends on:
1. User's subscription tier
2. Album's available formats
3. Geographic restrictions
### Rate Limiting
**Unknown.** Private API does not document rate limits.
**Observation:** Aggressive usage (>100 requests/minute) may trigger temporary blocks.
**Recommendation:** Implement conservative rate limiting (10-20 requests/minute).
### Use Cases
1. **High-Resolution Downloads:** Get FLAC files up to 24-bit/192kHz
2. **Metadata Enrichment:** Get detailed album info (label, UPC, release date)
3. **Playlist Management:** Sync playlists between services
4. **Favorites Sync:** Export/import favorite tracks
### Limitations
- Private API (may break without notice)
- Requires active subscription
- No lyrics or credits
- Geographic restrictions on content
- Terms of service violations (use at own risk)
## Spotify Integration
### Service Overview
**Purpose:** Music streaming, discovery, and social features
**Website:** https://www.spotify.com
**API Documentation:** https://developer.spotify.com/documentation/web-api
**API Type:** Public (Web API) + Private (Lyrics)
### Authentication
**Method:** OAuth 2.0 with four flow types
**Flow 1: Authorization Code**
- Full user access with refresh token
- Requires user login via browser
- Best for web applications
**Flow 2: PKCE (Proof Key for Code Exchange)**
- For mobile and desktop apps
- No client secret required
- Enhanced security for public clients
**Flow 3: Client Credentials**
- App-only access (no user context)
- No user login required
- Limited to catalog endpoints (no user library, playlists, playback)
**Flow 4: Web Player (Undocumented)**
- Extract `sp_dc` cookie from browser
- Access private endpoints (lyrics)
- Violates terms of service
**Implementation:**
```python
from minim import spotify
# Authorization Code flow
api = spotify.WebAPI(
client_id="your_client_id",
client_secret="your_client_secret",
redirect_uri="http://localhost:8888"
)
api.set_flow("authorization_code", scopes=[
"user-library-read",
"playlist-read-private",
"user-read-playback-state"
])
api.set_access_token() # Opens browser
# Client Credentials flow (no user login)
api = spotify.WebAPI(client_id="...", client_secret="...")
api.set_flow("client_credentials")
api.set_access_token()
```
### API Coverage
**Catalog:**
- Search tracks, albums, artists, playlists, shows, episodes
- Get detailed information
- Get related artists
- Get artist top tracks
- Get album tracks
- Get audio features (danceability, energy, tempo, etc.)
- Get audio analysis (detailed beat/bar/section analysis)
**User Library:**
- Get saved tracks, albums, shows, episodes
- Save/remove items
- Check if items are saved
**Playlists:**
- Get user playlists
- Get playlist details and tracks
- Create, update, delete playlists
- Add/remove tracks
- Reorder tracks
- Upload custom cover image
**Playback:**
- Get current playback state
- Get available devices
- Start/pause/skip playback
- Seek to position
- Set volume
- Toggle shuffle/repeat
- Transfer playback between devices
**Personalization:**
- Get top artists and tracks
- Get recently played tracks
- Get recommendations based on seeds
**Follow:**
- Follow/unfollow artists, users, playlists
- Check if following
- Get followed artists
**Browse:**
- Get featured playlists
- Get new releases
- Get categories
- Get category playlists
**Lyrics (Private API):**
- Get synchronized lyrics via Musixmatch integration
- Requires `sp_dc` cookie
### Data Model
**Track Object:**
```json
{
"id": "3n3Ppam7vgaVa1iaRUc9Lp",
"name": "Creep",
"artists": [
{"name": "Radiohead", "id": "4Z8W4fKeB5YxbusRsdQVPb"}
],
"album": {
"name": "Pablo Honey",
"id": "6AZv3m27uyRxi8KyJSfUxL",
"release_date": "1993-02-22",
"images": [
{"url": "https://.../640x640.jpg", "width": 640, "height": 640}
]
},
"duration_ms": 238640,
"track_number": 2,
"disc_number": 1,
"explicit": false,
"external_ids": {"isrc": "GBAYE9200070"},
"popularity": 82,
"preview_url": "https://.../preview.mp3"
}
```
**Audio Features Object:**
```json
{
"id": "3n3Ppam7vgaVa1iaRUc9Lp",
"danceability": 0.456,
"energy": 0.789,
"key": 7,
"loudness": -6.234,
"mode": 1,
"speechiness": 0.034,
"acousticness": 0.123,
"instrumentalness": 0.000012,
"liveness": 0.089,
"valence": 0.234,
"tempo": 92.456,
"duration_ms": 238640,
"time_signature": 4
}
```
**Lyrics Object (Private API):**
```json
{
"lyrics": {
"syncType": "LINE_SYNCED",
"lines": [
{"startTimeMs": "0", "words": "When you were here before", "syllables": []},
{"startTimeMs": "5230", "words": "Couldn't look you in the eye", "syllables": []}
],
"language": "en"
}
}
```
### Scopes
Spotify uses OAuth scopes to control API access. Common scopes:
**Library:**
- `user-library-read`: Read saved tracks/albums
- `user-library-modify`: Save/remove tracks/albums
**Playlists:**
- `playlist-read-private`: Read private playlists
- `playlist-read-collaborative`: Read collaborative playlists
- `playlist-modify-public`: Modify public playlists
- `playlist-modify-private`: Modify private playlists
**Playback:**
- `user-read-playback-state`: Read playback state
- `user-modify-playback-state`: Control playback
- `user-read-currently-playing`: Read currently playing track
**Personalization:**
- `user-top-read`: Read top artists and tracks
- `user-read-recently-played`: Read recently played tracks
**Follow:**
- `user-follow-read`: Read followed artists/users
- `user-follow-modify`: Follow/unfollow artists/users
**User:**
- `user-read-private`: Read user profile (country, product)
- `user-read-email`: Read user email
### Rate Limiting
**Limit:** Varies by endpoint, typically 180 requests per 30 seconds
**Enforcement:** HTTP 429 (Too Many Requests)
**Headers:**
- `Retry-After`: Seconds to wait before retrying
**Recommendation:** Implement exponential backoff and respect `Retry-After` header.
### Use Cases
1. **Comprehensive Metadata:** Get detailed track info, audio features, related artists
2. **Playlist Management:** Create, sync, and manage playlists
3. **Playback Control:** Build custom music players
4. **Music Discovery:** Get recommendations, browse new releases
5. **Lyrics Integration:** Display synchronized lyrics (via private API)
### Limitations
- No streaming URLs (only 30-second previews)
- No download capability
- Lyrics require private API (terms of service violation)
- Rate limiting varies by endpoint
- Some features require premium subscription
## TIDAL Integration
### Service Overview
**Purpose:** High-fidelity music streaming with MQA support
**Website:** https://www.tidal.com
**API Documentation:** Limited (mostly undocumented)
**API Type:** Public (limited) + Private (extensive)
### Authentication
**Method:** OAuth 2.0 (PKCE or Client Credentials)
**Public API:**
- Client credentials flow
- Limited endpoints (catalog search, basic info)
**Private API:**
- PKCE flow with user login
- Full access (streaming URLs, lyrics, credits)
**Implementation:**
```python
from minim import tidal
# Private API (full access)
api = tidal.PrivateAPI(
client_id="your_client_id",
client_secret="your_client_secret"
)
api.set_flow("pkce")
api.set_access_token() # Opens browser for login
```
**Client ID/Secret:**
- Extracted from TIDAL desktop/mobile apps
- Not officially provided by TIDAL
- Use at own risk (terms of service violation)
### API Coverage
**Catalog:**
- Search tracks, albums, artists, playlists, videos
- Get detailed information
- Get artist top tracks and albums
- Get similar artists
- Get album review and credits
**Streaming:**
- Get streaming URLs with quality selection
- Quality levels: LOW (96kbps AAC), HIGH (320kbps AAC), LOSSLESS (FLAC 16/44.1), HI_RES (FLAC 24/96+), HI_RES_LOSSLESS (MQA)
- Manifest decryption for protected streams
**Lyrics:**
- Get synchronized lyrics (LRC format)
- Get plain text lyrics
**Credits:**
- Get detailed credits (producers, engineers, musicians, composers, etc.)
- Role-based organization
**User Library:**
- Get user playlists
- Create, update, delete playlists
- Add/remove tracks
- Get favorites (tracks, albums, artists, videos)
- Add/remove favorites
**Not Available:**
- Playback control (no remote control API)
- Audio features/analysis
- Recommendations (limited)
### Data Model
**Track Object:**
```json
{
"id": 12345678,
"title": "Creep",
"duration": 238,
"trackNumber": 2,
"volumeNumber": 1,
"isrc": "GBAYE9200070",
"explicit": false,
"audioQuality": "HI_RES",
"artists": [
{"name": "Radiohead", "id": 4050}
],
"album": {
"id": 1234567,
"title": "Pablo Honey",
"releaseDate": "1993-02-22",
"cover": "01234567-89ab-cdef-0123-456789abcdef",
"upc": "0060254734729",
"numberOfTracks": 12,
"audioQuality": "HI_RES"
},
"streamStartDate": "1993-02-22T00:00:00.000Z"
}
```
**Streaming Manifest:**
```json
{
"mimeType": "audio/flac",
"codecs": "flac",
"encryptionType": "NONE",
"urls": ["https://streaming.tidal.com/..."],
"soundQuality": "HI_RES",
"bitDepth": 24,
"sampleRate": 96000
}
```
**Lyrics Object:**
```json
{
"trackId": 12345678,
"lyricsProvider": "Musixmatch",
"providerLyricsId": "12345",
"lyrics": "When you were here before\nCouldn't look you in the eye...",
"subtitles": "[00:00.00] When you were here before\n[00:05.23] Couldn't look you in the eye..."
}
```
**Credits Object:**
```json
{
"credits": [
{
"type": "Producers",
"contributors": [
{"name": "Sean Slade", "id": 12345},
{"name": "Paul Q. Kolderie", "id": 67890}
]
},
{
"type": "Performers",
"contributors": [
{"name": "Thom Yorke", "id": 111, "role": "Vocals"},
{"name": "Jonny Greenwood", "id": 222, "role": "Guitar"}
]
}
]
}
```
### Quality Levels
| Quality | Codec | Bitrate/Depth | Subscription Tier |
|---------|-------|---------------|-------------------|
| LOW | AAC | 96 kbps | Free (trial) |
| HIGH | AAC | 320 kbps | HiFi |
| LOSSLESS | FLAC | 16-bit/44.1kHz | HiFi |
| HI_RES | FLAC | 24-bit/96kHz+ | HiFi Plus |
| HI_RES_LOSSLESS | MQA | 24-bit/96kHz+ (MQA) | HiFi Plus |
**MQA (Master Quality Authenticated):**
- Proprietary format by Meridian Audio
- Requires MQA-compatible DAC for full unfolding
- Software decoding provides 24-bit/96kHz
### Manifest Decryption
Some streaming URLs are encrypted. minim handles decryption:
```python
def _decrypt_manifest(self, manifest: dict) -> str:
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.backends import default_backend
import base64
# Extract encrypted URL
encrypted_url = manifest["urls"][0]
# Decrypt using AES-128-CTR
key = base64.b64decode(manifest["encryptionKey"])
nonce = base64.b64decode(manifest["nonce"])
cipher = Cipher(
algorithms.AES(key),
modes.CTR(nonce),
backend=default_backend()
)
decryptor = cipher.decryptor()
decrypted = decryptor.update(base64.b64decode(encrypted_url)) + decryptor.finalize()
return decrypted.decode("utf-8")
```
### Rate Limiting
**Unknown.** Private API does not document rate limits.
**Observation:** Moderate usage (<50 requests/minute) appears safe.
**Recommendation:** Implement conservative rate limiting and exponential backoff on errors.
### Use Cases
1. **High-Fidelity Streaming:** Get FLAC files up to 24-bit/192kHz and MQA
2. **Comprehensive Credits:** Get detailed production credits
3. **Synchronized Lyrics:** Display time-synced lyrics
4. **Metadata Enrichment:** Get authoritative release dates, ISRCs, UPCs
5. **Playlist Management:** Sync playlists between services
### Limitations
- Private API (may break without notice)
- Requires active subscription (HiFi or HiFi Plus for lossless)
- Client ID/secret extraction violates terms of service
- No playback control API
- No audio features/analysis
- Geographic restrictions on content
## Integration Comparison
### Authentication Complexity
**Simplest:** iTunes (no auth)
**Simple:** Discogs (personal token), Spotify (client credentials)
**Moderate:** Spotify (authorization code), TIDAL (PKCE)
**Complex:** Qobuz (app_id extraction + password grant)
### API Documentation Quality
**Best:** Spotify (comprehensive, well-maintained)
**Good:** Discogs, iTunes
**Poor:** TIDAL (limited public docs)
**None:** Qobuz (fully reverse-engineered)
### Streaming Capability
**High-Resolution:** Qobuz (up to 24/192 FLAC), TIDAL (up to 24/192 FLAC + MQA)
**Lossless:** TIDAL (16/44.1 FLAC)
**Lossy:** TIDAL (AAC), Qobuz (MP3)
**Preview Only:** Spotify (30sec MP3), iTunes (30sec M4A)
**None:** Discogs
### Metadata Richness
**Credits:** TIDAL (excellent), Discogs (good), others (none)
**Lyrics:** TIDAL (synced), Spotify (synced, private API), others (none)
**Audio Features:** Spotify (excellent), others (none)
**Catalog Info:** All services (good)
### Terms of Service Compliance
**Compliant:** Discogs (public API), iTunes (public API), Spotify (public Web API)
**Questionable:** Spotify (private lyrics API)
**Violation:** Qobuz (app_id extraction, private API), TIDAL (client_id extraction, private API)
## Summary
minim provides comprehensive integration with five music services, each serving different use cases:
- **Discogs:** Best for credits, catalog numbers, and collection management
- **iTunes:** Best for quick, unauthenticated metadata lookup
- **Qobuz:** Best for high-resolution downloads (within subscription terms)
- **Spotify:** Best for comprehensive metadata, audio features, and playlist management
- **TIDAL:** Best for high-fidelity streaming, credits, and synchronized lyrics
All integrations follow consistent patterns (authentication, request handling, error raising) while exposing service-specific features. The private API usage (Qobuz, Spotify lyrics, TIDAL) provides powerful capabilities but carries legal and stability risks.
For a metadata aggregator project, prioritize public APIs (Spotify Web API, Discogs, iTunes) for production use, and use private APIs only for research or personal projects.