a1f6701bac
- 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
1162 lines
27 KiB
Markdown
1162 lines
27 KiB
Markdown
# Lidarr Metadata API - API Reference
|
|
|
|
## Base URL and Versioning
|
|
|
|
**Production deployment**:
|
|
- Stable: `https://api.lidarr.audio/api/v0.3`
|
|
- Testing: `https://api.lidarr.audio/api/testing`
|
|
|
|
**Local development**:
|
|
- Default: `http://localhost:5001`
|
|
- Configurable via `APPLICATION_ROOT` environment variable
|
|
|
|
**Version strategy**: Dual deployment with stable (v0.3) and testing versions running simultaneously.
|
|
|
|
## Authentication
|
|
|
|
**Read endpoints**: No authentication required (public access)
|
|
|
|
**Write endpoints**: API key authentication via header
|
|
|
|
```http
|
|
POST /invalidate HTTP/1.1
|
|
X-Api-Key: your-api-key-here
|
|
```
|
|
|
|
**Configuration**:
|
|
```bash
|
|
export INVALIDATE_APIKEY=your-secure-key
|
|
```
|
|
|
|
**Default**: `replaceme` (insecure, must change in production)
|
|
|
|
## Response Format
|
|
|
|
All endpoints return JSON with consistent structure.
|
|
|
|
### Common Response Headers
|
|
|
|
```http
|
|
HTTP/1.1 200 OK
|
|
Content-Type: application/json
|
|
Cache-Control: s-maxage=2592000, max-age=0
|
|
X-Application-Version: 10.0.0.0
|
|
```
|
|
|
|
**Cache-Control explanation**:
|
|
- `s-maxage=2592000`: CDN can cache for 30 days
|
|
- `max-age=0`: Clients must revalidate (always check CDN/origin)
|
|
|
|
### Error Response Format
|
|
|
|
```json
|
|
{
|
|
"error": "Artist not found",
|
|
"status": 404,
|
|
"mbid": "invalid-mbid-format"
|
|
}
|
|
```
|
|
|
|
**HTTP status codes**:
|
|
- `200`: Success
|
|
- `400`: Bad request (invalid parameters)
|
|
- `401`: Unauthorized (missing/invalid API key)
|
|
- `404`: Resource not found
|
|
- `429`: Rate limit exceeded
|
|
- `500`: Internal server error
|
|
- `503`: Service unavailable (database/cache down)
|
|
|
|
## Health and Version Endpoints
|
|
|
|
### GET /
|
|
|
|
**Purpose**: Health check and version information
|
|
|
|
**Parameters**: None
|
|
|
|
**Response**:
|
|
```json
|
|
{
|
|
"version": "10.0.0.0",
|
|
"status": "healthy",
|
|
"timestamp": "2025-04-28T12:34:56Z"
|
|
}
|
|
```
|
|
|
|
**Cache**: No caching
|
|
|
|
**Use case**: Load balancer health checks, monitoring
|
|
|
|
## Artist Endpoints
|
|
|
|
### GET /artist/{mbid}
|
|
|
|
**Purpose**: Retrieve complete artist metadata with releases
|
|
|
|
**Parameters**:
|
|
|
|
| Parameter | Type | Required | Default | Description |
|
|
|-----------|------|----------|---------|-------------|
|
|
| `mbid` | UUID | Yes | - | MusicBrainz artist ID |
|
|
| `primTypes` | String | No | All | Primary release types (comma-separated) |
|
|
| `secTypes` | String | No | All | Secondary release types (comma-separated) |
|
|
| `releaseStatuses` | String | No | All | Release statuses (comma-separated) |
|
|
|
|
**Primary release types**:
|
|
- `Album`: Studio albums
|
|
- `Single`: Singles
|
|
- `EP`: Extended plays
|
|
- `Broadcast`: Radio/TV broadcasts
|
|
- `Other`: Miscellaneous
|
|
|
|
**Secondary release types**:
|
|
- `Compilation`: Compilation albums
|
|
- `Soundtrack`: Movie/game soundtracks
|
|
- `Spokenword`: Audiobooks, poetry
|
|
- `Interview`: Interview recordings
|
|
- `Audiobook`: Audiobooks
|
|
- `Live`: Live recordings
|
|
- `Remix`: Remix albums
|
|
- `DJ-mix`: DJ mix albums
|
|
- `Mixtape/Street`: Mixtapes
|
|
|
|
**Release statuses**:
|
|
- `Official`: Official releases
|
|
- `Promotion`: Promotional releases
|
|
- `Bootleg`: Bootleg recordings
|
|
- `Pseudo-Release`: Pseudo-releases
|
|
|
|
**Example request**:
|
|
```http
|
|
GET /artist/5b11f4ce-a62d-471e-81fc-a69a8278c7da?primTypes=Album,Single&releaseStatuses=Official HTTP/1.1
|
|
```
|
|
|
|
**Response**:
|
|
```json
|
|
{
|
|
"Id": "5b11f4ce-a62d-471e-81fc-a69a8278c7da",
|
|
"OldIds": [],
|
|
"ArtistName": "Nirvana",
|
|
"SortName": "Nirvana",
|
|
"Disambiguation": "90s US grunge band",
|
|
"Overview": "Nirvana was an American rock band formed in Aberdeen, Washington, in 1987...",
|
|
"Type": "Group",
|
|
"Status": "Ended",
|
|
"Gender": null,
|
|
"Hometown": "Aberdeen, WA, United States",
|
|
"StartYear": 1987,
|
|
"EndYear": 1994,
|
|
"ArtistAliases": [
|
|
{
|
|
"Name": "ニルヴァーナ",
|
|
"SortName": "ニルヴァーナ",
|
|
"Locale": "ja",
|
|
"Primary": true
|
|
}
|
|
],
|
|
"Links": [
|
|
{
|
|
"Url": "https://www.spotify.com/artist/6olE6TJLqED3rqDCT0FyPh",
|
|
"Name": "spotify"
|
|
},
|
|
{
|
|
"Url": "https://www.allmusic.com/artist/mn0000352188",
|
|
"Name": "allmusic"
|
|
},
|
|
{
|
|
"Url": "https://www.discogs.com/artist/65450",
|
|
"Name": "discogs"
|
|
},
|
|
{
|
|
"Url": "https://en.wikipedia.org/wiki/Nirvana_(band)",
|
|
"Name": "wikipedia"
|
|
}
|
|
],
|
|
"Images": [
|
|
{
|
|
"Url": "https://imagecache.lidarr.audio/fanart/artist/5b11f4ce-a62d-471e-81fc-a69a8278c7da/poster.jpg",
|
|
"CoverType": "poster",
|
|
"Extension": ".jpg"
|
|
},
|
|
{
|
|
"Url": "https://imagecache.lidarr.audio/fanart/artist/5b11f4ce-a62d-471e-81fc-a69a8278c7da/banner.jpg",
|
|
"CoverType": "banner",
|
|
"Extension": ".jpg"
|
|
},
|
|
{
|
|
"Url": "https://imagecache.lidarr.audio/fanart/artist/5b11f4ce-a62d-471e-81fc-a69a8278c7da/logo.png",
|
|
"CoverType": "logo",
|
|
"Extension": ".png"
|
|
},
|
|
{
|
|
"Url": "https://imagecache.lidarr.audio/fanart/artist/5b11f4ce-a62d-471e-81fc-a69a8278c7da/fanart.jpg",
|
|
"CoverType": "fanart",
|
|
"Extension": ".jpg"
|
|
}
|
|
],
|
|
"Genres": [
|
|
"Grunge",
|
|
"Alternative Rock",
|
|
"Punk Rock"
|
|
],
|
|
"Rating": {
|
|
"Votes": 42,
|
|
"Value": 4.8
|
|
},
|
|
"Albums": [
|
|
{
|
|
"Id": "1b022e01-4da6-387b-8658-8678046e4cef",
|
|
"Title": "Nevermind",
|
|
"Disambiguation": "",
|
|
"ReleaseDate": "1991-09-24",
|
|
"Type": "Album",
|
|
"SecondaryTypes": [],
|
|
"Status": "Official",
|
|
"Genres": ["Grunge", "Alternative Rock"],
|
|
"Rating": {
|
|
"Votes": 156,
|
|
"Value": 4.9
|
|
},
|
|
"Images": [
|
|
{
|
|
"Url": "https://imagecache.lidarr.audio/cover/1b022e01-4da6-387b-8658-8678046e4cef/front.jpg",
|
|
"CoverType": "cover",
|
|
"Extension": ".jpg"
|
|
}
|
|
]
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
**Response fields**:
|
|
|
|
| Field | Type | Description |
|
|
|-------|------|-------------|
|
|
| `Id` | UUID | MusicBrainz artist ID |
|
|
| `OldIds` | Array | Historical MusicBrainz IDs (after merges) |
|
|
| `ArtistName` | String | Primary artist name |
|
|
| `SortName` | String | Name for alphabetical sorting |
|
|
| `Disambiguation` | String | Disambiguation comment |
|
|
| `Overview` | String | Artist biography (from Wikipedia) |
|
|
| `Type` | String | Artist type (Person, Group, Orchestra, etc.) |
|
|
| `Status` | String | Artist status (Active, Ended, etc.) |
|
|
| `Gender` | String | Gender (for Person type) |
|
|
| `Hometown` | String | Origin location |
|
|
| `StartYear` | Integer | Formation/birth year |
|
|
| `EndYear` | Integer | Dissolution/death year |
|
|
| `ArtistAliases` | Array | Alternative names and translations |
|
|
| `Links` | Array | External links (Spotify, AllMusic, Discogs, etc.) |
|
|
| `Images` | Array | Artist images (poster, banner, logo, fanart) |
|
|
| `Genres` | Array | Genre tags |
|
|
| `Rating` | Object | Community rating (votes and value) |
|
|
| `Albums` | Array | Release groups (filtered by parameters) |
|
|
|
|
**Cache behavior**:
|
|
- **Cache key**: `artist:{mbid}:{primTypes}:{secTypes}:{releaseStatuses}`
|
|
- **TTL**: 30 days (2592000 seconds)
|
|
- **Invalidation**: Manual via `/artist/{mbid}/refresh` or automatic via crawler
|
|
|
|
**Performance**:
|
|
- **Cold request**: 2-5 seconds (MusicBrainz DB + external APIs)
|
|
- **Cached request**: 50-200ms (Redis/PostgreSQL)
|
|
- **CDN request**: 10-50ms (Cloudflare edge)
|
|
|
|
### POST /artist/{mbid}/refresh
|
|
|
|
**Purpose**: Invalidate cache and force fresh metadata fetch
|
|
|
|
**Authentication**: Required (API key)
|
|
|
|
**Parameters**:
|
|
|
|
| Parameter | Type | Required | Description |
|
|
|-----------|------|----------|-------------|
|
|
| `mbid` | UUID | Yes | MusicBrainz artist ID |
|
|
|
|
**Request**:
|
|
```http
|
|
POST /artist/5b11f4ce-a62d-471e-81fc-a69a8278c7da/refresh HTTP/1.1
|
|
X-Api-Key: your-api-key-here
|
|
```
|
|
|
|
**Response**:
|
|
```json
|
|
{
|
|
"status": "success",
|
|
"mbid": "5b11f4ce-a62d-471e-81fc-a69a8278c7da",
|
|
"invalidated": [
|
|
"redis:artist:5b11f4ce-a62d-471e-81fc-a69a8278c7da",
|
|
"postgres:artist:5b11f4ce-a62d-471e-81fc-a69a8278c7da",
|
|
"cdn:https://api.lidarr.audio/api/v0.3/artist/5b11f4ce-a62d-471e-81fc-a69a8278c7da"
|
|
]
|
|
}
|
|
```
|
|
|
|
**Cache invalidation**:
|
|
1. Delete from Redis cache
|
|
2. Delete from PostgreSQL cache
|
|
3. Purge from Cloudflare CDN
|
|
4. Next request will fetch fresh data
|
|
|
|
**Use cases**:
|
|
- Artist metadata updated in MusicBrainz
|
|
- New images added to FanArt.tv
|
|
- Wikipedia article updated
|
|
- Manual cache refresh
|
|
|
|
### GET /recent/artist
|
|
|
|
**Purpose**: List recently updated artists
|
|
|
|
**Parameters**:
|
|
|
|
| Parameter | Type | Required | Default | Description |
|
|
|-----------|------|----------|---------|-------------|
|
|
| `limit` | Integer | No | 50 | Maximum number of results |
|
|
| `since` | ISO 8601 | No | 24h ago | Only artists updated after this timestamp |
|
|
|
|
**Example request**:
|
|
```http
|
|
GET /recent/artist?limit=10&since=2025-04-27T00:00:00Z HTTP/1.1
|
|
```
|
|
|
|
**Response**:
|
|
```json
|
|
{
|
|
"artists": [
|
|
{
|
|
"Id": "5b11f4ce-a62d-471e-81fc-a69a8278c7da",
|
|
"ArtistName": "Nirvana",
|
|
"LastUpdated": "2025-04-28T10:30:00Z",
|
|
"ChangeType": "metadata"
|
|
},
|
|
{
|
|
"Id": "83d91898-7763-47d7-b03b-b92132375c47",
|
|
"ArtistName": "Pink Floyd",
|
|
"LastUpdated": "2025-04-28T09:15:00Z",
|
|
"ChangeType": "new_release"
|
|
}
|
|
],
|
|
"total": 2,
|
|
"limit": 10,
|
|
"since": "2025-04-27T00:00:00Z"
|
|
}
|
|
```
|
|
|
|
**Change types**:
|
|
- `metadata`: Artist metadata updated
|
|
- `new_release`: New release added
|
|
- `updated_release`: Existing release updated
|
|
- `new_link`: New external link added
|
|
- `cover_art`: Cover art updated
|
|
|
|
**Cache**: 1 hour TTL
|
|
|
|
**Use case**: Crawler scheduling, monitoring MusicBrainz updates
|
|
|
|
## Album Endpoints
|
|
|
|
### GET /album/{mbid}
|
|
|
|
**Purpose**: Retrieve complete album metadata with tracks
|
|
|
|
**Parameters**:
|
|
|
|
| Parameter | Type | Required | Description |
|
|
|-----------|------|----------|-------------|
|
|
| `mbid` | UUID | Yes | MusicBrainz release group ID |
|
|
|
|
**Example request**:
|
|
```http
|
|
GET /album/1b022e01-4da6-387b-8658-8678046e4cef HTTP/1.1
|
|
```
|
|
|
|
**Response**:
|
|
```json
|
|
{
|
|
"Id": "1b022e01-4da6-387b-8658-8678046e4cef",
|
|
"OldIds": [],
|
|
"Title": "Nevermind",
|
|
"Disambiguation": "",
|
|
"Overview": "Nevermind is the second studio album by American rock band Nirvana...",
|
|
"ReleaseDate": "1991-09-24",
|
|
"Type": "Album",
|
|
"SecondaryTypes": [],
|
|
"Status": "Official",
|
|
"Artist": {
|
|
"Id": "5b11f4ce-a62d-471e-81fc-a69a8278c7da",
|
|
"ArtistName": "Nirvana"
|
|
},
|
|
"Links": [
|
|
{
|
|
"Url": "https://www.spotify.com/album/2guirTSEqLizK7j9i1MTTZ",
|
|
"Name": "spotify"
|
|
},
|
|
{
|
|
"Url": "https://www.allmusic.com/album/mw0000083881",
|
|
"Name": "allmusic"
|
|
}
|
|
],
|
|
"Images": [
|
|
{
|
|
"Url": "https://imagecache.lidarr.audio/cover/1b022e01-4da6-387b-8658-8678046e4cef/front.jpg",
|
|
"CoverType": "cover",
|
|
"Extension": ".jpg"
|
|
},
|
|
{
|
|
"Url": "https://imagecache.lidarr.audio/cover/1b022e01-4da6-387b-8658-8678046e4cef/back.jpg",
|
|
"CoverType": "disc",
|
|
"Extension": ".jpg"
|
|
}
|
|
],
|
|
"Genres": ["Grunge", "Alternative Rock"],
|
|
"Rating": {
|
|
"Votes": 156,
|
|
"Value": 4.9
|
|
},
|
|
"Media": [
|
|
{
|
|
"Position": 1,
|
|
"Format": "CD",
|
|
"Tracks": [
|
|
{
|
|
"Position": 1,
|
|
"Title": "Smells Like Teen Spirit",
|
|
"Duration": 301000,
|
|
"ArtistName": "Nirvana"
|
|
},
|
|
{
|
|
"Position": 2,
|
|
"Title": "In Bloom",
|
|
"Duration": 254000,
|
|
"ArtistName": "Nirvana"
|
|
}
|
|
]
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
**Response fields**:
|
|
|
|
| Field | Type | Description |
|
|
|-------|------|-------------|
|
|
| `Id` | UUID | MusicBrainz release group ID |
|
|
| `OldIds` | Array | Historical IDs (after merges) |
|
|
| `Title` | String | Album title |
|
|
| `Disambiguation` | String | Disambiguation comment |
|
|
| `Overview` | String | Album description (from Wikipedia) |
|
|
| `ReleaseDate` | ISO 8601 | Original release date |
|
|
| `Type` | String | Primary type (Album, Single, EP, etc.) |
|
|
| `SecondaryTypes` | Array | Secondary types (Compilation, Live, etc.) |
|
|
| `Status` | String | Release status (Official, Promotion, etc.) |
|
|
| `Artist` | Object | Artist information |
|
|
| `Links` | Array | External links |
|
|
| `Images` | Array | Cover art images |
|
|
| `Genres` | Array | Genre tags |
|
|
| `Rating` | Object | Community rating |
|
|
| `Media` | Array | Physical/digital media with track listings |
|
|
|
|
**Cache behavior**:
|
|
- **Cache key**: `album:{mbid}`
|
|
- **TTL**: 30 days
|
|
- **Invalidation**: Manual or automatic
|
|
|
|
### POST /album/{mbid}/refresh
|
|
|
|
**Purpose**: Invalidate album cache
|
|
|
|
**Authentication**: Required (API key)
|
|
|
|
**Parameters**: Same as artist refresh
|
|
|
|
**Response**: Same structure as artist refresh
|
|
|
|
### GET /recent/album
|
|
|
|
**Purpose**: List recently updated albums
|
|
|
|
**Parameters**: Same as `/recent/artist`
|
|
|
|
**Response**: Similar structure with album-specific fields
|
|
|
|
## Search Endpoints
|
|
|
|
### GET /search/artist
|
|
|
|
**Purpose**: Search for artists by name
|
|
|
|
**Parameters**:
|
|
|
|
| Parameter | Type | Required | Default | Description |
|
|
|-----------|------|----------|---------|-------------|
|
|
| `query` | String | Yes | - | Search query |
|
|
| `limit` | Integer | No | 10 | Maximum results |
|
|
|
|
**Example request**:
|
|
```http
|
|
GET /search/artist?query=nirvana&limit=5 HTTP/1.1
|
|
```
|
|
|
|
**Response**:
|
|
```json
|
|
{
|
|
"results": [
|
|
{
|
|
"Id": "5b11f4ce-a62d-471e-81fc-a69a8278c7da",
|
|
"ArtistName": "Nirvana",
|
|
"Disambiguation": "90s US grunge band",
|
|
"Type": "Group",
|
|
"Score": 100,
|
|
"Images": [
|
|
{
|
|
"Url": "https://imagecache.lidarr.audio/fanart/artist/5b11f4ce-a62d-471e-81fc-a69a8278c7da/poster.jpg",
|
|
"CoverType": "poster",
|
|
"Extension": ".jpg"
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"Id": "9282c8b4-ca0b-4c6b-b7e3-4f7762dfc4d6",
|
|
"ArtistName": "Nirvana",
|
|
"Disambiguation": "60s UK psychedelic band",
|
|
"Type": "Group",
|
|
"Score": 95,
|
|
"Images": []
|
|
}
|
|
],
|
|
"total": 2,
|
|
"query": "nirvana",
|
|
"limit": 5
|
|
}
|
|
```
|
|
|
|
**Search algorithm**:
|
|
1. Query Solr artist core with dismax parser
|
|
2. Boost fields: `artist^2 sortname alias`
|
|
3. Minimum match: 1 term
|
|
4. Sort by relevance score
|
|
5. Enrich results with cached metadata
|
|
|
|
**Cache**:
|
|
- **Cache key**: `search:artist:{query}:{limit}`
|
|
- **TTL**: 1 hour
|
|
|
|
**Performance**:
|
|
- **Cold request**: 500ms-1s (Solr query + metadata enrichment)
|
|
- **Cached request**: 50-100ms
|
|
|
|
### GET /search/album
|
|
|
|
**Purpose**: Search for albums by title
|
|
|
|
**Parameters**: Same as artist search
|
|
|
|
**Response**: Similar structure with album-specific fields
|
|
|
|
**Search algorithm**: Same as artist search, using `release-group` Solr core
|
|
|
|
### GET /search/all
|
|
|
|
**Purpose**: Combined artist and album search
|
|
|
|
**Parameters**:
|
|
|
|
| Parameter | Type | Required | Default | Description |
|
|
|-----------|------|----------|---------|-------------|
|
|
| `query` | String | Yes | - | Search query |
|
|
| `artistLimit` | Integer | No | 5 | Max artist results |
|
|
| `albumLimit` | Integer | No | 5 | Max album results |
|
|
|
|
**Response**:
|
|
```json
|
|
{
|
|
"artists": [...],
|
|
"albums": [...],
|
|
"query": "nevermind",
|
|
"artistLimit": 5,
|
|
"albumLimit": 5
|
|
}
|
|
```
|
|
|
|
**Use case**: Unified search UI, autocomplete
|
|
|
|
### POST /search/fingerprint
|
|
|
|
**Purpose**: Acoustic fingerprint search (AcoustID integration)
|
|
|
|
**Parameters**:
|
|
|
|
| Parameter | Type | Required | Description |
|
|
|-----------|------|----------|-------------|
|
|
| `fingerprint` | String | Yes | Chromaprint fingerprint |
|
|
| `duration` | Integer | Yes | Track duration in seconds |
|
|
|
|
**Request**:
|
|
```http
|
|
POST /search/fingerprint HTTP/1.1
|
|
Content-Type: application/json
|
|
|
|
```
|
|
|
|
**Response**:
|
|
```json
|
|
{
|
|
"results": [
|
|
{
|
|
"Id": "5b11f4ce-a62d-471e-81fc-a69a8278c7da",
|
|
"ArtistName": "Nirvana",
|
|
"AlbumTitle": "Nevermind",
|
|
"TrackTitle": "Smells Like Teen Spirit",
|
|
"Score": 0.98,
|
|
"Duration": 301
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
**Use case**: Audio file identification, automatic tagging
|
|
|
|
## Spotify Integration Endpoints
|
|
|
|
### GET /spotify/artist/{id}
|
|
|
|
**Purpose**: Get artist metadata by Spotify ID
|
|
|
|
**Parameters**:
|
|
|
|
| Parameter | Type | Required | Description |
|
|
|-----------|------|----------|-------------|
|
|
| `id` | String | Yes | Spotify artist ID |
|
|
|
|
**Example request**:
|
|
```http
|
|
GET /spotify/artist/6olE6TJLqED3rqDCT0FyPh HTTP/1.1
|
|
```
|
|
|
|
**Response**: Same structure as `/artist/{mbid}` with MusicBrainz mapping
|
|
|
|
**Mapping algorithm**:
|
|
1. Query Spotify API for artist metadata
|
|
2. Search MusicBrainz by artist name
|
|
3. Calculate Levenshtein distance (threshold: 0.8)
|
|
4. Return best match with MusicBrainz ID
|
|
|
|
### GET /spotify/album/{id}
|
|
|
|
**Purpose**: Get album metadata by Spotify ID
|
|
|
|
**Parameters**: Same as artist endpoint
|
|
|
|
**Response**: Same structure as `/album/{mbid}` with MusicBrainz mapping
|
|
|
|
### POST /spotify/lookup
|
|
|
|
**Purpose**: Batch Spotify ID to MusicBrainz ID mapping
|
|
|
|
**Request**:
|
|
```http
|
|
POST /spotify/lookup HTTP/1.1
|
|
Content-Type: application/json
|
|
|
|
```
|
|
|
|
**Response**:
|
|
```json
|
|
{
|
|
"artists": {
|
|
"6olE6TJLqED3rqDCT0FyPh": "5b11f4ce-a62d-471e-81fc-a69a8278c7da",
|
|
"0k17h0D3J5VfsdmQ1iZtE9": "83d91898-7763-47d7-b03b-b92132375c47"
|
|
},
|
|
"albums": {
|
|
"2guirTSEqLizK7j9i1MTTZ": "1b022e01-4da6-387b-8658-8678046e4cef"
|
|
}
|
|
}
|
|
```
|
|
|
|
**Cache**: 90 days TTL (Spotify IDs rarely change)
|
|
|
|
### GET /spotify/auth
|
|
|
|
**Purpose**: Get Spotify OAuth authorization URL
|
|
|
|
**Response**:
|
|
```json
|
|
{
|
|
"auth_url": "https://accounts.spotify.com/authorize?client_id=...&response_type=code&redirect_uri=..."
|
|
}
|
|
```
|
|
|
|
**Use case**: User authentication for Spotify features
|
|
|
|
### GET /spotify/renew
|
|
|
|
**Purpose**: Refresh Spotify OAuth token
|
|
|
|
**Parameters**:
|
|
|
|
| Parameter | Type | Required | Description |
|
|
|-----------|------|----------|-------------|
|
|
| `refresh_token` | String | Yes | Spotify refresh token |
|
|
|
|
**Response**:
|
|
```json
|
|
{
|
|
"access_token": "BQD...",
|
|
"expires_in": 3600
|
|
}
|
|
```
|
|
|
|
## Chart Endpoints
|
|
|
|
### GET /chart/{name}/{type}/{selection}
|
|
|
|
**Purpose**: Get music charts from various sources
|
|
|
|
**Parameters**:
|
|
|
|
| Parameter | Type | Required | Values | Description |
|
|
|-----------|------|----------|--------|-------------|
|
|
| `name` | String | Yes | `apple-music`, `billboard`, `itunes`, `lastfm` | Chart source |
|
|
| `type` | String | Yes | `artist`, `album`, `track` | Chart type |
|
|
| `selection` | String | Yes | Source-specific | Chart category |
|
|
|
|
**Chart sources and selections**:
|
|
|
|
#### Apple Music
|
|
|
|
**Selections**:
|
|
- `top-albums`: Top 100 albums
|
|
- `new-releases`: New album releases
|
|
- `top-songs`: Top 100 songs
|
|
|
|
**Example**:
|
|
```http
|
|
GET /chart/apple-music/album/top-albums HTTP/1.1
|
|
```
|
|
|
|
#### Billboard
|
|
|
|
**Selections**:
|
|
- `hot-100`: Billboard Hot 100
|
|
- `billboard-200`: Billboard 200 albums
|
|
- `artist-100`: Top artists
|
|
- `streaming-songs`: Top streaming songs
|
|
|
|
**Example**:
|
|
```http
|
|
GET /chart/billboard/track/hot-100 HTTP/1.1
|
|
```
|
|
|
|
#### iTunes
|
|
|
|
**Selections**:
|
|
- `top-albums`: iTunes top albums
|
|
- `top-songs`: iTunes top songs
|
|
|
|
**Example**:
|
|
```http
|
|
GET /chart/itunes/album/top-albums HTTP/1.1
|
|
```
|
|
|
|
#### Last.fm
|
|
|
|
**Selections**:
|
|
- `top-artists`: Most scrobbled artists
|
|
- `top-albums`: Most scrobbled albums
|
|
- `top-tracks`: Most scrobbled tracks
|
|
|
|
**Example**:
|
|
```http
|
|
GET /chart/lastfm/artist/top-artists HTTP/1.1
|
|
```
|
|
|
|
**Response**:
|
|
```json
|
|
{
|
|
"chart": "billboard",
|
|
"type": "track",
|
|
"selection": "hot-100",
|
|
"date": "2025-04-28",
|
|
"entries": [
|
|
{
|
|
"position": 1,
|
|
"previousPosition": 2,
|
|
"peakPosition": 1,
|
|
"weeksOnChart": 12,
|
|
"artist": {
|
|
"Id": "5b11f4ce-a62d-471e-81fc-a69a8278c7da",
|
|
"ArtistName": "Nirvana"
|
|
},
|
|
"album": {
|
|
"Id": "1b022e01-4da6-387b-8658-8678046e4cef",
|
|
"Title": "Nevermind"
|
|
},
|
|
"track": {
|
|
"Title": "Smells Like Teen Spirit",
|
|
"Duration": 301000
|
|
}
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
**Cache**: 6 hours TTL (charts update daily)
|
|
|
|
**MusicBrainz mapping**: Chart entries are automatically mapped to MusicBrainz IDs when possible
|
|
|
|
## Series Endpoints
|
|
|
|
### GET /series/{mbid}
|
|
|
|
**Purpose**: Get series metadata (box sets, compilations)
|
|
|
|
**Parameters**:
|
|
|
|
| Parameter | Type | Required | Description |
|
|
|-----------|------|----------|-------------|
|
|
| `mbid` | UUID | Yes | MusicBrainz series ID |
|
|
|
|
**Response**:
|
|
```json
|
|
{
|
|
"Id": "series-mbid",
|
|
"Name": "The Beatles Remastered Series",
|
|
"Type": "Release group series",
|
|
"Albums": [
|
|
{
|
|
"Id": "album-mbid-1",
|
|
"Title": "Please Please Me",
|
|
"Position": 1
|
|
},
|
|
{
|
|
"Id": "album-mbid-2",
|
|
"Title": "With the Beatles",
|
|
"Position": 2
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
**Use case**: Box set tracking, series completion
|
|
|
|
## Cache Management Endpoints
|
|
|
|
### POST /invalidate
|
|
|
|
**Purpose**: Batch cache invalidation
|
|
|
|
**Authentication**: Required (API key)
|
|
|
|
**Request**:
|
|
```http
|
|
POST /invalidate HTTP/1.1
|
|
X-Api-Key: your-api-key-here
|
|
Content-Type: application/json
|
|
|
|
```
|
|
|
|
**Response**:
|
|
```json
|
|
{
|
|
"status": "success",
|
|
"invalidated": {
|
|
"artists": 2,
|
|
"albums": 1,
|
|
"total_keys": 6
|
|
}
|
|
}
|
|
```
|
|
|
|
**Invalidation scope**:
|
|
- Redis cache
|
|
- PostgreSQL cache
|
|
- Cloudflare CDN cache
|
|
|
|
**Use case**: Bulk cache refresh after MusicBrainz updates
|
|
|
|
## Rate Limiting
|
|
|
|
**Default**: No rate limiting (NullRateLimiter)
|
|
|
|
**Optional**: Redis-based distributed rate limiting
|
|
|
|
**Configuration**:
|
|
```python
|
|
RATE_LIMITER = 'redis'
|
|
RATE_LIMIT_REQUESTS = 100
|
|
RATE_LIMIT_WINDOW = 60 # seconds
|
|
```
|
|
|
|
**Rate limit response**:
|
|
```http
|
|
HTTP/1.1 429 Too Many Requests
|
|
Retry-After: 30
|
|
|
|
{
|
|
"error": "Rate limit exceeded",
|
|
"limit": 100,
|
|
"window": 60,
|
|
"retry_after": 30
|
|
}
|
|
```
|
|
|
|
## CORS Support
|
|
|
|
**Default**: CORS enabled for all origins
|
|
|
|
**Headers**:
|
|
```http
|
|
Access-Control-Allow-Origin: *
|
|
Access-Control-Allow-Methods: GET, POST, OPTIONS
|
|
Access-Control-Allow-Headers: Content-Type, X-Api-Key
|
|
```
|
|
|
|
**Configuration**:
|
|
```python
|
|
CORS_ORIGINS = ['https://lidarr.audio', 'https://app.lidarr.audio']
|
|
```
|
|
|
|
## Pagination
|
|
|
|
**Not implemented**: All endpoints return complete result sets
|
|
|
|
**Workaround**: Use `limit` parameter on search and recent endpoints
|
|
|
|
**Future consideration**: Cursor-based pagination for large result sets
|
|
|
|
## Filtering and Sorting
|
|
|
|
**Artist endpoint**:
|
|
- Filter by release type: `primTypes`, `secTypes`
|
|
- Filter by release status: `releaseStatuses`
|
|
- Sort: Albums sorted by release date (descending)
|
|
|
|
**Search endpoints**:
|
|
- Sort: Relevance score (descending)
|
|
- Filter: Not implemented
|
|
|
|
**Chart endpoints**:
|
|
- Sort: Chart position (ascending)
|
|
- Filter: Not implemented
|
|
|
|
## Compression
|
|
|
|
**Response compression**: Supported via `Accept-Encoding: gzip`
|
|
|
|
**Request compression**: Not supported
|
|
|
|
**Example**:
|
|
```http
|
|
GET /artist/5b11f4ce-a62d-471e-81fc-a69a8278c7da HTTP/1.1
|
|
Accept-Encoding: gzip
|
|
|
|
HTTP/1.1 200 OK
|
|
Content-Encoding: gzip
|
|
Content-Length: 1234
|
|
```
|
|
|
|
**Compression ratio**: Typically 5:1 for JSON responses
|
|
|
|
## Conditional Requests
|
|
|
|
**ETag support**: Not implemented
|
|
|
|
**Last-Modified support**: Not implemented
|
|
|
|
**Future consideration**: ETag generation based on cache timestamp
|
|
|
|
## Webhooks
|
|
|
|
**Not implemented**: No webhook support for cache invalidation or updates
|
|
|
|
**Workaround**: Poll `/recent/artist` and `/recent/album` endpoints
|
|
|
|
**Future consideration**: WebSocket or Server-Sent Events for real-time updates
|
|
|
|
## API Versioning Strategy
|
|
|
|
**Current approach**: Path-based versioning (`/api/v0.3`)
|
|
|
|
**Version lifecycle**:
|
|
1. New features developed in `testing` version
|
|
2. Tested in production alongside stable version
|
|
3. Promoted to new stable version (e.g., v0.4)
|
|
4. Old stable version deprecated after 6 months
|
|
|
|
**Breaking changes**: Only in major version increments
|
|
|
|
**Backward compatibility**: Maintained within minor versions
|
|
|
|
## Performance Benchmarks
|
|
|
|
| Endpoint | Cold (ms) | Cached (ms) | CDN (ms) |
|
|
|----------|-----------|-------------|----------|
|
|
| GET /artist/{mbid} | 2000-5000 | 50-200 | 10-50 |
|
|
| GET /album/{mbid} | 1500-3000 | 50-150 | 10-50 |
|
|
| GET /search/artist | 500-1000 | 50-100 | N/A |
|
|
| GET /chart/billboard/hot-100 | 3000-5000 | 100-200 | 20-100 |
|
|
| POST /artist/{mbid}/refresh | 100-200 | N/A | N/A |
|
|
|
|
**Factors affecting cold request performance**:
|
|
- MusicBrainz database query complexity
|
|
- Number of external API calls (FanArt, Wikipedia, etc.)
|
|
- External API response times
|
|
- Network latency
|
|
|
|
**Cache hit rate**: 85%+ with crawler enabled
|
|
|
|
## Error Handling Examples
|
|
|
|
**Invalid MBID format**:
|
|
```http
|
|
GET /artist/invalid-mbid HTTP/1.1
|
|
|
|
HTTP/1.1 400 Bad Request
|
|
{
|
|
"error": "Invalid MBID format",
|
|
"mbid": "invalid-mbid"
|
|
}
|
|
```
|
|
|
|
**Artist not found**:
|
|
```http
|
|
GET /artist/00000000-0000-0000-0000-000000000000 HTTP/1.1
|
|
|
|
HTTP/1.1 404 Not Found
|
|
{
|
|
"error": "Artist not found",
|
|
"mbid": "00000000-0000-0000-0000-000000000000"
|
|
}
|
|
```
|
|
|
|
**Missing API key**:
|
|
```http
|
|
POST /invalidate HTTP/1.1
|
|
|
|
HTTP/1.1 401 Unauthorized
|
|
{
|
|
"error": "Missing or invalid API key"
|
|
}
|
|
```
|
|
|
|
**Database connection error**:
|
|
```http
|
|
GET /artist/5b11f4ce-a62d-471e-81fc-a69a8278c7da HTTP/1.1
|
|
|
|
HTTP/1.1 503 Service Unavailable
|
|
{
|
|
"error": "Database unavailable",
|
|
"retry_after": 60
|
|
}
|
|
```
|
|
|
|
## Client Implementation Examples
|
|
|
|
### Python
|
|
|
|
```python
|
|
import requests
|
|
|
|
class LidarrMetadataClient:
|
|
def __init__(self, base_url='https://api.lidarr.audio/api/v0.3'):
|
|
self.base_url = base_url
|
|
self.session = requests.Session()
|
|
|
|
def get_artist(self, mbid, prim_types=None, sec_types=None, statuses=None):
|
|
params = {}
|
|
if prim_types:
|
|
params['primTypes'] = ','.join(prim_types)
|
|
if sec_types:
|
|
params['secTypes'] = ','.join(sec_types)
|
|
if statuses:
|
|
params['releaseStatuses'] = ','.join(statuses)
|
|
|
|
response = self.session.get(f'{self.base_url}/artist/{mbid}', params=params)
|
|
response.raise_for_status()
|
|
return response.json()
|
|
|
|
def search_artist(self, query, limit=10):
|
|
response = self.session.get(
|
|
f'{self.base_url}/search/artist',
|
|
params={'query': query, 'limit': limit}
|
|
)
|
|
response.raise_for_status()
|
|
return response.json()
|
|
|
|
# Usage
|
|
client = LidarrMetadataClient()
|
|
artist = client.get_artist('5b11f4ce-a62d-471e-81fc-a69a8278c7da', prim_types=['Album'])
|
|
results = client.search_artist('nirvana')
|
|
```
|
|
|
|
### JavaScript
|
|
|
|
```javascript
|
|
class LidarrMetadataClient {
|
|
constructor(baseUrl = 'https://api.lidarr.audio/api/v0.3') {
|
|
this.baseUrl = baseUrl;
|
|
}
|
|
|
|
async getArtist(mbid, options = {}) {
|
|
const params = new URLSearchParams();
|
|
if (options.primTypes) params.set('primTypes', options.primTypes.join(','));
|
|
if (options.secTypes) params.set('secTypes', options.secTypes.join(','));
|
|
if (options.releaseStatuses) params.set('releaseStatuses', options.releaseStatuses.join(','));
|
|
|
|
const response = await fetch(`${this.baseUrl}/artist/${mbid}?${params}`);
|
|
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
|
return response.json();
|
|
}
|
|
|
|
async searchArtist(query, limit = 10) {
|
|
const params = new URLSearchParams({ query, limit });
|
|
const response = await fetch(`${this.baseUrl}/search/artist?${params}`);
|
|
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
|
return response.json();
|
|
}
|
|
}
|
|
|
|
// Usage
|
|
const client = new LidarrMetadataClient();
|
|
const artist = await client.getArtist('5b11f4ce-a62d-471e-81fc-a69a8278c7da', {
|
|
primTypes: ['Album']
|
|
});
|
|
const results = await client.searchArtist('nirvana');
|
|
```
|
|
|
|
## Conclusion
|
|
|
|
The API provides comprehensive music metadata with:
|
|
|
|
- **15+ endpoints** covering artists, albums, search, charts, and Spotify integration
|
|
- **Multi-source aggregation** from MusicBrainz, FanArt.tv, Wikipedia, Spotify, Last.fm, Billboard
|
|
- **Intelligent caching** with 30-day TTL and CDN integration
|
|
- **Flexible filtering** for release types and statuses
|
|
- **Chart integration** from 4 major sources
|
|
- **Spotify mapping** for cross-platform compatibility
|
|
|
|
The API is production-ready with excellent performance characteristics when properly cached. The dual-version deployment strategy allows safe rollout of new features.
|
|
|
|
Key strengths: comprehensive metadata, excellent caching, multi-source enrichment.
|
|
|
|
Key weaknesses: no authentication on read endpoints, no pagination, no webhooks.
|