2 unstable releases

new 0.6.0 Jun 25, 2024
0.5.1 May 30, 2024

#133 in Asynchronous

Download history 361/week @ 2024-05-26 97/week @ 2024-06-02 171/week @ 2024-06-09 102/week @ 2024-06-16

731 downloads per month
Used in listenbrainz-cli-tools

MIT license

190KB
3K SLoC

MusicBrainz Rust  

Latest Version Build Status codecov GitHub tag (latest by date) Conventional Commits License

MusicBrainz rust is a utility crate for the the MusicBrainz API.

This is a fork of musicbrainz_rs crate, as it is currently seemingly unmaintained. While this crate is mostly meant as a temporary fix for another project, this crate can be used instead of the original.

This is meant to be a drop-in replacement, but may require some tweaks in some cases. Don't forget to test the switch.


you may be looking for :

Usage

You can choose to use either the default async client or a blocking one.

async client:

musicbrainz_rs_nova = "0.6.0"

blocking client:

musicbrainz_rs_nova = { version = "0.6.0", default-features = false, features = ["blocking"] }

If it's a migration from musicbrainz_rs:

musicbrainz_rs = {version = "0.6.0", package = "musicbrainz_rs_nova"}

Features

Note: All the example below use the blocking feature for the sake of conciseness.

Fetch query

To perform a lookups via fetch queries, you need to import the Fetch trait. This can be done using musicbrainz_rs::prelude

use musicbrainz_rs_nova::entity::artist;
use musicbrainz_rs_nova::entity::artist::*;
use musicbrainz_rs_nova::prelude::*;

fn main() {
    let nirvana = Artist::fetch()
        .id("5b11f4ce-a62d-471e-81fc-a69a8278c7da")
        .execute();

    assert_eq!(nirvana.unwrap().name, "Nirvana".to_string());
}

Include parameters

You can also use includes to get more detail about a resource :

Every Musicbrainz resource has allowed include parameters.

use musicbrainz_rs_nova::entity::label::*;
use musicbrainz_rs_nova::prelude::*;

fn main() {
    let ninja_tune = Label::fetch()
        .id("dc940013-b8a8-4362-a465-291026c04b42")
        .with_tags()
        .with_ratings()
        .execute()
        .unwrap();

    assert!(ninja_tune
        .tags
        .unwrap()
        .iter()
        .any(|tag| tag.name == "independent"));

    assert!(ninja_tune.rating.is_some());
}

CoverArt query

Release and ReleaseGroup entities in MusicBrainz also allow you to make CoverArt queries on them:

use musicbrainz_rs_nova::entity::release::*;
use musicbrainz_rs_nova::entity::CoverartResponse;
use musicbrainz_rs_nova::prelude::*;
use musicbrainz_rs_nova::FetchCoverart;

fn main() {
    // CoverArt Query for a Release.
    let in_utero_coverart = Release::fetch_coverart()
        .id("76df3287-6cda-33eb-8e9a-044b5e15ffdd")
        .execute()
        .expect("Unable to get cover art");

    if let CoverartResponse::Json(coverart) = in_utero_coverart {
        assert!(!coverart.images[0].back);
        assert_eq!(
            coverart.images[0].image,
            "http://coverartarchive.org/release/76df3287-6cda-33eb-8e9a-044b5e15ffdd/829521842.jpg"
        );
    } else {
        assert!(false);
    }

    let in_utero = Release::fetch()
        .id("76df3287-6cda-33eb-8e9a-044b5e15ffdd")
        .execute()
        .expect("Unable to get release");

    // Calling `get_coverart()` method on an already fetched Release entity.
    let in_utero_coverart = in_utero
        .get_coverart()
        .execute()
        .expect("Unable to get coverart");

    if let CoverartResponse::Json(coverart) = in_utero_coverart {
        assert!(!coverart.images[0].back);
        assert_eq!(
            coverart.images[0].image,
            "http://coverartarchive.org/release/76df3287-6cda-33eb-8e9a-044b5e15ffdd/829521842.jpg"
        );
    } else {
        assert!(false);
    }

    // CoverArt Query Builder to fetch a specific resource.
    let in_utero_500px_front_coverart = Release::fetch_coverart()
        .id("76df3287-6cda-33eb-8e9a-044b5e15ffdd")
        .res_500()
        .back()
        .execute()
        .expect("Unable to get cover art");

    if let CoverartResponse::Url(coverart_url) = in_utero_500px_front_coverart {
        println!("{}", coverart_url);
    } else {
        assert!(false);
    }
}

