A Kotlin library to interact with the MusicBrainz
API on Android.
It allows performing search, lookup, and fetching cover art in a simple
and structured way.
- Search
- Direct lookups
- Relationship options (Include)
- Album cover art retrieval (Cover Art)
- minSdk 24
- Java compile version: 21 (configure
compileOptionsandkotlinOptionsto useJavaVersion.VERSION_21andjvmTarget = "21")
In the archive settings.gradle.kts add the JitPack repository:
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
// Add JitPack repository
maven(url = "https://jitpack.io")
}
}In the archive build.gradle.kts add the dependency:
implementation("com.github.hall9zeha:Music-Brainz-Android-Client:1.0.0")Remember to add the internet access permission in your application's manifest:
<uses-permission android:name="android.permission.INTERNET"/>This project implements two specific queries for MusicBrainz: one for Recording (using search) and another for Release (using lookup), as a proof of concept. However, by utilizing the generic Search and Lookup queries, along with dynamic query generators for search and relations or includes parameters for lookup, almost any request can be made to the MusicBrainz API.
// It is not mandatory to pass arguments, but it is recommended to include appName, appVersion,
// and contact information. This helps MusicBrainz identify your client, optimize requests,
// and avoid being flagged as spam or abusive usage.
val mbClient = MusicBrainzClient(
appName = "Test app",
appVersion = "1.0.0",
contact = "mail@mail.com"
)val recordingQuery = RecordingQueryBuilder()
.title("I don't wanna go")
.artist("Kidburn")
.build()
mbClient.searchRecording(
query = recordingQuery,
limit = 1,
offset = 0
) { response ->
response.onSuccess {
Log.d("RESPONSE_MUZIC", "Success: $it")
}
response.onError {
Log.e("RESPONSE_MUZIC", "Error ${it.errorCode}: ${it.message}")
it.cause?.printStackTrace()
}
}val releaseQuery = ReleaseQueryBuilder()
.releaseId("4ad149df-86f9-47c5-96f3-8db9ffd66da4")
.build()
mbClient.getReleaseById(releaseQuery) {
it.onSuccess {
Log.d("RESPONSE_MUZIC_RELEASE", "Success: $it")
}
it.onError {
Log.e("RESPONSE_MUZIC_RELEASE", "Error ${it.errorCode}: ${it.message}")
it.cause?.printStackTrace()
}
}val genericQuery = GenericQueryBuilder()
.field(SearchField.RECORDING, "I Don't Wanna Go")
.field(SearchField.ARTIST, "Kidburn")
.build()
mbClient.searchEntity<RecordingResponse>(
entity = SearchEntity.RECORDING,
query = genericQuery,
limit = 1,
offset = 0
) { response ->
response.onSuccess {
Log.d("RESPONSE_MUZIC_GENERIC", "Success: $it")
}
response.onError {
Log.e("RESPONSE_MUZIC", "Error ${it.errorCode}: ${it.message}")
it.cause?.printStackTrace()
}
}mbClient.lookupEntity<RecordingLookupResponse>(
entity = LookupEntity.RECORDING,
mbId = "b9ad642e-b012-41c7-b72a-42cf4911f9ff",
inc = null // Pass null if you don’t want relations
) { response ->
response.onSuccess {
Log.d("RESPONSE_MUZIC_LOOKUP", "Success: $it")
}
response.onError {
Log.e("RESPONSE_MUZIC_LOOKUP", "Error ${it.errorCode}: ${it.message}")
it.cause?.printStackTrace()
}
}val includeFields = GenericIncludeBuilder()
.incArtistCredits()
.incIsrcs()
.incAliases()
.build()
mbClient.lookupEntity<RecordingLookupResponse>(
entity = LookupEntity.RECORDING,
mbId = "b9ad642e-b012-41c7-b72a-42cf4911f9ff",
inc = includeFields
) { response ->
response.onSuccess {
Log.d("RESPONSE_MUZIC_LOOKUP_INC", "Success: $it")
}
response.onError {
Log.e("RESPONSE_MUZIC_LOOKUP_INC", "Error ${it.errorCode}: ${it.message}")
it.cause?.printStackTrace()
}
}mbClient.fetchCoverArt(mbId = "99b09d02-9cc9-3fed-8431-f162165a9371") {
it.onSuccess { coverArtResponse ->
Log.d("RESPONSE_MUZIC_COVER_ART", "Success: $coverArtResponse")
}
it.onError { error ->
Log.e("RESPONSE_MUZIC_COVER_ART", "Error ${error.errorCode}: ${error.message}")
error.cause?.printStackTrace()
}
}mbClient.fetchCoverArtThumbnail(mbId = "99b09d02-9cc9-3fed-8431-f162165a9371") {
it.onSuccess { coverArtResponse ->
Log.d("RESPONSE_MUZIC_COVER_THUMBNAIL", "Success: $coverArtResponse")
}
it.onError { error ->
Log.e("RESPONSE_MUZIC_COVER_THUMBNAIL", "Error ${error.errorCode}: ${error.message}")
error.cause?.printStackTrace()
}
}mbClient.fetchCoverArtSide(
mbId = "99b09d02-9cc9-3fed-8431-f162165a9371",
side = COVER_ART_BOTH_SIDES, // Default: COVER_ART_FRONT
size = CoverSize.S_500 // Default: CoverSize.S_250
) {
it.onSuccess { coverArtResponse ->
Log.d("RESPONSE_MUZIC_COVER_FRONT", "${coverArtResponse.front}")
Log.d("RESPONSE_MUZIC_COVER_BACK", "${coverArtResponse.back}")
}
it.onError { error ->
Log.e("RESPONSE_MUZIC_COVER_FRONT", "Error ${error.errorCode}: ${error.message}")
error.cause?.printStackTrace()
}
}// Fetches cover art URLs by track title and artist name.
// Both parameters are required to improve accuracy and reduce extra results.
// If `onlyFirst` is enabled, returns the first matching cover art; otherwise,
// returns all available URLs, which may take longer due to the more complex query.
mbClient.fetchCoverArtByTitleAndArtist(
title = "¿Sabes?",
artist = "Álex ubago",
side = COVER_ART_BOTH_SIDES, // Default: COVER_ART_FRONT
size = CoverSize.S_500 // Default: CoverSize.S_250
) {
it.onSuccess { coverArtResponse ->
coverArtResponse.forEach { coverArtUrl ->
Log.d("RESPONSE_MUZIC_COVER_BY_NAME", "${coverArtUrl.front}")
}
}
it.onError { error ->
Log.e("RESPONSE_MUZIC_COVER_BY_NAME", "Error ${error.errorCode}: ${error.message}")
error.cause?.printStackTrace()
}
}| Enum | Path |
|---|---|
| AREA | area |
| ARTIST | artist |
| EVENT | event |
| INSTRUMENT | instrument |
| LABEL | label |
| PLACE | place |
| RECORDING | recording |
| RELEASE | release |
| RELEASE_GROUP | release-group |
| SERIES | series |
| WORK | work |
| Enum | Path |
|---|---|
| AREA | area |
| ARTIST | artist |
| GENRE | genre |
| EVENT | event |
| INSTRUMENT | instrument |
| LABEL | label |
| PLACE | place |
| RECORDING | recording |
| RELEASE | release |
| RELEASE_GROUP | release-group |
| RATING | rating |
| WORK | work |
| URL | url |
| Category | Fields |
|---|---|
| Common | alias, tag, comment |
| Artist | artist, artistname, arid, sortname |
| Recording | recording, recordingaccent, isrc, rid, reid, rgid, dur, qdur, video, creditname, tid |
| Release | release, releasegroup, country, format, status, primarytype, secondarytype, date, firstreleasedate, label, catno, barcode, lang |
| Work | work, iswc, type |
| Series | series |
| URL | url |
| Others | place, event, instrument, genre |
| Entity | Response |
|---|---|
| Annotation | AnnotationResponse |
| Area | AreaResponse |
| CdStub | CdStubsResponse |
| Event | EventResponse |
| Instrument | InstrumentResponse |
| Label | LabelResponse |
| Recording | RecordingResponse |
| Release | ReleaseResponse |
| Artist | ArtistResponse |
| Release Group | ReleaseGroupResponse |
| Series | SerieResponse |
| Tag | TagResponse |
| Url | UrlResponse |
| Work | WorkResponse |
| Place | PlaceResponse |
| Entity | Response |
|---|---|
| Area | AreaLookupResponse |
| Artist | ArtistLookupResponse |
| Event | EventLookupResponse |
| Genre | GenreLookupResponse |
| Instrument | InstrumentLookupResponse |
| Label | LabelLookupResponse |
| Place | PlaceLookupResponse |
| Recording | RecordingLookupResponse |
| Release | ReleaseLookupResponse |
| Release Group | ReleaseGroupLookupResponse |
| Url | UrlLookupResponse |
| Work | WorkLookupResponse |
Designed and developed by 2025 hall9zeha (Barry Zea H.)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
This project is not officially affiliated with MetaBrainz.