Enable fetch to apply modifications to the database #221
@ -171,10 +171,7 @@ impl SortCommand {
|
||||
fn handle(self, music_hoard: &mut MH, artist_name: &str) {
|
||||
match self {
|
||||
SortCommand::Set(artist_sort_value) => music_hoard
|
||||
.set_artist_sort(
|
||||
ArtistId::new(artist_name),
|
||||
String::from(artist_sort_value.sort),
|
||||
)
|
||||
.set_artist_sort(ArtistId::new(artist_name), artist_sort_value.sort)
|
||||
.expect("faild to set artist sort name"),
|
||||
SortCommand::Clear => music_hoard
|
||||
.clear_artist_sort(ArtistId::new(artist_name))
|
||||
|
@ -22,6 +22,12 @@ pub struct Artist {
|
||||
pub struct ArtistMeta {
|
||||
pub id: ArtistId,
|
||||
pub sort: Option<String>,
|
||||
pub info: ArtistInfo,
|
||||
}
|
||||
|
||||
/// Artist non-identifier metadata.
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct ArtistInfo {
|
||||
pub musicbrainz: MbRefOption<MbArtistRef>,
|
||||
pub properties: HashMap<String, Vec<String>>,
|
||||
}
|
||||
@ -75,8 +81,10 @@ impl ArtistMeta {
|
||||
ArtistMeta {
|
||||
id: id.into(),
|
||||
sort: None,
|
||||
musicbrainz: MbRefOption::None,
|
||||
properties: HashMap::new(),
|
||||
info: ArtistInfo {
|
||||
musicbrainz: MbRefOption::None,
|
||||
properties: HashMap::new(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@ -91,7 +99,9 @@ impl ArtistMeta {
|
||||
pub fn clear_sort_key(&mut self) {
|
||||
self.sort.take();
|
||||
}
|
||||
}
|
||||
|
||||
impl ArtistInfo {
|
||||
pub fn set_musicbrainz_ref(&mut self, mbref: MbRefOption<MbArtistRef>) {
|
||||
self.musicbrainz = mbref
|
||||
}
|
||||
@ -162,6 +172,12 @@ impl Merge for ArtistMeta {
|
||||
assert_eq!(self.id, other.id);
|
||||
|
||||
self.sort = self.sort.take().or(other.sort);
|
||||
self.info.merge_in_place(other.info);
|
||||
}
|
||||
}
|
||||
|
||||
impl Merge for ArtistInfo {
|
||||
fn merge_in_place(&mut self, other: Self) {
|
||||
self.musicbrainz = self.musicbrainz.take().or(other.musicbrainz);
|
||||
self.properties.merge_in_place(other.properties);
|
||||
}
|
||||
@ -256,160 +272,132 @@ mod tests {
|
||||
let mut artist = Artist::new(ArtistId::new("an artist"));
|
||||
|
||||
let mut expected: MbRefOption<MbArtistRef> = MbRefOption::None;
|
||||
assert_eq!(artist.meta.musicbrainz, expected);
|
||||
assert_eq!(artist.meta.info.musicbrainz, expected);
|
||||
|
||||
// Setting a URL on an artist.
|
||||
artist.meta.set_musicbrainz_ref(MbRefOption::Some(
|
||||
artist.meta.info.set_musicbrainz_ref(MbRefOption::Some(
|
||||
MbArtistRef::from_url_str(MUSICBRAINZ).unwrap(),
|
||||
));
|
||||
expected.replace(MbArtistRef::from_url_str(MUSICBRAINZ).unwrap());
|
||||
assert_eq!(artist.meta.musicbrainz, expected);
|
||||
assert_eq!(artist.meta.info.musicbrainz, expected);
|
||||
|
||||
artist.meta.set_musicbrainz_ref(MbRefOption::Some(
|
||||
artist.meta.info.set_musicbrainz_ref(MbRefOption::Some(
|
||||
MbArtistRef::from_url_str(MUSICBRAINZ).unwrap(),
|
||||
));
|
||||
assert_eq!(artist.meta.musicbrainz, expected);
|
||||
assert_eq!(artist.meta.info.musicbrainz, expected);
|
||||
|
||||
artist.meta.set_musicbrainz_ref(MbRefOption::Some(
|
||||
artist.meta.info.set_musicbrainz_ref(MbRefOption::Some(
|
||||
MbArtistRef::from_url_str(MUSICBRAINZ_2).unwrap(),
|
||||
));
|
||||
expected.replace(MbArtistRef::from_url_str(MUSICBRAINZ_2).unwrap());
|
||||
assert_eq!(artist.meta.musicbrainz, expected);
|
||||
assert_eq!(artist.meta.info.musicbrainz, expected);
|
||||
|
||||
// Clearing URLs.
|
||||
artist.meta.clear_musicbrainz_ref();
|
||||
artist.meta.info.clear_musicbrainz_ref();
|
||||
expected.take();
|
||||
assert_eq!(artist.meta.musicbrainz, expected);
|
||||
assert_eq!(artist.meta.info.musicbrainz, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn add_to_remove_from_property() {
|
||||
let mut artist = Artist::new(ArtistId::new("an artist"));
|
||||
|
||||
let info = &mut artist.meta.info;
|
||||
let mut expected: Vec<String> = vec![];
|
||||
assert!(artist.meta.properties.is_empty());
|
||||
assert!(info.properties.is_empty());
|
||||
|
||||
// Adding a single URL.
|
||||
artist
|
||||
.meta
|
||||
.add_to_property("MusicButler", vec![MUSICBUTLER]);
|
||||
info.add_to_property("MusicButler", vec![MUSICBUTLER]);
|
||||
expected.push(MUSICBUTLER.to_owned());
|
||||
assert_eq!(artist.meta.properties.get("MusicButler"), Some(&expected));
|
||||
assert_eq!(info.properties.get("MusicButler"), Some(&expected));
|
||||
|
||||
// Adding a URL that already exists is ok, but does not do anything.
|
||||
artist
|
||||
.meta
|
||||
.add_to_property("MusicButler", vec![MUSICBUTLER]);
|
||||
assert_eq!(artist.meta.properties.get("MusicButler"), Some(&expected));
|
||||
info.add_to_property("MusicButler", vec![MUSICBUTLER]);
|
||||
assert_eq!(info.properties.get("MusicButler"), Some(&expected));
|
||||
|
||||
// Adding another single URL.
|
||||
artist
|
||||
.meta
|
||||
.add_to_property("MusicButler", vec![MUSICBUTLER_2]);
|
||||
info.add_to_property("MusicButler", vec![MUSICBUTLER_2]);
|
||||
expected.push(MUSICBUTLER_2.to_owned());
|
||||
assert_eq!(artist.meta.properties.get("MusicButler"), Some(&expected));
|
||||
assert_eq!(info.properties.get("MusicButler"), Some(&expected));
|
||||
|
||||
artist
|
||||
.meta
|
||||
.add_to_property("MusicButler", vec![MUSICBUTLER_2]);
|
||||
assert_eq!(artist.meta.properties.get("MusicButler"), Some(&expected));
|
||||
info.add_to_property("MusicButler", vec![MUSICBUTLER_2]);
|
||||
assert_eq!(info.properties.get("MusicButler"), Some(&expected));
|
||||
|
||||
// Removing a URL.
|
||||
artist
|
||||
.meta
|
||||
.remove_from_property("MusicButler", vec![MUSICBUTLER]);
|
||||
info.remove_from_property("MusicButler", vec![MUSICBUTLER]);
|
||||
expected.retain(|url| url != MUSICBUTLER);
|
||||
assert_eq!(artist.meta.properties.get("MusicButler"), Some(&expected));
|
||||
assert_eq!(info.properties.get("MusicButler"), Some(&expected));
|
||||
|
||||
// Removing URls that do not exist is okay, they will be ignored.
|
||||
artist
|
||||
.meta
|
||||
.remove_from_property("MusicButler", vec![MUSICBUTLER]);
|
||||
assert_eq!(artist.meta.properties.get("MusicButler"), Some(&expected));
|
||||
info.remove_from_property("MusicButler", vec![MUSICBUTLER]);
|
||||
assert_eq!(info.properties.get("MusicButler"), Some(&expected));
|
||||
|
||||
// Removing a URL.
|
||||
artist
|
||||
.meta
|
||||
.remove_from_property("MusicButler", vec![MUSICBUTLER_2]);
|
||||
info.remove_from_property("MusicButler", vec![MUSICBUTLER_2]);
|
||||
expected.retain(|url| url.as_str() != MUSICBUTLER_2);
|
||||
assert!(artist.meta.properties.is_empty());
|
||||
assert!(info.properties.is_empty());
|
||||
|
||||
artist
|
||||
.meta
|
||||
.remove_from_property("MusicButler", vec![MUSICBUTLER_2]);
|
||||
assert!(artist.meta.properties.is_empty());
|
||||
info.remove_from_property("MusicButler", vec![MUSICBUTLER_2]);
|
||||
assert!(info.properties.is_empty());
|
||||
|
||||
// Adding URLs if some exist is okay, they will be ignored.
|
||||
artist
|
||||
.meta
|
||||
.add_to_property("MusicButler", vec![MUSICBUTLER]);
|
||||
info.add_to_property("MusicButler", vec![MUSICBUTLER]);
|
||||
expected.push(MUSICBUTLER.to_owned());
|
||||
assert_eq!(artist.meta.properties.get("MusicButler"), Some(&expected));
|
||||
assert_eq!(info.properties.get("MusicButler"), Some(&expected));
|
||||
|
||||
artist
|
||||
.meta
|
||||
.add_to_property("MusicButler", vec![MUSICBUTLER, MUSICBUTLER_2]);
|
||||
info.add_to_property("MusicButler", vec![MUSICBUTLER, MUSICBUTLER_2]);
|
||||
expected.push(MUSICBUTLER_2.to_owned());
|
||||
assert_eq!(artist.meta.properties.get("MusicButler"), Some(&expected));
|
||||
assert_eq!(info.properties.get("MusicButler"), Some(&expected));
|
||||
|
||||
// Removing URLs if some do not exist is okay, they will be ignored.
|
||||
artist
|
||||
.meta
|
||||
.remove_from_property("MusicButler", vec![MUSICBUTLER]);
|
||||
info.remove_from_property("MusicButler", vec![MUSICBUTLER]);
|
||||
expected.retain(|url| url.as_str() != MUSICBUTLER);
|
||||
assert_eq!(artist.meta.properties.get("MusicButler"), Some(&expected));
|
||||
assert_eq!(info.properties.get("MusicButler"), Some(&expected));
|
||||
|
||||
artist
|
||||
.meta
|
||||
.remove_from_property("MusicButler", vec![MUSICBUTLER, MUSICBUTLER_2]);
|
||||
info.remove_from_property("MusicButler", vec![MUSICBUTLER, MUSICBUTLER_2]);
|
||||
expected.retain(|url| url.as_str() != MUSICBUTLER_2);
|
||||
assert!(artist.meta.properties.is_empty());
|
||||
assert!(info.properties.is_empty());
|
||||
|
||||
// Adding mutliple URLs without clashes.
|
||||
artist
|
||||
.meta
|
||||
.add_to_property("MusicButler", vec![MUSICBUTLER, MUSICBUTLER_2]);
|
||||
info.add_to_property("MusicButler", vec![MUSICBUTLER, MUSICBUTLER_2]);
|
||||
expected.push(MUSICBUTLER.to_owned());
|
||||
expected.push(MUSICBUTLER_2.to_owned());
|
||||
assert_eq!(artist.meta.properties.get("MusicButler"), Some(&expected));
|
||||
assert_eq!(info.properties.get("MusicButler"), Some(&expected));
|
||||
|
||||
// Removing multiple URLs without clashes.
|
||||
artist
|
||||
.meta
|
||||
.remove_from_property("MusicButler", vec![MUSICBUTLER, MUSICBUTLER_2]);
|
||||
info.remove_from_property("MusicButler", vec![MUSICBUTLER, MUSICBUTLER_2]);
|
||||
expected.clear();
|
||||
assert!(artist.meta.properties.is_empty());
|
||||
assert!(info.properties.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn set_clear_musicbutler_urls() {
|
||||
let mut artist = Artist::new(ArtistId::new("an artist"));
|
||||
|
||||
let info = &mut artist.meta.info;
|
||||
let mut expected: Vec<String> = vec![];
|
||||
assert!(artist.meta.properties.is_empty());
|
||||
assert!(info.properties.is_empty());
|
||||
|
||||
// Set URLs.
|
||||
artist.meta.set_property("MusicButler", vec![MUSICBUTLER]);
|
||||
info.set_property("MusicButler", vec![MUSICBUTLER]);
|
||||
expected.push(MUSICBUTLER.to_owned());
|
||||
assert_eq!(artist.meta.properties.get("MusicButler"), Some(&expected));
|
||||
assert_eq!(info.properties.get("MusicButler"), Some(&expected));
|
||||
|
||||
artist.meta.set_property("MusicButler", vec![MUSICBUTLER_2]);
|
||||
info.set_property("MusicButler", vec![MUSICBUTLER_2]);
|
||||
expected.clear();
|
||||
expected.push(MUSICBUTLER_2.to_owned());
|
||||
assert_eq!(artist.meta.properties.get("MusicButler"), Some(&expected));
|
||||
assert_eq!(info.properties.get("MusicButler"), Some(&expected));
|
||||
|
||||
artist
|
||||
.meta
|
||||
.set_property("MusicButler", vec![MUSICBUTLER, MUSICBUTLER_2]);
|
||||
info.set_property("MusicButler", vec![MUSICBUTLER, MUSICBUTLER_2]);
|
||||
expected.clear();
|
||||
expected.push(MUSICBUTLER.to_owned());
|
||||
expected.push(MUSICBUTLER_2.to_owned());
|
||||
assert_eq!(artist.meta.properties.get("MusicButler"), Some(&expected));
|
||||
assert_eq!(info.properties.get("MusicButler"), Some(&expected));
|
||||
|
||||
// Clear URLs.
|
||||
artist.meta.clear_property("MusicButler");
|
||||
info.clear_property("MusicButler");
|
||||
expected.clear();
|
||||
assert!(artist.meta.properties.is_empty());
|
||||
assert!(info.properties.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -417,14 +405,15 @@ mod tests {
|
||||
let left = FULL_COLLECTION[0].to_owned();
|
||||
let mut right = FULL_COLLECTION[1].to_owned();
|
||||
right.meta.id = left.meta.id.clone();
|
||||
right.meta.musicbrainz = MbRefOption::None;
|
||||
right.meta.properties = HashMap::new();
|
||||
right.meta.info.musicbrainz = MbRefOption::None;
|
||||
right.meta.info.properties = HashMap::new();
|
||||
|
||||
let mut expected = left.clone();
|
||||
expected.meta.properties = expected
|
||||
expected.meta.info.properties = expected
|
||||
.meta
|
||||
.info
|
||||
.properties
|
||||
.merge(right.clone().meta.properties);
|
||||
.merge(right.clone().meta.info.properties);
|
||||
expected.albums.append(&mut right.albums.clone());
|
||||
expected.albums.sort_unstable();
|
||||
|
||||
@ -445,10 +434,11 @@ mod tests {
|
||||
left.albums.sort_unstable();
|
||||
|
||||
let mut expected = left.clone();
|
||||
expected.meta.properties = expected
|
||||
expected.meta.info.properties = expected
|
||||
.meta
|
||||
.info
|
||||
.properties
|
||||
.merge(right.clone().meta.properties);
|
||||
.merge(right.clone().meta.info.properties);
|
||||
expected.albums.append(&mut right.albums.clone());
|
||||
expected.albums.sort_unstable();
|
||||
expected.albums.dedup();
|
||||
|
@ -165,7 +165,7 @@ impl<Database: IDatabase, Library> IMusicHoardDatabase for MusicHoard<Database,
|
||||
mbid: MbRefOption<MbArtistRef>,
|
||||
) -> Result<(), Error> {
|
||||
self.update_artist(artist_id.as_ref(), |artist| {
|
||||
artist.meta.set_musicbrainz_ref(mbid)
|
||||
artist.meta.info.set_musicbrainz_ref(mbid)
|
||||
})
|
||||
}
|
||||
|
||||
@ -174,7 +174,7 @@ impl<Database: IDatabase, Library> IMusicHoardDatabase for MusicHoard<Database,
|
||||
artist_id: Id,
|
||||
) -> Result<(), Error> {
|
||||
self.update_artist(artist_id.as_ref(), |artist| {
|
||||
artist.meta.clear_musicbrainz_ref()
|
||||
artist.meta.info.clear_musicbrainz_ref()
|
||||
})
|
||||
}
|
||||
|
||||
@ -185,7 +185,7 @@ impl<Database: IDatabase, Library> IMusicHoardDatabase for MusicHoard<Database,
|
||||
values: Vec<S>,
|
||||
) -> Result<(), Error> {
|
||||
self.update_artist(artist_id.as_ref(), |artist| {
|
||||
artist.meta.add_to_property(property, values)
|
||||
artist.meta.info.add_to_property(property, values)
|
||||
})
|
||||
}
|
||||
|
||||
@ -196,7 +196,7 @@ impl<Database: IDatabase, Library> IMusicHoardDatabase for MusicHoard<Database,
|
||||
values: Vec<S>,
|
||||
) -> Result<(), Error> {
|
||||
self.update_artist(artist_id.as_ref(), |artist| {
|
||||
artist.meta.remove_from_property(property, values)
|
||||
artist.meta.info.remove_from_property(property, values)
|
||||
})
|
||||
}
|
||||
|
||||
@ -207,7 +207,7 @@ impl<Database: IDatabase, Library> IMusicHoardDatabase for MusicHoard<Database,
|
||||
values: Vec<S>,
|
||||
) -> Result<(), Error> {
|
||||
self.update_artist(artist_id.as_ref(), |artist| {
|
||||
artist.meta.set_property(property, values)
|
||||
artist.meta.info.set_property(property, values)
|
||||
})
|
||||
}
|
||||
|
||||
@ -217,7 +217,7 @@ impl<Database: IDatabase, Library> IMusicHoardDatabase for MusicHoard<Database,
|
||||
property: S,
|
||||
) -> Result<(), Error> {
|
||||
self.update_artist(artist_id.as_ref(), |artist| {
|
||||
artist.meta.clear_property(property)
|
||||
artist.meta.info.clear_property(property)
|
||||
})
|
||||
}
|
||||
|
||||
@ -533,7 +533,7 @@ mod tests {
|
||||
assert!(music_hoard.add_artist(artist_id.clone()).is_ok());
|
||||
|
||||
let mut expected: MbRefOption<MbArtistRef> = MbRefOption::None;
|
||||
assert_eq!(music_hoard.collection[0].meta.musicbrainz, expected);
|
||||
assert_eq!(music_hoard.collection[0].meta.info.musicbrainz, expected);
|
||||
|
||||
let mbref = MbRefOption::Some(MbArtistRef::from_uuid_str(MBID).unwrap());
|
||||
|
||||
@ -541,23 +541,23 @@ mod tests {
|
||||
assert!(music_hoard
|
||||
.set_artist_musicbrainz(&artist_id_2, mbref.clone())
|
||||
.is_err());
|
||||
assert_eq!(music_hoard.collection[0].meta.musicbrainz, expected);
|
||||
assert_eq!(music_hoard.collection[0].meta.info.musicbrainz, expected);
|
||||
|
||||
// Setting a URL on an artist.
|
||||
assert!(music_hoard
|
||||
.set_artist_musicbrainz(&artist_id, mbref.clone())
|
||||
.is_ok());
|
||||
expected.replace(MbArtistRef::from_uuid_str(MBID).unwrap());
|
||||
assert_eq!(music_hoard.collection[0].meta.musicbrainz, expected);
|
||||
assert_eq!(music_hoard.collection[0].meta.info.musicbrainz, expected);
|
||||
|
||||
// Clearing URLs on an artist that does not exist is an error.
|
||||
assert!(music_hoard.clear_artist_musicbrainz(&artist_id_2).is_err());
|
||||
assert_eq!(music_hoard.collection[0].meta.musicbrainz, expected);
|
||||
assert_eq!(music_hoard.collection[0].meta.info.musicbrainz, expected);
|
||||
|
||||
// Clearing URLs.
|
||||
assert!(music_hoard.clear_artist_musicbrainz(&artist_id).is_ok());
|
||||
expected.take();
|
||||
assert_eq!(music_hoard.collection[0].meta.musicbrainz, expected);
|
||||
assert_eq!(music_hoard.collection[0].meta.info.musicbrainz, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -573,13 +573,13 @@ mod tests {
|
||||
assert!(music_hoard.add_artist(artist_id.clone()).is_ok());
|
||||
|
||||
let mut expected: Vec<String> = vec![];
|
||||
assert!(music_hoard.collection[0].meta.properties.is_empty());
|
||||
assert!(music_hoard.collection[0].meta.info.properties.is_empty());
|
||||
|
||||
// Adding URLs to an artist not in the collection is an error.
|
||||
assert!(music_hoard
|
||||
.add_to_artist_property(&artist_id_2, "MusicButler", vec![MUSICBUTLER])
|
||||
.is_err());
|
||||
assert!(music_hoard.collection[0].meta.properties.is_empty());
|
||||
assert!(music_hoard.collection[0].meta.info.properties.is_empty());
|
||||
|
||||
// Adding mutliple URLs without clashes.
|
||||
assert!(music_hoard
|
||||
@ -587,19 +587,15 @@ mod tests {
|
||||
.is_ok());
|
||||
expected.push(MUSICBUTLER.to_owned());
|
||||
expected.push(MUSICBUTLER_2.to_owned());
|
||||
assert_eq!(
|
||||
music_hoard.collection[0].meta.properties.get("MusicButler"),
|
||||
Some(&expected)
|
||||
);
|
||||
let info = &music_hoard.collection[0].meta.info;
|
||||
assert_eq!(info.properties.get("MusicButler"), Some(&expected));
|
||||
|
||||
// Removing URLs from an artist not in the collection is an error.
|
||||
assert!(music_hoard
|
||||
.remove_from_artist_property(&artist_id_2, "MusicButler", vec![MUSICBUTLER])
|
||||
.is_err());
|
||||
assert_eq!(
|
||||
music_hoard.collection[0].meta.properties.get("MusicButler"),
|
||||
Some(&expected)
|
||||
);
|
||||
let info = &music_hoard.collection[0].meta.info;
|
||||
assert_eq!(info.properties.get("MusicButler"), Some(&expected));
|
||||
|
||||
// Removing multiple URLs without clashes.
|
||||
assert!(music_hoard
|
||||
@ -610,7 +606,7 @@ mod tests {
|
||||
)
|
||||
.is_ok());
|
||||
expected.clear();
|
||||
assert!(music_hoard.collection[0].meta.properties.is_empty());
|
||||
assert!(music_hoard.collection[0].meta.info.properties.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -626,13 +622,13 @@ mod tests {
|
||||
assert!(music_hoard.add_artist(artist_id.clone()).is_ok());
|
||||
|
||||
let mut expected: Vec<String> = vec![];
|
||||
assert!(music_hoard.collection[0].meta.properties.is_empty());
|
||||
assert!(music_hoard.collection[0].meta.info.properties.is_empty());
|
||||
|
||||
// Seting URL on an artist not in the collection is an error.
|
||||
assert!(music_hoard
|
||||
.set_artist_property(&artist_id_2, "MusicButler", vec![MUSICBUTLER])
|
||||
.is_err());
|
||||
assert!(music_hoard.collection[0].meta.properties.is_empty());
|
||||
assert!(music_hoard.collection[0].meta.info.properties.is_empty());
|
||||
|
||||
// Set URLs.
|
||||
assert!(music_hoard
|
||||
@ -641,10 +637,8 @@ mod tests {
|
||||
expected.clear();
|
||||
expected.push(MUSICBUTLER.to_owned());
|
||||
expected.push(MUSICBUTLER_2.to_owned());
|
||||
assert_eq!(
|
||||
music_hoard.collection[0].meta.properties.get("MusicButler"),
|
||||
Some(&expected)
|
||||
);
|
||||
let info = &music_hoard.collection[0].meta.info;
|
||||
assert_eq!(info.properties.get("MusicButler"), Some(&expected));
|
||||
|
||||
// Clearing URLs on an artist that does not exist is an error.
|
||||
assert!(music_hoard
|
||||
@ -656,7 +650,7 @@ mod tests {
|
||||
.clear_artist_property(&artist_id, "MusicButler")
|
||||
.is_ok());
|
||||
expected.clear();
|
||||
assert!(music_hoard.collection[0].meta.properties.is_empty());
|
||||
assert!(music_hoard.collection[0].meta.info.properties.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -3,7 +3,7 @@ use std::collections::HashMap;
|
||||
|
||||
use crate::core::collection::{
|
||||
album::{Album, AlbumId, AlbumMeta, AlbumPrimaryType, AlbumSeq},
|
||||
artist::{Artist, ArtistId, ArtistMeta},
|
||||
artist::{Artist, ArtistId, ArtistInfo, ArtistMeta},
|
||||
musicbrainz::{MbAlbumRef, MbArtistRef, MbRefOption},
|
||||
track::{Track, TrackFormat, TrackId, TrackNum, TrackQuality},
|
||||
};
|
||||
|
8
src/external/database/serde/deserialize.rs
vendored
8
src/external/database/serde/deserialize.rs
vendored
@ -5,7 +5,7 @@ use serde::{de::Visitor, Deserialize, Deserializer};
|
||||
use crate::{
|
||||
collection::{
|
||||
album::AlbumMeta,
|
||||
artist::ArtistMeta,
|
||||
artist::{ArtistInfo, ArtistMeta},
|
||||
musicbrainz::{MbAlbumRef, MbArtistRef, MbRefOption, Mbid},
|
||||
},
|
||||
core::collection::{
|
||||
@ -115,8 +115,10 @@ impl From<DeserializeArtist> for Artist {
|
||||
meta: ArtistMeta {
|
||||
id: ArtistId::new(artist.name),
|
||||
sort: artist.sort,
|
||||
musicbrainz: artist.musicbrainz.into(),
|
||||
properties: artist.properties,
|
||||
info: ArtistInfo {
|
||||
musicbrainz: artist.musicbrainz.into(),
|
||||
properties: artist.properties,
|
||||
},
|
||||
},
|
||||
albums: artist.albums.into_iter().map(Into::into).collect(),
|
||||
}
|
||||
|
3
src/external/database/serde/serialize.rs
vendored
3
src/external/database/serde/serialize.rs
vendored
@ -73,9 +73,10 @@ impl<'a> From<&'a Artist> for SerializeArtist<'a> {
|
||||
SerializeArtist {
|
||||
name: &artist.meta.id.name,
|
||||
sort: artist.meta.sort.as_deref(),
|
||||
musicbrainz: (&artist.meta.musicbrainz).into(),
|
||||
musicbrainz: (&artist.meta.info.musicbrainz).into(),
|
||||
properties: artist
|
||||
.meta
|
||||
.info
|
||||
.properties
|
||||
.iter()
|
||||
.map(|(k, v)| (k.as_ref(), v))
|
||||
|
6
src/external/musicbrainz/api/mod.rs
vendored
6
src/external/musicbrainz/api/mod.rs
vendored
@ -79,8 +79,8 @@ impl From<SerdeMbArtistMeta> for MbArtistMeta {
|
||||
fn from(value: SerdeMbArtistMeta) -> Self {
|
||||
MbArtistMeta {
|
||||
id: value.id.into(),
|
||||
name: value.name.into(),
|
||||
sort_name: value.sort_name.into(),
|
||||
name: value.name,
|
||||
sort_name: value.sort_name,
|
||||
disambiguation: value.disambiguation,
|
||||
}
|
||||
}
|
||||
@ -109,7 +109,7 @@ impl From<SerdeMbReleaseGroupMeta> for MbReleaseGroupMeta {
|
||||
fn from(value: SerdeMbReleaseGroupMeta) -> Self {
|
||||
MbReleaseGroupMeta {
|
||||
id: value.id.into(),
|
||||
title: value.title.into(),
|
||||
title: value.title,
|
||||
first_release_date: value.first_release_date.into(),
|
||||
primary_type: value.primary_type.into(),
|
||||
secondary_types: value
|
||||
|
@ -7,19 +7,21 @@ macro_rules! full_collection {
|
||||
name: "Album_Artist ‘A’".to_string(),
|
||||
},
|
||||
sort: None,
|
||||
musicbrainz: MbRefOption::Some(MbArtistRef::from_url_str(
|
||||
"https://musicbrainz.org/artist/00000000-0000-0000-0000-000000000000"
|
||||
).unwrap()),
|
||||
properties: HashMap::from([
|
||||
(String::from("MusicButler"), vec![
|
||||
String::from("https://www.musicbutler.io/artist-page/000000000"),
|
||||
info: ArtistInfo {
|
||||
musicbrainz: MbRefOption::Some(MbArtistRef::from_url_str(
|
||||
"https://musicbrainz.org/artist/00000000-0000-0000-0000-000000000000"
|
||||
).unwrap()),
|
||||
properties: HashMap::from([
|
||||
(String::from("MusicButler"), vec![
|
||||
String::from("https://www.musicbutler.io/artist-page/000000000"),
|
||||
]),
|
||||
(String::from("Qobuz"), vec![
|
||||
String::from(
|
||||
"https://www.qobuz.com/nl-nl/interpreter/artist-a/download-streaming-albums",
|
||||
)
|
||||
]),
|
||||
]),
|
||||
(String::from("Qobuz"), vec![
|
||||
String::from(
|
||||
"https://www.qobuz.com/nl-nl/interpreter/artist-a/download-streaming-albums",
|
||||
)
|
||||
]),
|
||||
]),
|
||||
},
|
||||
},
|
||||
albums: vec![
|
||||
Album {
|
||||
@ -129,23 +131,25 @@ macro_rules! full_collection {
|
||||
name: "Album_Artist ‘B’".to_string(),
|
||||
},
|
||||
sort: None,
|
||||
musicbrainz: MbRefOption::Some(MbArtistRef::from_url_str(
|
||||
"https://musicbrainz.org/artist/11111111-1111-1111-1111-111111111111"
|
||||
).unwrap()),
|
||||
properties: HashMap::from([
|
||||
(String::from("MusicButler"), vec![
|
||||
String::from("https://www.musicbutler.io/artist-page/111111111"),
|
||||
String::from("https://www.musicbutler.io/artist-page/111111112"),
|
||||
info: ArtistInfo {
|
||||
musicbrainz: MbRefOption::Some(MbArtistRef::from_url_str(
|
||||
"https://musicbrainz.org/artist/11111111-1111-1111-1111-111111111111"
|
||||
).unwrap()),
|
||||
properties: HashMap::from([
|
||||
(String::from("MusicButler"), vec![
|
||||
String::from("https://www.musicbutler.io/artist-page/111111111"),
|
||||
String::from("https://www.musicbutler.io/artist-page/111111112"),
|
||||
]),
|
||||
(String::from("Bandcamp"), vec![
|
||||
String::from("https://artist-b.bandcamp.com/")
|
||||
]),
|
||||
(String::from("Qobuz"), vec![
|
||||
String::from(
|
||||
"https://www.qobuz.com/nl-nl/interpreter/artist-b/download-streaming-albums",
|
||||
)
|
||||
]),
|
||||
]),
|
||||
(String::from("Bandcamp"), vec![
|
||||
String::from("https://artist-b.bandcamp.com/")
|
||||
]),
|
||||
(String::from("Qobuz"), vec![
|
||||
String::from(
|
||||
"https://www.qobuz.com/nl-nl/interpreter/artist-b/download-streaming-albums",
|
||||
)
|
||||
]),
|
||||
]),
|
||||
},
|
||||
},
|
||||
albums: vec![
|
||||
Album {
|
||||
@ -316,8 +320,10 @@ macro_rules! full_collection {
|
||||
name: "The Album_Artist ‘C’".to_string(),
|
||||
},
|
||||
sort: Some("Album_Artist ‘C’, The".to_string()),
|
||||
musicbrainz: MbRefOption::CannotHaveMbid,
|
||||
properties: HashMap::new(),
|
||||
info: ArtistInfo {
|
||||
musicbrainz: MbRefOption::CannotHaveMbid,
|
||||
properties: HashMap::new(),
|
||||
},
|
||||
},
|
||||
albums: vec![
|
||||
Album {
|
||||
@ -406,8 +412,10 @@ macro_rules! full_collection {
|
||||
name: "Album_Artist ‘D’".to_string(),
|
||||
},
|
||||
sort: None,
|
||||
musicbrainz: MbRefOption::None,
|
||||
properties: HashMap::new(),
|
||||
info: ArtistInfo {
|
||||
musicbrainz: MbRefOption::None,
|
||||
properties: HashMap::new(),
|
||||
},
|
||||
},
|
||||
albums: vec![
|
||||
Album {
|
||||
|
@ -8,8 +8,10 @@ macro_rules! library_collection {
|
||||
name: "Album_Artist ‘A’".to_string(),
|
||||
},
|
||||
sort: None,
|
||||
musicbrainz: MbRefOption::None,
|
||||
properties: HashMap::new(),
|
||||
info: ArtistInfo {
|
||||
musicbrainz: MbRefOption::None,
|
||||
properties: HashMap::new(),
|
||||
},
|
||||
},
|
||||
albums: vec![
|
||||
Album {
|
||||
@ -117,8 +119,10 @@ macro_rules! library_collection {
|
||||
name: "Album_Artist ‘B’".to_string(),
|
||||
},
|
||||
sort: None,
|
||||
musicbrainz: MbRefOption::None,
|
||||
properties: HashMap::new(),
|
||||
info: ArtistInfo {
|
||||
musicbrainz: MbRefOption::None,
|
||||
properties: HashMap::new(),
|
||||
},
|
||||
},
|
||||
albums: vec![
|
||||
Album {
|
||||
@ -285,8 +289,10 @@ macro_rules! library_collection {
|
||||
name: "The Album_Artist ‘C’".to_string(),
|
||||
},
|
||||
sort: Some("Album_Artist ‘C’, The".to_string()),
|
||||
musicbrainz: MbRefOption::None,
|
||||
properties: HashMap::new(),
|
||||
info: ArtistInfo {
|
||||
musicbrainz: MbRefOption::None,
|
||||
properties: HashMap::new(),
|
||||
},
|
||||
},
|
||||
albums: vec![
|
||||
Album {
|
||||
@ -375,8 +381,10 @@ macro_rules! library_collection {
|
||||
name: "Album_Artist ‘D’".to_string(),
|
||||
},
|
||||
sort: None,
|
||||
musicbrainz: MbRefOption::None,
|
||||
properties: HashMap::new(),
|
||||
info: ArtistInfo {
|
||||
musicbrainz: MbRefOption::None,
|
||||
properties: HashMap::new(),
|
||||
},
|
||||
},
|
||||
albums: vec![
|
||||
Album {
|
||||
|
@ -156,7 +156,7 @@ impl AppMachine<FetchState> {
|
||||
result_sender: ResultSender,
|
||||
artist: &Artist,
|
||||
) -> Result<(), FetchError> {
|
||||
let requests = match artist.meta.musicbrainz {
|
||||
let requests = match artist.meta.info.musicbrainz {
|
||||
MbRefOption::Some(ref arid) => {
|
||||
let arid = arid.mbid();
|
||||
let albums = artist.albums.iter();
|
||||
|
@ -27,7 +27,7 @@ macro_rules! item_option_artist_set {
|
||||
meta: &ArtistMeta,
|
||||
) -> Result<(), musichoard::Error> {
|
||||
let mbref = match self {
|
||||
MatchOption::Some(m) => m.item.musicbrainz,
|
||||
MatchOption::Some(m) => m.item.info.musicbrainz,
|
||||
MatchOption::CannotHaveMbid => MbRefOption::CannotHaveMbid,
|
||||
MatchOption::ManualInputMbid => panic!(),
|
||||
};
|
||||
|
@ -182,7 +182,7 @@ impl IAppInteractSearchPrivate for AppMachine<SearchState> {
|
||||
|
||||
if let Some(ref probe_sort) = probe.meta.sort {
|
||||
if !result {
|
||||
let name = Self::normalize_search(&probe_sort, !case_sens, !char_sens);
|
||||
let name = Self::normalize_search(probe_sort, !case_sens, !char_sens);
|
||||
result = name.starts_with(search);
|
||||
}
|
||||
}
|
||||
|
14
src/tui/lib/external/musicbrainz/api/mod.rs
vendored
14
src/tui/lib/external/musicbrainz/api/mod.rs
vendored
@ -5,7 +5,7 @@ use std::collections::HashMap;
|
||||
use musichoard::{
|
||||
collection::{
|
||||
album::{AlbumDate, AlbumMeta, AlbumSeq},
|
||||
artist::ArtistMeta,
|
||||
artist::{ArtistInfo, ArtistMeta},
|
||||
musicbrainz::{MbRefOption, Mbid},
|
||||
},
|
||||
external::musicbrainz::{
|
||||
@ -98,8 +98,10 @@ fn from_lookup_artist_response(entity: LookupArtistResponse) -> Lookup<ArtistMet
|
||||
item: ArtistMeta {
|
||||
id: entity.meta.name.into(),
|
||||
sort,
|
||||
musicbrainz: MbRefOption::Some(entity.meta.id.into()),
|
||||
properties: HashMap::new(),
|
||||
info: ArtistInfo {
|
||||
musicbrainz: MbRefOption::Some(entity.meta.id.into()),
|
||||
properties: HashMap::new(),
|
||||
},
|
||||
},
|
||||
disambiguation: entity.meta.disambiguation,
|
||||
}
|
||||
@ -126,8 +128,10 @@ fn from_search_artist_response_artist(entity: SearchArtistResponseArtist) -> Mat
|
||||
item: ArtistMeta {
|
||||
id: entity.meta.name.into(),
|
||||
sort,
|
||||
musicbrainz: MbRefOption::Some(entity.meta.id.into()),
|
||||
properties: HashMap::new(),
|
||||
info: ArtistInfo {
|
||||
musicbrainz: MbRefOption::Some(entity.meta.id.into()),
|
||||
properties: HashMap::new(),
|
||||
},
|
||||
},
|
||||
disambiguation: entity.meta.disambiguation,
|
||||
}
|
||||
|
@ -419,7 +419,7 @@ mod tests {
|
||||
}
|
||||
|
||||
fn search_albums_requests() -> VecDeque<MbParams> {
|
||||
let mbref = mb_ref_opt_as_ref(&COLLECTION[1].meta.musicbrainz);
|
||||
let mbref = mb_ref_opt_as_ref(&COLLECTION[1].meta.info.musicbrainz);
|
||||
let arid = mb_ref_opt_unwrap(mbref).mbid().clone();
|
||||
|
||||
let artist_id = COLLECTION[1].meta.id.clone();
|
||||
@ -437,7 +437,7 @@ mod tests {
|
||||
}
|
||||
|
||||
fn album_arid_expectation() -> Mbid {
|
||||
let mbref = mb_ref_opt_as_ref(&COLLECTION[1].meta.musicbrainz);
|
||||
let mbref = mb_ref_opt_as_ref(&COLLECTION[1].meta.info.musicbrainz);
|
||||
mb_ref_opt_unwrap(mbref).mbid().clone()
|
||||
}
|
||||
|
||||
|
@ -2,7 +2,7 @@ use std::collections::HashMap;
|
||||
|
||||
use musichoard::collection::{
|
||||
album::{Album, AlbumId, AlbumMeta, AlbumPrimaryType, AlbumSeq},
|
||||
artist::{Artist, ArtistId, ArtistMeta},
|
||||
artist::{Artist, ArtistId, ArtistInfo, ArtistMeta},
|
||||
musicbrainz::{MbAlbumRef, MbArtistRef, MbRefOption},
|
||||
track::{Track, TrackFormat, TrackId, TrackNum, TrackQuality},
|
||||
};
|
||||
|
@ -76,10 +76,10 @@ impl<'a> ArtistOverlay<'a> {
|
||||
Properties: {}",
|
||||
artist.map(|a| a.meta.id.name.as_str()).unwrap_or(""),
|
||||
artist
|
||||
.map(|a| UiDisplay::display_mb_ref_option_as_url(&a.meta.musicbrainz))
|
||||
.map(|a| UiDisplay::display_mb_ref_option_as_url(&a.meta.info.musicbrainz))
|
||||
.unwrap_or_default(),
|
||||
Self::opt_hashmap_to_string(
|
||||
artist.map(|a| &a.meta.properties),
|
||||
artist.map(|a| &a.meta.info.properties),
|
||||
&double_item_indent,
|
||||
&double_list_indent
|
||||
),
|
||||
|
118
tests/testlib.rs
118
tests/testlib.rs
@ -3,7 +3,7 @@ use std::collections::HashMap;
|
||||
|
||||
use musichoard::collection::{
|
||||
album::{Album, AlbumId, AlbumMeta, AlbumPrimaryType, AlbumSecondaryType, AlbumSeq},
|
||||
artist::{Artist, ArtistId, ArtistMeta},
|
||||
artist::{Artist, ArtistId, ArtistInfo, ArtistMeta},
|
||||
musicbrainz::{MbArtistRef, MbRefOption},
|
||||
track::{Track, TrackFormat, TrackId, TrackNum, TrackQuality},
|
||||
Collection,
|
||||
@ -17,20 +17,22 @@ pub static COLLECTION: Lazy<Vec<Artist>> = Lazy::new(|| -> Collection {
|
||||
name: String::from("Аркона"),
|
||||
},
|
||||
sort: Some(String::from("Arkona")),
|
||||
musicbrainz: MbRefOption::Some(MbArtistRef::from_url_str(
|
||||
"https://musicbrainz.org/artist/baad262d-55ef-427a-83c7-f7530964f212"
|
||||
).unwrap()),
|
||||
properties: HashMap::from([
|
||||
(String::from("MusicButler"), vec![
|
||||
String::from("https://www.musicbutler.io/artist-page/283448581"),
|
||||
info: ArtistInfo {
|
||||
musicbrainz: MbRefOption::Some(MbArtistRef::from_url_str(
|
||||
"https://musicbrainz.org/artist/baad262d-55ef-427a-83c7-f7530964f212"
|
||||
).unwrap()),
|
||||
properties: HashMap::from([
|
||||
(String::from("MusicButler"), vec![
|
||||
String::from("https://www.musicbutler.io/artist-page/283448581"),
|
||||
]),
|
||||
(String::from("Bandcamp"), vec![
|
||||
String::from("https://arkonamoscow.bandcamp.com/"),
|
||||
]),
|
||||
(String::from("Qobuz"), vec![String::from(
|
||||
"https://www.qobuz.com/nl-nl/interpreter/arkona/download-streaming-albums",
|
||||
)]),
|
||||
]),
|
||||
(String::from("Bandcamp"), vec![
|
||||
String::from("https://arkonamoscow.bandcamp.com/"),
|
||||
]),
|
||||
(String::from("Qobuz"), vec![String::from(
|
||||
"https://www.qobuz.com/nl-nl/interpreter/arkona/download-streaming-albums",
|
||||
)]),
|
||||
]),
|
||||
},
|
||||
},
|
||||
albums: vec![Album {
|
||||
meta: AlbumMeta {
|
||||
@ -207,17 +209,19 @@ pub static COLLECTION: Lazy<Vec<Artist>> = Lazy::new(|| -> Collection {
|
||||
name: String::from("Eluveitie"),
|
||||
},
|
||||
sort: None,
|
||||
musicbrainz: MbRefOption::Some(MbArtistRef::from_url_str(
|
||||
"https://musicbrainz.org/artist/8000598a-5edb-401c-8e6d-36b167feaf38"
|
||||
).unwrap()),
|
||||
properties: HashMap::from([
|
||||
(String::from("MusicButler"), vec![
|
||||
String::from("https://www.musicbutler.io/artist-page/269358403"),
|
||||
info: ArtistInfo {
|
||||
musicbrainz: MbRefOption::Some(MbArtistRef::from_url_str(
|
||||
"https://musicbrainz.org/artist/8000598a-5edb-401c-8e6d-36b167feaf38"
|
||||
).unwrap()),
|
||||
properties: HashMap::from([
|
||||
(String::from("MusicButler"), vec![
|
||||
String::from("https://www.musicbutler.io/artist-page/269358403"),
|
||||
]),
|
||||
(String::from("Qobuz"), vec![String::from(
|
||||
"https://www.qobuz.com/nl-nl/interpreter/eluveitie/download-streaming-albums",
|
||||
)]),
|
||||
]),
|
||||
(String::from("Qobuz"), vec![String::from(
|
||||
"https://www.qobuz.com/nl-nl/interpreter/eluveitie/download-streaming-albums",
|
||||
)]),
|
||||
]),
|
||||
},
|
||||
},
|
||||
albums: vec![
|
||||
Album {
|
||||
@ -454,17 +458,19 @@ pub static COLLECTION: Lazy<Vec<Artist>> = Lazy::new(|| -> Collection {
|
||||
name: String::from("Frontside"),
|
||||
},
|
||||
sort: None,
|
||||
musicbrainz: MbRefOption::Some(MbArtistRef::from_url_str(
|
||||
"https://musicbrainz.org/artist/3a901353-fccd-4afd-ad01-9c03f451b490"
|
||||
).unwrap()),
|
||||
properties: HashMap::from([
|
||||
(String::from("MusicButler"), vec![
|
||||
String::from("https://www.musicbutler.io/artist-page/826588800"),
|
||||
info: ArtistInfo {
|
||||
musicbrainz: MbRefOption::Some(MbArtistRef::from_url_str(
|
||||
"https://musicbrainz.org/artist/3a901353-fccd-4afd-ad01-9c03f451b490"
|
||||
).unwrap()),
|
||||
properties: HashMap::from([
|
||||
(String::from("MusicButler"), vec![
|
||||
String::from("https://www.musicbutler.io/artist-page/826588800"),
|
||||
]),
|
||||
(String::from("Qobuz"), vec![String::from(
|
||||
"https://www.qobuz.com/nl-nl/interpreter/frontside/download-streaming-albums",
|
||||
)]),
|
||||
]),
|
||||
(String::from("Qobuz"), vec![String::from(
|
||||
"https://www.qobuz.com/nl-nl/interpreter/frontside/download-streaming-albums",
|
||||
)]),
|
||||
]),
|
||||
},
|
||||
},
|
||||
albums: vec![Album {
|
||||
meta: AlbumMeta {
|
||||
@ -608,17 +614,19 @@ pub static COLLECTION: Lazy<Vec<Artist>> = Lazy::new(|| -> Collection {
|
||||
name: String::from("Heaven’s Basement"),
|
||||
},
|
||||
sort: Some(String::from("Heaven’s Basement")),
|
||||
musicbrainz: MbRefOption::Some(MbArtistRef::from_url_str(
|
||||
"https://musicbrainz.org/artist/c2c4d56a-d599-4a18-bd2f-ae644e2198cc"
|
||||
).unwrap()),
|
||||
properties: HashMap::from([
|
||||
(String::from("MusicButler"), vec![
|
||||
String::from("https://www.musicbutler.io/artist-page/291158685"),
|
||||
info: ArtistInfo {
|
||||
musicbrainz: MbRefOption::Some(MbArtistRef::from_url_str(
|
||||
"https://musicbrainz.org/artist/c2c4d56a-d599-4a18-bd2f-ae644e2198cc"
|
||||
).unwrap()),
|
||||
properties: HashMap::from([
|
||||
(String::from("MusicButler"), vec![
|
||||
String::from("https://www.musicbutler.io/artist-page/291158685"),
|
||||
]),
|
||||
(String::from("Qobuz"), vec![String::from(
|
||||
"https://www.qobuz.com/nl-nl/interpreter/heaven-s-basement/download-streaming-albums",
|
||||
)]),
|
||||
]),
|
||||
(String::from("Qobuz"), vec![String::from(
|
||||
"https://www.qobuz.com/nl-nl/interpreter/heaven-s-basement/download-streaming-albums",
|
||||
)]),
|
||||
]),
|
||||
},
|
||||
},
|
||||
albums: vec![Album {
|
||||
meta: AlbumMeta {
|
||||
@ -742,17 +750,19 @@ pub static COLLECTION: Lazy<Vec<Artist>> = Lazy::new(|| -> Collection {
|
||||
name: String::from("Metallica"),
|
||||
},
|
||||
sort: None,
|
||||
musicbrainz: MbRefOption::Some(MbArtistRef::from_url_str(
|
||||
"https://musicbrainz.org/artist/65f4f0c5-ef9e-490c-aee3-909e7ae6b2ab"
|
||||
).unwrap()),
|
||||
properties: HashMap::from([
|
||||
(String::from("MusicButler"), vec![
|
||||
String::from("https://www.musicbutler.io/artist-page/3996865"),
|
||||
info: ArtistInfo {
|
||||
musicbrainz: MbRefOption::Some(MbArtistRef::from_url_str(
|
||||
"https://musicbrainz.org/artist/65f4f0c5-ef9e-490c-aee3-909e7ae6b2ab"
|
||||
).unwrap()),
|
||||
properties: HashMap::from([
|
||||
(String::from("MusicButler"), vec![
|
||||
String::from("https://www.musicbutler.io/artist-page/3996865"),
|
||||
]),
|
||||
(String::from("Qobuz"), vec![String::from(
|
||||
"https://www.qobuz.com/nl-nl/interpreter/metallica/download-streaming-albums",
|
||||
)]),
|
||||
]),
|
||||
(String::from("Qobuz"), vec![String::from(
|
||||
"https://www.qobuz.com/nl-nl/interpreter/metallica/download-streaming-albums",
|
||||
)]),
|
||||
]),
|
||||
},
|
||||
},
|
||||
albums: vec![
|
||||
Album {
|
||||
|
Loading…
Reference in New Issue
Block a user