Browse query

Use musicbrainz_rs::Browse or bring it in scope using musicbrainz_rs::prelude to perform a browse query. Just like Include every muscibrainz resource has allowable linked entities for such queries.

use musicbrainz_rs_nova::entity::artist;
use musicbrainz_rs_nova::entity::artist::Artist;
use musicbrainz_rs_nova::prelude::*;

fn main() {
    let artists_on_in_utero_release = Artist::browse()
        .by_release("18d4e9b4-9247-4b44-914a-8ddec3502103")
        .execute();

    let artists_on_in_utero_release = artists_on_in_utero_release.unwrap();

    artists_on_in_utero_release
        .entities
        .iter()
        .for_each(|artist| println!("{:?}", artist.name));
}

Search query

Use musicbrainz_rs::Search to perform a search query.

use musicbrainz_rs_nova::entity::artist::Artist;
use musicbrainz_rs_nova::prelude::*;

fn main() {
    musicbrainz_rs::config::set_user_agent("my_awesome_app/1.0");

    let query = Artist::query_builder()
        .artist("Miles Davis")
        .and()
        .country("US")
        .build();

    let query_result = Artist::search(query).execute().unwrap();
    let query_result: Vec<String> = query_result.entities
        .iter()
        .map(|artist| artist.name.clone()).collect();

    assert!(query_result.contains(&"Miles Davis".to_string()));
    assert!(query_result.contains(&"Miles Davis Quintet".to_string()));
}

Custom user agent

You can set your application user-agent as recommended in the musicbrainz documentation :

use musicbrainz_rs_nova::entity::artist::Artist;
use musicbrainz_rs_nova::prelude::*;

fn main() {
    musicbrainz_rs::config::set_user_agent("my_awesome_app/1.0");

    let nirvana = Artist::fetch()
        .id("5b11f4ce-a62d-471e-81fc-a69a8278c7da")
        .execute();

    assert_eq!(nirvana.unwrap().name, "Nirvana".to_string());
}

Rate limit

By default, a rate limiter of 1req/sec is implemented according to MB's policy. This allow to fearlessly send heaps of requests without worrying about DDOSing MusicBrainz. This feature is only available bundled with the async feature, as it require an async runtime. But this isn't an issue for blocking users, as the api is a bit lenient, and calling requests in a loop rarely achieve 1req/sec

Examples

To see what is currently implemented in the crate you can look at the tests directory.

You can run examples with cargo run --example example_name

Cargo Features

Here is the list of supported feature values. The default features are: async, rate_limit and reqwest/default-tls

  • blocking: use a blocking client
  • async: use an async client
  • rate_limit: add a rate limiter for the requests. Require async
  • rustls: Use rustls instead of the platform's tls

MSRV

The Minimum Supported Rust Version for cacache is 1.64.0. Any bump to the MSRV will be considered breaking changes.

Currently the MSRV hasn't been checked if it's the actual minimum version, so it may go down. If it's an issue for you, please send a reply here and I'll hurry up. Similarly, it may bump up to 1.75 if async in traits is needed

Contributing

All contributions are welcome, if find a bug or have a feature request don't hesitate to open an issue!

Credits

Most of this crate documentation is taken from the official MusicBrainz doc, thanks to the MetaBrainz Foundation and its sponsors and supporters. Cover Art provided by the Cover Art Archive.

Dependencies

~5–19MB
~295K SLoC