SonEx MusicBrainz Client
A lightweight, Elixir client for the MusicBrainz API v2.
Installation
Add son_ex_musicbrainz_client to your list of dependencies in mix.exs:
def deps do
[
{:son_ex_musicbrainz_client, "~> 0.2.1"}
]
endThen run:
mix deps.getConfiguration
User Agent (Required for Production)
MusicBrainz requires a user agent identifying your application. Configure it in config/config.exs:
config :son_ex_musicbrainz_client,
user_agent: "MyApp/1.0 (contact@example.com)"HTTP Options (Optional)
You can configure additional HTTP options for the Req client:
config :son_ex_musicbrainz_client,
http_options: [
# Add custom options here
receive_timeout: 30_000
]Usage
Lookup by MBID
Retrieve a specific entity using its MusicBrainz Identifier (MBID):
# Look up an artist
{:ok, artist} = SonEx.MusicBrainz.lookup(:artist, "5b11f4ce-a62d-471e-81fc-a69a8278c7da")
artist["name"]
# => "Nirvana"
# Look up a release with additional data
{:ok, release} = SonEx.MusicBrainz.lookup(
:release,
"a1b2c3d4-e5f6-7890-abcd-ef1234567890",
inc: ["artists", "labels", "recordings"]
)
# Look up a recording
{:ok, recording} = SonEx.MusicBrainz.lookup(:recording, "recording-mbid-here")
recording["title"]
# => "Smells Like Teen Spirit"Supported entity types for lookup::area, :artist, :event, :genre, :instrument, :label, :place, :recording, :release, :release_group, :series, :work, :url
Common options:
:inc- List of subqueries (e.g.,["artists", "releases", "recordings"])
Browse Relationships
Discover entities related to another entity:
# Browse all releases by an artist
{:ok, result} = SonEx.MusicBrainz.browse(
:release,
[artist: "5b11f4ce-a62d-471e-81fc-a69a8278c7da"],
limit: 50
)
result["releases"]
# => [%{"id" => "...", "title" => "Nevermind"}, ...]
# Browse recordings on a release
{:ok, result} = SonEx.MusicBrainz.browse(
:recording,
[release: "release-mbid"],
inc: ["artist-credits"]
)
# Browse artists from an area
{:ok, result} = SonEx.MusicBrainz.browse(
:artist,
[area: "area-mbid"],
offset: 100,
limit: 25
)
# Browse release groups by artist
{:ok, result} = SonEx.MusicBrainz.browse(
:release_group,
[artist: "artist-mbid"],
type: "album"
)Common browse relationships by entity:
- Artist:
area,collection,recording,release,release_group,work - Release:
artist,collection,label,track,track_artist,recording,release_group - Recording:
artist,collection,release,work - Release Group:
artist,collection,release - Event:
area,artist,collection,place - Label:
area,collection,release - Place:
area,collection - Work:
artist,collection - Area:
collection - Instrument:
collection - Series:
collection - URL:
resource
Common options:
:limit- Number of results (default: 25, max: 100):offset- Pagination offset:inc- Additional data to include:type- Type filter (for releases and release groups):status- Status filter (for releases)
Search
Full-text search using Lucene query syntax:
# Search for artists
{:ok, result} = SonEx.MusicBrainz.search(:artist, "artist:nirvana AND country:US")
result["artists"]
# => [%{"id" => "...", "name" => "Nirvana", "score" => 100}, ...]
result["count"]
# => 15
# Search for releases with pagination
{:ok, result} = SonEx.MusicBrainz.search(
:release,
"release:nevermind AND artist:nirvana",
limit: 10,
offset: 0
)
# Search for recordings
{:ok, result} = SonEx.MusicBrainz.search(
:recording,
"recording:\"Smells Like Teen Spirit\" AND artist:nirvana"
)
# Complex search with multiple criteria
{:ok, result} = SonEx.MusicBrainz.search(
:artist,
"artist:beatles AND country:GB AND type:group",
limit: 25
)Supported entity types for search::area, :artist, :event, :genre, :instrument, :label, :place, :recording, :release, :release_group, :series, :work, :url
Common search fields by entity:
- Artist:
artist,alias,type,gender,area,tag,arid(MBID) - Release:
release,artist,date,country,barcode,status,reid(MBID) - Recording:
recording,artist,release,date,isrc,rid(MBID) - Release Group:
releasegroup,artist,type,tag,rgid(MBID) - Label:
label,area,type,code,laid(MBID) - Work:
work,artist,type,tag,wid(MBID)
See the MusicBrainz Search Documentation for complete field references.
Common options:
:limit- Number of results (default: 25, max: 100):offset- Pagination offset
Response Format
All functions return native Elixir maps parsed from JSON:
# Successful response
{:ok, data} = SonEx.MusicBrainz.lookup(:artist, "mbid-here")
# data is a map: %{"id" => "...", "name" => "...", ...}
# Error responses are passed through from Req
{:error, reason} = SonEx.MusicBrainz.lookup(:artist, "invalid-mbid")
# Handle errors as neededExamples
Finding an Artist's Discography
artist_mbid = "5b11f4ce-a62d-471e-81fc-a69a8278c7da" # Nirvana
# Get artist info
{:ok, artist} = SonEx.MusicBrainz.lookup(:artist, artist_mbid)
IO.puts("Artist: #{artist["name"]}")
# Browse all official albums
{:ok, result} = SonEx.MusicBrainz.browse(
:release_group,
[artist: artist_mbid],
type: "album",
limit: 100
)
Enum.each(result["release-groups"], fn rg ->
IO.puts(" #{rg["title"]} (#{rg["first-release-date"]})")
end)Searching for Recordings by ISRC
isrc = "USCA29900012"
{:ok, result} = SonEx.MusicBrainz.search(:recording, "isrc:#{isrc}")
case result["recordings"] do
[recording | _] ->
IO.puts("Found: #{recording["title"]}")
IO.puts("Artist: #{get_in(recording, ["artist-credit", 0, "name"])}")
[] ->
IO.puts("No recording found for ISRC: #{isrc}")
endGetting Label Catalog
label_mbid = "label-mbid-here"
# Get all releases from a label
{:ok, label_info} = SonEx.MusicBrainz.lookup(:label, label_mbid)
{:ok, releases} = SonEx.MusicBrainz.browse(
:release,
[label: label_mbid],
limit: 100,
inc: ["artists", "release-groups"]
)
IO.puts("Label: #{label_info["name"]}")
IO.puts("Total releases: #{releases["release-count"]}")Working with Cover Art
The library includes support for the Cover Art Archive API, which provides cover art images for releases and release groups:
# Get cover art metadata for a release
{:ok, metadata} = SonEx.MusicBrainz.fetch_release_cover_art("release-mbid")
# Access image information
Enum.each(metadata["images"], fn image ->
IO.puts("Image URL: #{image["image"]}")
IO.puts("Front cover: #{image["front"]}")
IO.puts("Types: #{inspect(image["types"])}")
IO.puts("Thumbnails: #{inspect(Map.keys(image["thumbnails"]))}")
end)
# Get the front cover image URL
{:ok, url} = SonEx.MusicBrainz.fetch_front("release-mbid")
# => {:ok, "https://archive.org/download/mbid-770b9b80-.../image.jpg"}
# Get a thumbnail (250px, 500px, or 1200px)
{:ok, url} = SonEx.MusicBrainz.fetch_front("release-mbid", size: 500)
# => {:ok, "https://archive.org/download/mbid-770b9b80-.../image-500.jpg"}
# Get back cover
{:ok, url} = SonEx.MusicBrainz.fetch_back("release-mbid")
# => {:ok, "https://archive.org/download/mbid-770b9b80-.../back.jpg"}
# Get cover art for a release group
{:ok, metadata} = SonEx.MusicBrainz.fetch_release_group_cover_art("release-group-mbid")
# Get front cover from a release group
{:ok, url} = SonEx.MusicBrainz.fetch_front(
"release-group-mbid",
entity_type: :release_group
)
# Get specific image by ID
{:ok, url} = SonEx.MusicBrainz.fetch_cover_art_image(
"release-mbid",
"1234567890",
size: 250
)Available cover art functions:
fetch_release_cover_art/2- Get all cover art metadata for a release (returns{:ok, map()})fetch_release_group_cover_art/2- Get cover art metadata for a release group (returns{:ok, map()})fetch_front/2- Get front cover image URL (returns{:ok, url})fetch_back/2- Get back cover image URL (returns{:ok, url})fetch_cover_art_image/3- Get specific image URL by ID (returns{:ok, url})
Thumbnail sizes:250, 500, 1200 (pixels)
Response Format:
- Metadata requests: Return
{:ok, map()}with JSON data containing image URLs and metadata - Image requests: Return
{:ok, url}with the direct URL to the image as a string
Rate Limiting
MusicBrainz enforces rate limits (approximately 1 request per second for anonymous users). This client does not implement rate limiting internally.
For production applications, you should implement rate limiting at a higher level (e.g., using a GenServer-based queue, a library like ex_rated, or a supervision tree managing backpressure).
Testing
The library includes comprehensive tests using mock-based testing (no real API calls):
# Run tests
mix test
# Run tests with coverage
mix test --coverAPI Documentation
For detailed API documentation, see:
Contributing
Contributions are welcome! Please:
- Fork the repository
-
Create a feature branch (
git checkout -b feature/amazing-feature) -
Commit your changes (
git commit -m 'Add amazing feature') -
Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
Please ensure:
-
All tests pass (
mix test) -
Code is formatted (
mix format) -
Coverage remains high (
mix test --cover)
Changelog
v0.2.1 (2025-10-25)
Added:
-
Cover Art Archive API support with new
SonEx.MusicBrainz.CoverArtmodulefetch_release_cover_art/2- Fetch cover art metadata for releasesfetch_release_group_cover_art/2- Fetch cover art metadata for release groupsfetch_front/2- Fetch front cover images with optional thumbnail sizesfetch_back/2- Fetch back cover images with optional thumbnail sizesfetch_image_by_id/3- Fetch specific cover art images by ID
- Smart dispatch support for cover art functions (works with release/release-group maps)
- Comprehensive test coverage for cover art functionality (20 new tests)
- Documentation and examples for Cover Art Archive API
v0.2.0 (2025-10-24)
Added:
- Smart dispatch capabilities across all entity modules
- Enhanced MBID extraction from entity maps
- Entity-specific modules (Artist, Release, ReleaseGroup, Recording, etc.)
- Comprehensive test coverage with mock-based testing
- Pattern matching for entity type detection
Changed:
-
Improved API design with
defdelegatepattern - Better error handling and response normalization
v0.1.0 (Initial Release)
Added:
- Basic MusicBrainz API v2 client
- Support for lookup, browse, and search operations
- All core MusicBrainz entity types
- Configurable HTTP options
- User agent configuration
License
This project is licensed under the MIT License - see the LICENSE file for details.