112 lines
3.2 KiB
Rust
112 lines
3.2 KiB
Rust
use std::collections::HashSet;
|
|
|
|
use musicbrainz_rs::{
|
|
config,
|
|
entity::{
|
|
artist::{Artist, ArtistSearchQuery, ArtistType},
|
|
recording::Recording,
|
|
relations::{Relation, RelationContent},
|
|
},
|
|
prelude::*,
|
|
};
|
|
|
|
use crate::library;
|
|
|
|
const USER_AGENT: &str = "cover/0.1.0 ( mat@mat.services )";
|
|
|
|
pub async fn fetch_library(
|
|
names: impl IntoIterator<Item = String>,
|
|
) -> Result<library::Library, Error> {
|
|
config::set_user_agent(USER_AGENT);
|
|
let mut artists = vec![];
|
|
for name in names {
|
|
let artist = fetch_artist(name).await?;
|
|
println!("{artist:#?}");
|
|
artists.push(artist);
|
|
}
|
|
Ok(library::Library::new(artists))
|
|
}
|
|
|
|
async fn fetch_artist(name: String) -> Result<library::Artist, Error> {
|
|
let query = ArtistSearchQuery::query_builder().artist(&name).build();
|
|
let result = Artist::search(query)
|
|
.with_artist_relations()
|
|
.execute()
|
|
.await?;
|
|
let artist = result.entities.first().expect("Failed to load {name}");
|
|
let artist = Artist::fetch()
|
|
.id(&artist.id)
|
|
.with_artist_relations()
|
|
.execute()
|
|
.await?;
|
|
let members =
|
|
if artist.artist_type.is_some() && is_plural_artist(artist.artist_type.as_ref().unwrap()) {
|
|
artist
|
|
.relations
|
|
.as_ref()
|
|
.expect("Failed to load relations")
|
|
.iter()
|
|
.flat_map(member_from_relation)
|
|
.collect()
|
|
} else {
|
|
vec![artist.name.clone()]
|
|
};
|
|
let recordings = Recording::browse()
|
|
.by_artist(&artist.id)
|
|
.with_work_relations()
|
|
.with_work_level_relations()
|
|
.execute()
|
|
.await?;
|
|
let catalog = recordings
|
|
.entities
|
|
.into_iter()
|
|
.map(|recording| convert_recording(&artist, &members, recording));
|
|
Ok(library::Artist::new(name, members.clone(), catalog))
|
|
}
|
|
|
|
fn is_plural_artist(kind: &ArtistType) -> bool {
|
|
matches!(
|
|
kind,
|
|
ArtistType::Choir | ArtistType::Group | ArtistType::Orchestra
|
|
)
|
|
}
|
|
|
|
fn member_from_relation(relation: &Relation) -> Option<String> {
|
|
if relation.relation_type == "member of band" {
|
|
let artist = extract_artist(relation);
|
|
Some(artist.name.clone())
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
|
|
fn convert_recording(artist: &Artist, members: &[String], recording: Recording) -> library::Song {
|
|
let writers = get_writers(members, &recording);
|
|
library::Song {
|
|
title: recording.title,
|
|
performer: artist.name.clone(),
|
|
writers,
|
|
}
|
|
}
|
|
|
|
fn get_writers(members: &[String], recording: &Recording) -> HashSet<String> {
|
|
recording.relations.as_ref().map_or_else(
|
|
|| HashSet::from_iter(members.iter().cloned()),
|
|
|relations| relations.iter().flat_map(writer_from_relation).collect(),
|
|
)
|
|
}
|
|
|
|
fn writer_from_relation(relation: &Relation) -> Option<String> {
|
|
match relation.relation_type.as_str() {
|
|
"writer" | "composer" => Some(extract_artist(relation).name.clone()),
|
|
_ => None,
|
|
}
|
|
}
|
|
|
|
fn extract_artist(relation: &Relation) -> &Artist {
|
|
match &relation.content {
|
|
RelationContent::Artist(artist) => artist,
|
|
_ => unreachable!("Bad relation"),
|
|
}
|
|
}
|