Decide carefully where external::musicbrainz
belongs
#196
@ -45,14 +45,14 @@ name = "musichoard-edit"
|
|||||||
required-features = ["bin", "database-json"]
|
required-features = ["bin", "database-json"]
|
||||||
|
|
||||||
[[example]]
|
[[example]]
|
||||||
name = "musicbrainz-api---lookup-artist-release-groups"
|
name = "musicbrainz-api---lookup-artist"
|
||||||
path = "examples/musicbrainz_api/lookup_artist_release_groups.rs"
|
path = "examples/musicbrainz_api/lookup_artist.rs"
|
||||||
required-features = ["bin", "musicbrainz-api"]
|
required-features = ["bin", "musicbrainz"]
|
||||||
|
|
||||||
[[example]]
|
[[example]]
|
||||||
name = "musicbrainz-api---search-release-group"
|
name = "musicbrainz-api---search-release-group"
|
||||||
path = "examples/musicbrainz_api/search_release_group.rs"
|
path = "examples/musicbrainz_api/search_release_group.rs"
|
||||||
required-features = ["bin", "musicbrainz-api"]
|
required-features = ["bin", "musicbrainz"]
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
[package.metadata.docs.rs]
|
||||||
all-features = true
|
all-features = true
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
#![allow(non_snake_case)]
|
#![allow(non_snake_case)]
|
||||||
|
|
||||||
use musichoard::{
|
use musichoard::{
|
||||||
external::musicbrainz::api::{client::MusicBrainzApiClient, MusicBrainzApi},
|
external::musicbrainz::{http::MusicBrainzHttp, LookupArtistRequest, MusicBrainzClient},
|
||||||
interface::musicbrainz::{IMusicBrainz, Mbid},
|
interface::musicbrainz::Mbid,
|
||||||
};
|
};
|
||||||
use structopt::StructOpt;
|
use structopt::StructOpt;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
const USER_AGENT: &str = concat!(
|
const USER_AGENT: &str = concat!(
|
||||||
"MusicHoard---examples---musicbrainz-api---lookup-artist-release-groups/",
|
"MusicHoard---examples---musicbrainz-api---lookup-artist/",
|
||||||
env!("CARGO_PKG_VERSION"),
|
env!("CARGO_PKG_VERSION"),
|
||||||
" ( musichoard@thenineworlds.net )"
|
" ( musichoard@thenineworlds.net )"
|
||||||
);
|
);
|
||||||
@ -24,12 +24,15 @@ fn main() {
|
|||||||
|
|
||||||
println!("USER_AGENT: {USER_AGENT}");
|
println!("USER_AGENT: {USER_AGENT}");
|
||||||
|
|
||||||
let client = MusicBrainzApiClient::new(USER_AGENT).expect("failed to create API client");
|
let http = MusicBrainzHttp::new(USER_AGENT).expect("failed to create API client");
|
||||||
let mut api = MusicBrainzApi::new(client);
|
let mut client = MusicBrainzClient::new(http);
|
||||||
|
|
||||||
let mbid: Mbid = opt.mbid.into();
|
let mbid: Mbid = opt.mbid.into();
|
||||||
let albums = api
|
let mut request = LookupArtistRequest::new(&mbid);
|
||||||
.lookup_artist_release_groups(&mbid)
|
request.include_release_groups();
|
||||||
|
|
||||||
|
let albums = client
|
||||||
|
.lookup_artist(request)
|
||||||
.expect("failed to make API call");
|
.expect("failed to make API call");
|
||||||
|
|
||||||
println!("{albums:#?}");
|
println!("{albums:#?}");
|
@ -3,9 +3,9 @@
|
|||||||
use std::{num::ParseIntError, str::FromStr};
|
use std::{num::ParseIntError, str::FromStr};
|
||||||
|
|
||||||
use musichoard::{
|
use musichoard::{
|
||||||
collection::album::{Album, AlbumDate, AlbumId},
|
collection::album::AlbumDate,
|
||||||
external::musicbrainz::api::{client::MusicBrainzApiClient, MusicBrainzApi},
|
external::musicbrainz::{http::MusicBrainzHttp, MusicBrainzClient, SearchReleaseGroupRequest},
|
||||||
interface::musicbrainz::{IMusicBrainz, Mbid},
|
interface::musicbrainz::Mbid,
|
||||||
};
|
};
|
||||||
use structopt::StructOpt;
|
use structopt::StructOpt;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
@ -18,16 +18,13 @@ const USER_AGENT: &str = concat!(
|
|||||||
|
|
||||||
#[derive(StructOpt)]
|
#[derive(StructOpt)]
|
||||||
struct Opt {
|
struct Opt {
|
||||||
#[structopt(help = "Release group's artist MBID")]
|
|
||||||
arid: Uuid,
|
|
||||||
|
|
||||||
#[structopt(subcommand)]
|
#[structopt(subcommand)]
|
||||||
command: OptCommand,
|
command: OptCommand,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(StructOpt)]
|
#[derive(StructOpt)]
|
||||||
enum OptCommand {
|
enum OptCommand {
|
||||||
#[structopt(about = "Search by title (and date)")]
|
#[structopt(about = "Search by artist MBID, title(, and date)")]
|
||||||
Title(OptTitle),
|
Title(OptTitle),
|
||||||
#[structopt(about = "Search by release group MBID")]
|
#[structopt(about = "Search by release group MBID")]
|
||||||
Rgid(OptRgid),
|
Rgid(OptRgid),
|
||||||
@ -35,6 +32,9 @@ enum OptCommand {
|
|||||||
|
|
||||||
#[derive(StructOpt)]
|
#[derive(StructOpt)]
|
||||||
struct OptTitle {
|
struct OptTitle {
|
||||||
|
#[structopt(help = "Release group's artist MBID")]
|
||||||
|
arid: Uuid,
|
||||||
|
|
||||||
#[structopt(help = "Release group title")]
|
#[structopt(help = "Release group title")]
|
||||||
title: String,
|
title: String,
|
||||||
|
|
||||||
@ -80,30 +80,32 @@ fn main() {
|
|||||||
|
|
||||||
println!("USER_AGENT: {USER_AGENT}");
|
println!("USER_AGENT: {USER_AGENT}");
|
||||||
|
|
||||||
let client = MusicBrainzApiClient::new(USER_AGENT).expect("failed to create API client");
|
let http = MusicBrainzHttp::new(USER_AGENT).expect("failed to create API client");
|
||||||
let mut api = MusicBrainzApi::new(client);
|
let mut client = MusicBrainzClient::new(http);
|
||||||
|
|
||||||
let arid: Mbid = opt.arid.into();
|
let mut request = SearchReleaseGroupRequest::default();
|
||||||
|
let arid: Mbid;
|
||||||
let album = match opt.command {
|
let date: AlbumDate;
|
||||||
|
let title: String;
|
||||||
|
let rgid: Mbid;
|
||||||
|
match opt.command {
|
||||||
OptCommand::Title(opt_title) => {
|
OptCommand::Title(opt_title) => {
|
||||||
let date: AlbumDate = opt_title.date.map(Into::into).unwrap_or_default();
|
arid = opt_title.arid.into();
|
||||||
Album::new(AlbumId::new(opt_title.title), date, None, vec![])
|
date = opt_title.date.map(Into::into).unwrap_or_default();
|
||||||
|
title = opt_title.title;
|
||||||
|
request
|
||||||
|
.arid(&arid)
|
||||||
|
.first_release_date(&date)
|
||||||
|
.release_group(&title);
|
||||||
}
|
}
|
||||||
OptCommand::Rgid(opt_rgid) => {
|
OptCommand::Rgid(opt_rgid) => {
|
||||||
let mut album = Album::new(
|
rgid = opt_rgid.rgid.into();
|
||||||
AlbumId::new(String::default()),
|
request.rgid(&rgid);
|
||||||
AlbumDate::default(),
|
|
||||||
None,
|
|
||||||
vec![],
|
|
||||||
);
|
|
||||||
album.set_musicbrainz_ref(opt_rgid.rgid.into());
|
|
||||||
album
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let matches = api
|
let matches = client
|
||||||
.search_release_group(&arid, &album)
|
.search_release_group(request)
|
||||||
.expect("failed to make API call");
|
.expect("failed to make API call");
|
||||||
|
|
||||||
println!("{matches:#?}");
|
println!("{matches:#?}");
|
||||||
|
115
src/external/musicbrainz/mod.rs
vendored
115
src/external/musicbrainz/mod.rs
vendored
@ -78,7 +78,7 @@ impl<Http> MusicBrainzClient<Http> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<Http: IMusicBrainzHttp> MusicBrainzClient<Http> {
|
impl<Http: IMusicBrainzHttp> MusicBrainzClient<Http> {
|
||||||
fn display_album_date(date: &AlbumDate) -> Option<String> {
|
fn format_album_date(date: &AlbumDate) -> Option<String> {
|
||||||
match date.year {
|
match date.year {
|
||||||
Some(year) => match date.month {
|
Some(year) => match date.month {
|
||||||
Some(month) => match date.day {
|
Some(month) => match date.day {
|
||||||
@ -91,6 +91,26 @@ impl<Http: IMusicBrainzHttp> MusicBrainzClient<Http> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn lookup_artist(
|
||||||
|
&mut self,
|
||||||
|
request: LookupArtistRequest,
|
||||||
|
) -> Result<LookupArtistResponse, Error> {
|
||||||
|
let mut include: Vec<String> = vec![];
|
||||||
|
|
||||||
|
let mbid: String = request.mbid.uuid().as_hyphenated().to_string();
|
||||||
|
|
||||||
|
if request.release_groups {
|
||||||
|
include.push(String::from("release-groups"));
|
||||||
|
}
|
||||||
|
|
||||||
|
let include: String =
|
||||||
|
form_urlencoded::byte_serialize(include.join("+").as_bytes()).collect();
|
||||||
|
let url = format!("{MB_BASE_URL}/artist/{mbid}?inc={include}");
|
||||||
|
|
||||||
|
let response: DeserializeLookupArtistResponse = self.http.get(&url)?;
|
||||||
|
Ok(response.into())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn search_release_group(
|
pub fn search_release_group(
|
||||||
&mut self,
|
&mut self,
|
||||||
request: SearchReleaseGroupRequest,
|
request: SearchReleaseGroupRequest,
|
||||||
@ -102,7 +122,7 @@ impl<Http: IMusicBrainzHttp> MusicBrainzClient<Http> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let Some(date) = request.first_release_date {
|
if let Some(date) = request.first_release_date {
|
||||||
if let Some(date_string) = Self::display_album_date(date) {
|
if let Some(date_string) = Self::format_album_date(date) {
|
||||||
query.push(format!("firstreleasedate:{date_string}"))
|
query.push(format!("firstreleasedate:{date_string}"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -124,6 +144,75 @@ impl<Http: IMusicBrainzHttp> MusicBrainzClient<Http> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct LookupArtistRequest<'a> {
|
||||||
|
mbid: &'a Mbid,
|
||||||
|
release_groups: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> LookupArtistRequest<'a> {
|
||||||
|
pub fn new(mbid: &'a Mbid) -> Self {
|
||||||
|
LookupArtistRequest {
|
||||||
|
mbid,
|
||||||
|
release_groups: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn include_release_groups(&mut self) -> &mut Self {
|
||||||
|
self.release_groups = true;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct LookupArtistResponse {
|
||||||
|
pub release_groups: Vec<LookupArtistResponseReleaseGroup>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
#[serde(rename_all(deserialize = "kebab-case"))]
|
||||||
|
struct DeserializeLookupArtistResponse {
|
||||||
|
release_groups: Vec<DeserializeLookupArtistResponseReleaseGroup>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<DeserializeLookupArtistResponse> for LookupArtistResponse {
|
||||||
|
fn from(value: DeserializeLookupArtistResponse) -> Self {
|
||||||
|
LookupArtistResponse {
|
||||||
|
release_groups: value.release_groups.into_iter().map(Into::into).collect(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct LookupArtistResponseReleaseGroup {
|
||||||
|
pub id: Mbid,
|
||||||
|
pub title: String,
|
||||||
|
pub first_release_date: AlbumDate,
|
||||||
|
pub primary_type: AlbumPrimaryType,
|
||||||
|
pub secondary_types: Vec<AlbumSecondaryType>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
#[serde(rename_all(deserialize = "kebab-case"))]
|
||||||
|
struct DeserializeLookupArtistResponseReleaseGroup {
|
||||||
|
id: SerdeMbid,
|
||||||
|
title: String,
|
||||||
|
first_release_date: SerdeAlbumDate,
|
||||||
|
primary_type: SerdeAlbumPrimaryType,
|
||||||
|
secondary_types: Vec<SerdeAlbumSecondaryType>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<DeserializeLookupArtistResponseReleaseGroup> for LookupArtistResponseReleaseGroup {
|
||||||
|
fn from(value: DeserializeLookupArtistResponseReleaseGroup) -> Self {
|
||||||
|
LookupArtistResponseReleaseGroup {
|
||||||
|
id: value.id.into(),
|
||||||
|
title: value.title,
|
||||||
|
first_release_date: value.first_release_date.into(),
|
||||||
|
primary_type: value.primary_type.into(),
|
||||||
|
secondary_types: value.secondary_types.into_iter().map(Into::into).collect(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct SearchReleaseGroupRequest<'a> {
|
pub struct SearchReleaseGroupRequest<'a> {
|
||||||
arid: Option<&'a Mbid>,
|
arid: Option<&'a Mbid>,
|
||||||
@ -133,6 +222,10 @@ pub struct SearchReleaseGroupRequest<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> SearchReleaseGroupRequest<'a> {
|
impl<'a> SearchReleaseGroupRequest<'a> {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self::default()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn arid(&mut self, arid: &'a Mbid) -> &mut Self {
|
pub fn arid(&mut self, arid: &'a Mbid) -> &mut Self {
|
||||||
self.arid = Some(arid);
|
self.arid = Some(arid);
|
||||||
self
|
self
|
||||||
@ -154,14 +247,15 @@ impl<'a> SearchReleaseGroupRequest<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct SearchReleaseGroupResponse {
|
pub struct SearchReleaseGroupResponse {
|
||||||
pub release_groups: Vec<SearchReleaseGroupResponseUnit>,
|
pub release_groups: Vec<SearchReleaseGroupResponseReleaseGroup>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
#[serde(rename_all(deserialize = "kebab-case"))]
|
#[serde(rename_all(deserialize = "kebab-case"))]
|
||||||
struct DeserializeSearchReleaseGroupResponse {
|
struct DeserializeSearchReleaseGroupResponse {
|
||||||
release_groups: Vec<DeserializeSearchReleaseGroupResponseUnit>,
|
release_groups: Vec<DeserializeSearchReleaseGroupResponseReleaseGroup>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<DeserializeSearchReleaseGroupResponse> for SearchReleaseGroupResponse {
|
impl From<DeserializeSearchReleaseGroupResponse> for SearchReleaseGroupResponse {
|
||||||
@ -172,7 +266,8 @@ impl From<DeserializeSearchReleaseGroupResponse> for SearchReleaseGroupResponse
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct SearchReleaseGroupResponseUnit {
|
#[derive(Debug)]
|
||||||
|
pub struct SearchReleaseGroupResponseReleaseGroup {
|
||||||
pub score: u8,
|
pub score: u8,
|
||||||
pub id: Mbid,
|
pub id: Mbid,
|
||||||
pub title: String,
|
pub title: String,
|
||||||
@ -183,7 +278,7 @@ pub struct SearchReleaseGroupResponseUnit {
|
|||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
#[serde(rename_all(deserialize = "kebab-case"))]
|
#[serde(rename_all(deserialize = "kebab-case"))]
|
||||||
struct DeserializeSearchReleaseGroupResponseUnit {
|
struct DeserializeSearchReleaseGroupResponseReleaseGroup {
|
||||||
score: u8,
|
score: u8,
|
||||||
id: SerdeMbid,
|
id: SerdeMbid,
|
||||||
title: String,
|
title: String,
|
||||||
@ -192,9 +287,11 @@ struct DeserializeSearchReleaseGroupResponseUnit {
|
|||||||
secondary_types: Option<Vec<SerdeAlbumSecondaryType>>,
|
secondary_types: Option<Vec<SerdeAlbumSecondaryType>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<DeserializeSearchReleaseGroupResponseUnit> for SearchReleaseGroupResponseUnit {
|
impl From<DeserializeSearchReleaseGroupResponseReleaseGroup>
|
||||||
fn from(value: DeserializeSearchReleaseGroupResponseUnit) -> Self {
|
for SearchReleaseGroupResponseReleaseGroup
|
||||||
SearchReleaseGroupResponseUnit {
|
{
|
||||||
|
fn from(value: DeserializeSearchReleaseGroupResponseReleaseGroup) -> Self {
|
||||||
|
SearchReleaseGroupResponseReleaseGroup {
|
||||||
score: value.score,
|
score: value.score,
|
||||||
id: value.id.into(),
|
id: value.id.into(),
|
||||||
title: value.title,
|
title: value.title,
|
||||||
|
8
src/tui/lib/external/musicbrainz/mod.rs
vendored
8
src/tui/lib/external/musicbrainz/mod.rs
vendored
@ -4,7 +4,7 @@ use musichoard::{
|
|||||||
collection::album::{Album, AlbumDate},
|
collection::album::{Album, AlbumDate},
|
||||||
external::musicbrainz::{
|
external::musicbrainz::{
|
||||||
IMusicBrainzHttp, MusicBrainzClient, SearchReleaseGroupRequest,
|
IMusicBrainzHttp, MusicBrainzClient, SearchReleaseGroupRequest,
|
||||||
SearchReleaseGroupResponseUnit,
|
SearchReleaseGroupResponseReleaseGroup,
|
||||||
},
|
},
|
||||||
interface::musicbrainz::Mbid,
|
interface::musicbrainz::Mbid,
|
||||||
};
|
};
|
||||||
@ -42,12 +42,14 @@ impl<Http: IMusicBrainzHttp> IMusicBrainz for MusicBrainz<Http> {
|
|||||||
Ok(mb_response
|
Ok(mb_response
|
||||||
.release_groups
|
.release_groups
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(from_search_release_group_response_unit)
|
.map(from_search_release_group_response_release_group)
|
||||||
.collect())
|
.collect())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn from_search_release_group_response_unit(entity: SearchReleaseGroupResponseUnit) -> Match<Album> {
|
fn from_search_release_group_response_release_group(
|
||||||
|
entity: SearchReleaseGroupResponseReleaseGroup,
|
||||||
|
) -> Match<Album> {
|
||||||
let mut album = Album::new(
|
let mut album = Album::new(
|
||||||
entity.title,
|
entity.title,
|
||||||
entity.first_release_date,
|
entity.first_release_date,
|
||||||
|
Loading…
Reference in New Issue
Block a user