Separate metadata from collections (#209)
Part 1 of #187 Reviewed-on: #209
This commit is contained in:
parent
ebd63cc80b
commit
fd9d3677ec
@ -12,20 +12,26 @@ use crate::core::collection::{
|
|||||||
/// An album is a collection of tracks that were released together.
|
/// An album is a collection of tracks that were released together.
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
pub struct Album {
|
pub struct Album {
|
||||||
|
pub meta: AlbumMeta,
|
||||||
|
pub tracks: Vec<Track>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Album metadata.
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
|
pub struct AlbumMeta {
|
||||||
pub id: AlbumId,
|
pub id: AlbumId,
|
||||||
pub date: AlbumDate,
|
pub date: AlbumDate,
|
||||||
pub seq: AlbumSeq,
|
pub seq: AlbumSeq,
|
||||||
pub musicbrainz: Option<MbAlbumRef>,
|
pub musicbrainz: Option<MbAlbumRef>,
|
||||||
pub primary_type: Option<AlbumPrimaryType>,
|
pub primary_type: Option<AlbumPrimaryType>,
|
||||||
pub secondary_types: Vec<AlbumSecondaryType>,
|
pub secondary_types: Vec<AlbumSecondaryType>,
|
||||||
pub tracks: Vec<Track>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WithId for Album {
|
impl WithId for Album {
|
||||||
type Id = AlbumId;
|
type Id = AlbumId;
|
||||||
|
|
||||||
fn id(&self) -> &Self::Id {
|
fn id(&self) -> &Self::Id {
|
||||||
&self.id
|
&self.meta.id
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -139,13 +145,50 @@ impl Album {
|
|||||||
secondary_types: Vec<AlbumSecondaryType>,
|
secondary_types: Vec<AlbumSecondaryType>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Album {
|
Album {
|
||||||
|
meta: AlbumMeta::new(id, date, primary_type, secondary_types),
|
||||||
|
tracks: vec![],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_status(&self) -> AlbumStatus {
|
||||||
|
AlbumStatus::from_tracks(&self.tracks)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialOrd for Album {
|
||||||
|
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||||
|
Some(self.cmp(other))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Ord for Album {
|
||||||
|
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
||||||
|
self.meta.cmp(&other.meta)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Merge for Album {
|
||||||
|
fn merge_in_place(&mut self, other: Self) {
|
||||||
|
self.meta.merge_in_place(other.meta);
|
||||||
|
let tracks = mem::take(&mut self.tracks);
|
||||||
|
self.tracks = MergeSorted::new(tracks.into_iter(), other.tracks.into_iter()).collect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AlbumMeta {
|
||||||
|
pub fn new<Id: Into<AlbumId>, Date: Into<AlbumDate>>(
|
||||||
|
id: Id,
|
||||||
|
date: Date,
|
||||||
|
primary_type: Option<AlbumPrimaryType>,
|
||||||
|
secondary_types: Vec<AlbumSecondaryType>,
|
||||||
|
) -> Self {
|
||||||
|
AlbumMeta {
|
||||||
id: id.into(),
|
id: id.into(),
|
||||||
date: date.into(),
|
date: date.into(),
|
||||||
seq: AlbumSeq::default(),
|
seq: AlbumSeq::default(),
|
||||||
musicbrainz: None,
|
musicbrainz: None,
|
||||||
primary_type,
|
primary_type,
|
||||||
secondary_types,
|
secondary_types,
|
||||||
tracks: vec![],
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -153,10 +196,6 @@ impl Album {
|
|||||||
(&self.date, &self.seq, &self.id)
|
(&self.date, &self.seq, &self.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_status(&self) -> AlbumStatus {
|
|
||||||
AlbumStatus::from_tracks(&self.tracks)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_seq(&mut self, seq: AlbumSeq) {
|
pub fn set_seq(&mut self, seq: AlbumSeq) {
|
||||||
self.seq = seq;
|
self.seq = seq;
|
||||||
}
|
}
|
||||||
@ -174,19 +213,19 @@ impl Album {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PartialOrd for Album {
|
impl PartialOrd for AlbumMeta {
|
||||||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||||
Some(self.cmp(other))
|
Some(self.cmp(other))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Ord for Album {
|
impl Ord for AlbumMeta {
|
||||||
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
||||||
self.get_sort_key().cmp(&other.get_sort_key())
|
self.get_sort_key().cmp(&other.get_sort_key())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Merge for Album {
|
impl Merge for AlbumMeta {
|
||||||
fn merge_in_place(&mut self, other: Self) {
|
fn merge_in_place(&mut self, other: Self) {
|
||||||
assert_eq!(self.id, other.id);
|
assert_eq!(self.id, other.id);
|
||||||
self.seq = std::cmp::max(self.seq, other.seq);
|
self.seq = std::cmp::max(self.seq, other.seq);
|
||||||
@ -194,9 +233,6 @@ impl Merge for Album {
|
|||||||
self.musicbrainz = self.musicbrainz.take().or(other.musicbrainz);
|
self.musicbrainz = self.musicbrainz.take().or(other.musicbrainz);
|
||||||
self.primary_type = self.primary_type.take().or(other.primary_type);
|
self.primary_type = self.primary_type.take().or(other.primary_type);
|
||||||
self.secondary_types.merge_in_place(other.secondary_types);
|
self.secondary_types.merge_in_place(other.secondary_types);
|
||||||
|
|
||||||
let tracks = mem::take(&mut self.tracks);
|
|
||||||
self.tracks = MergeSorted::new(tracks.into_iter(), other.tracks.into_iter()).collect();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -250,44 +286,45 @@ mod tests {
|
|||||||
title: String::from("album z"),
|
title: String::from("album z"),
|
||||||
};
|
};
|
||||||
let mut album_1 = Album::new(album_id_1, date.clone(), None, vec![]);
|
let mut album_1 = Album::new(album_id_1, date.clone(), None, vec![]);
|
||||||
album_1.set_seq(AlbumSeq(1));
|
album_1.meta.set_seq(AlbumSeq(1));
|
||||||
|
|
||||||
let album_id_2 = AlbumId {
|
let album_id_2 = AlbumId {
|
||||||
title: String::from("album a"),
|
title: String::from("album a"),
|
||||||
};
|
};
|
||||||
let mut album_2 = Album::new(album_id_2, date.clone(), None, vec![]);
|
let mut album_2 = Album::new(album_id_2, date.clone(), None, vec![]);
|
||||||
album_2.set_seq(AlbumSeq(2));
|
album_2.meta.set_seq(AlbumSeq(2));
|
||||||
|
|
||||||
assert_ne!(album_1, album_2);
|
assert_ne!(album_1, album_2);
|
||||||
assert!(album_1 < album_2);
|
assert!(album_1 < album_2);
|
||||||
|
assert!(album_1.meta < album_2.meta);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn set_clear_seq() {
|
fn set_clear_seq() {
|
||||||
let mut album = Album::new("An album", AlbumDate::default(), None, vec![]);
|
let mut album = Album::new("An album", AlbumDate::default(), None, vec![]);
|
||||||
|
|
||||||
assert_eq!(album.seq, AlbumSeq(0));
|
assert_eq!(album.meta.seq, AlbumSeq(0));
|
||||||
|
|
||||||
// Setting a seq on an album.
|
// Setting a seq on an album.
|
||||||
album.set_seq(AlbumSeq(6));
|
album.meta.set_seq(AlbumSeq(6));
|
||||||
assert_eq!(album.seq, AlbumSeq(6));
|
assert_eq!(album.meta.seq, AlbumSeq(6));
|
||||||
|
|
||||||
album.set_seq(AlbumSeq(6));
|
album.meta.set_seq(AlbumSeq(6));
|
||||||
assert_eq!(album.seq, AlbumSeq(6));
|
assert_eq!(album.meta.seq, AlbumSeq(6));
|
||||||
|
|
||||||
album.set_seq(AlbumSeq(8));
|
album.meta.set_seq(AlbumSeq(8));
|
||||||
assert_eq!(album.seq, AlbumSeq(8));
|
assert_eq!(album.meta.seq, AlbumSeq(8));
|
||||||
|
|
||||||
// Clearing seq.
|
// Clearing seq.
|
||||||
album.clear_seq();
|
album.meta.clear_seq();
|
||||||
assert_eq!(album.seq, AlbumSeq(0));
|
assert_eq!(album.meta.seq, AlbumSeq(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn merge_album_no_overlap() {
|
fn merge_album_no_overlap() {
|
||||||
let left = FULL_COLLECTION[0].albums[0].to_owned();
|
let left = FULL_COLLECTION[0].albums[0].to_owned();
|
||||||
let mut right = FULL_COLLECTION[0].albums[1].to_owned();
|
let mut right = FULL_COLLECTION[0].albums[1].to_owned();
|
||||||
right.id = left.id.clone();
|
right.meta.id = left.meta.id.clone();
|
||||||
|
|
||||||
let mut expected = left.clone();
|
let mut expected = left.clone();
|
||||||
expected.tracks.append(&mut right.tracks.clone());
|
expected.tracks.append(&mut right.tracks.clone());
|
||||||
@ -305,7 +342,7 @@ mod tests {
|
|||||||
fn merge_album_overlap() {
|
fn merge_album_overlap() {
|
||||||
let mut left = FULL_COLLECTION[0].albums[0].to_owned();
|
let mut left = FULL_COLLECTION[0].albums[0].to_owned();
|
||||||
let mut right = FULL_COLLECTION[0].albums[1].to_owned();
|
let mut right = FULL_COLLECTION[0].albums[1].to_owned();
|
||||||
right.id = left.id.clone();
|
right.meta.id = left.meta.id.clone();
|
||||||
left.tracks.push(right.tracks[0].clone());
|
left.tracks.push(right.tracks[0].clone());
|
||||||
left.tracks.sort_unstable();
|
left.tracks.sort_unstable();
|
||||||
|
|
||||||
@ -328,23 +365,29 @@ mod tests {
|
|||||||
let mut album = Album::new(AlbumId::new("an album"), AlbumDate::default(), None, vec![]);
|
let mut album = Album::new(AlbumId::new("an album"), AlbumDate::default(), None, vec![]);
|
||||||
|
|
||||||
let mut expected: Option<MbAlbumRef> = None;
|
let mut expected: Option<MbAlbumRef> = None;
|
||||||
assert_eq!(album.musicbrainz, expected);
|
assert_eq!(album.meta.musicbrainz, expected);
|
||||||
|
|
||||||
// Setting a URL on an album.
|
// Setting a URL on an album.
|
||||||
album.set_musicbrainz_ref(MbAlbumRef::from_url_str(MUSICBRAINZ).unwrap());
|
album
|
||||||
|
.meta
|
||||||
|
.set_musicbrainz_ref(MbAlbumRef::from_url_str(MUSICBRAINZ).unwrap());
|
||||||
_ = expected.insert(MbAlbumRef::from_url_str(MUSICBRAINZ).unwrap());
|
_ = expected.insert(MbAlbumRef::from_url_str(MUSICBRAINZ).unwrap());
|
||||||
assert_eq!(album.musicbrainz, expected);
|
assert_eq!(album.meta.musicbrainz, expected);
|
||||||
|
|
||||||
album.set_musicbrainz_ref(MbAlbumRef::from_url_str(MUSICBRAINZ).unwrap());
|
album
|
||||||
assert_eq!(album.musicbrainz, expected);
|
.meta
|
||||||
|
.set_musicbrainz_ref(MbAlbumRef::from_url_str(MUSICBRAINZ).unwrap());
|
||||||
|
assert_eq!(album.meta.musicbrainz, expected);
|
||||||
|
|
||||||
album.set_musicbrainz_ref(MbAlbumRef::from_url_str(MUSICBRAINZ_2).unwrap());
|
album
|
||||||
|
.meta
|
||||||
|
.set_musicbrainz_ref(MbAlbumRef::from_url_str(MUSICBRAINZ_2).unwrap());
|
||||||
_ = expected.insert(MbAlbumRef::from_url_str(MUSICBRAINZ_2).unwrap());
|
_ = expected.insert(MbAlbumRef::from_url_str(MUSICBRAINZ_2).unwrap());
|
||||||
assert_eq!(album.musicbrainz, expected);
|
assert_eq!(album.meta.musicbrainz, expected);
|
||||||
|
|
||||||
// Clearing URLs.
|
// Clearing URLs.
|
||||||
album.clear_musicbrainz_ref();
|
album.meta.clear_musicbrainz_ref();
|
||||||
_ = expected.take();
|
_ = expected.take();
|
||||||
assert_eq!(album.musicbrainz, expected);
|
assert_eq!(album.meta.musicbrainz, expected);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,18 +13,24 @@ use crate::core::collection::{
|
|||||||
/// An artist.
|
/// An artist.
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
pub struct Artist {
|
pub struct Artist {
|
||||||
|
pub meta: ArtistMeta,
|
||||||
|
pub albums: Vec<Album>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Artist metadata.
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
|
pub struct ArtistMeta {
|
||||||
pub id: ArtistId,
|
pub id: ArtistId,
|
||||||
pub sort: Option<ArtistId>,
|
pub sort: Option<ArtistId>,
|
||||||
pub musicbrainz: Option<MbArtistRef>,
|
pub musicbrainz: Option<MbArtistRef>,
|
||||||
pub properties: HashMap<String, Vec<String>>,
|
pub properties: HashMap<String, Vec<String>>,
|
||||||
pub albums: Vec<Album>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WithId for Artist {
|
impl WithId for Artist {
|
||||||
type Id = ArtistId;
|
type Id = ArtistId;
|
||||||
|
|
||||||
fn id(&self) -> &Self::Id {
|
fn id(&self) -> &Self::Id {
|
||||||
&self.id
|
&self.meta.id
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -38,11 +44,39 @@ impl Artist {
|
|||||||
/// Create new [`Artist`] with the given [`ArtistId`].
|
/// Create new [`Artist`] with the given [`ArtistId`].
|
||||||
pub fn new<Id: Into<ArtistId>>(id: Id) -> Self {
|
pub fn new<Id: Into<ArtistId>>(id: Id) -> Self {
|
||||||
Artist {
|
Artist {
|
||||||
|
meta: ArtistMeta::new(id),
|
||||||
|
albums: vec![],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialOrd for Artist {
|
||||||
|
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||||
|
Some(self.cmp(other))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Ord for Artist {
|
||||||
|
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
||||||
|
self.meta.cmp(&other.meta)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Merge for Artist {
|
||||||
|
fn merge_in_place(&mut self, other: Self) {
|
||||||
|
self.meta.merge_in_place(other.meta);
|
||||||
|
let albums = mem::take(&mut self.albums);
|
||||||
|
self.albums = MergeCollections::merge_iter(albums, other.albums);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ArtistMeta {
|
||||||
|
pub fn new<Id: Into<ArtistId>>(id: Id) -> Self {
|
||||||
|
ArtistMeta {
|
||||||
id: id.into(),
|
id: id.into(),
|
||||||
sort: None,
|
sort: None,
|
||||||
musicbrainz: None,
|
musicbrainz: None,
|
||||||
properties: HashMap::new(),
|
properties: HashMap::new(),
|
||||||
albums: vec![],
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -111,28 +145,25 @@ impl Artist {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PartialOrd for Artist {
|
impl PartialOrd for ArtistMeta {
|
||||||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||||
Some(self.cmp(other))
|
Some(self.cmp(other))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Ord for Artist {
|
impl Ord for ArtistMeta {
|
||||||
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
||||||
self.get_sort_key().cmp(&other.get_sort_key())
|
self.get_sort_key().cmp(&other.get_sort_key())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Merge for Artist {
|
impl Merge for ArtistMeta {
|
||||||
fn merge_in_place(&mut self, other: Self) {
|
fn merge_in_place(&mut self, other: Self) {
|
||||||
assert_eq!(self.id, other.id);
|
assert_eq!(self.id, other.id);
|
||||||
|
|
||||||
self.sort = self.sort.take().or(other.sort);
|
self.sort = self.sort.take().or(other.sort);
|
||||||
self.musicbrainz = self.musicbrainz.take().or(other.musicbrainz);
|
self.musicbrainz = self.musicbrainz.take().or(other.musicbrainz);
|
||||||
self.properties.merge_in_place(other.properties);
|
self.properties.merge_in_place(other.properties);
|
||||||
|
|
||||||
let albums = mem::take(&mut self.albums);
|
|
||||||
self.albums = MergeCollections::merge_iter(albums, other.albums);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -181,33 +212,41 @@ mod tests {
|
|||||||
|
|
||||||
let mut artist = Artist::new(&artist_id.name);
|
let mut artist = Artist::new(&artist_id.name);
|
||||||
|
|
||||||
assert_eq!(artist.id, artist_id);
|
assert_eq!(artist.meta.id, artist_id);
|
||||||
assert_eq!(artist.sort, None);
|
assert_eq!(artist.meta.sort, None);
|
||||||
assert_eq!(artist.get_sort_key(), (&artist_id,));
|
assert_eq!(artist.meta.get_sort_key(), (&artist_id,));
|
||||||
|
assert!(artist.meta < ArtistMeta::new(sort_id_1.clone()));
|
||||||
|
assert!(artist.meta < ArtistMeta::new(sort_id_2.clone()));
|
||||||
assert!(artist < Artist::new(sort_id_1.clone()));
|
assert!(artist < Artist::new(sort_id_1.clone()));
|
||||||
assert!(artist < Artist::new(sort_id_2.clone()));
|
assert!(artist < Artist::new(sort_id_2.clone()));
|
||||||
|
|
||||||
artist.set_sort_key(sort_id_1.clone());
|
artist.meta.set_sort_key(sort_id_1.clone());
|
||||||
|
|
||||||
assert_eq!(artist.id, artist_id);
|
assert_eq!(artist.meta.id, artist_id);
|
||||||
assert_eq!(artist.sort.as_ref(), Some(&sort_id_1));
|
assert_eq!(artist.meta.sort.as_ref(), Some(&sort_id_1));
|
||||||
assert_eq!(artist.get_sort_key(), (&sort_id_1,));
|
assert_eq!(artist.meta.get_sort_key(), (&sort_id_1,));
|
||||||
|
assert!(artist.meta > ArtistMeta::new(artist_id.clone()));
|
||||||
|
assert!(artist.meta < ArtistMeta::new(sort_id_2.clone()));
|
||||||
assert!(artist > Artist::new(artist_id.clone()));
|
assert!(artist > Artist::new(artist_id.clone()));
|
||||||
assert!(artist < Artist::new(sort_id_2.clone()));
|
assert!(artist < Artist::new(sort_id_2.clone()));
|
||||||
|
|
||||||
artist.set_sort_key(sort_id_2.clone());
|
artist.meta.set_sort_key(sort_id_2.clone());
|
||||||
|
|
||||||
assert_eq!(artist.id, artist_id);
|
assert_eq!(artist.meta.id, artist_id);
|
||||||
assert_eq!(artist.sort.as_ref(), Some(&sort_id_2));
|
assert_eq!(artist.meta.sort.as_ref(), Some(&sort_id_2));
|
||||||
assert_eq!(artist.get_sort_key(), (&sort_id_2,));
|
assert_eq!(artist.meta.get_sort_key(), (&sort_id_2,));
|
||||||
|
assert!(artist.meta > ArtistMeta::new(artist_id.clone()));
|
||||||
|
assert!(artist.meta > ArtistMeta::new(sort_id_1.clone()));
|
||||||
assert!(artist > Artist::new(artist_id.clone()));
|
assert!(artist > Artist::new(artist_id.clone()));
|
||||||
assert!(artist > Artist::new(sort_id_1.clone()));
|
assert!(artist > Artist::new(sort_id_1.clone()));
|
||||||
|
|
||||||
artist.clear_sort_key();
|
artist.meta.clear_sort_key();
|
||||||
|
|
||||||
assert_eq!(artist.id, artist_id);
|
assert_eq!(artist.meta.id, artist_id);
|
||||||
assert_eq!(artist.sort, None);
|
assert_eq!(artist.meta.sort, None);
|
||||||
assert_eq!(artist.get_sort_key(), (&artist_id,));
|
assert_eq!(artist.meta.get_sort_key(), (&artist_id,));
|
||||||
|
assert!(artist.meta < ArtistMeta::new(sort_id_1.clone()));
|
||||||
|
assert!(artist.meta < ArtistMeta::new(sort_id_2.clone()));
|
||||||
assert!(artist < Artist::new(sort_id_1.clone()));
|
assert!(artist < Artist::new(sort_id_1.clone()));
|
||||||
assert!(artist < Artist::new(sort_id_2.clone()));
|
assert!(artist < Artist::new(sort_id_2.clone()));
|
||||||
}
|
}
|
||||||
@ -217,24 +256,30 @@ mod tests {
|
|||||||
let mut artist = Artist::new(ArtistId::new("an artist"));
|
let mut artist = Artist::new(ArtistId::new("an artist"));
|
||||||
|
|
||||||
let mut expected: Option<MbArtistRef> = None;
|
let mut expected: Option<MbArtistRef> = None;
|
||||||
assert_eq!(artist.musicbrainz, expected);
|
assert_eq!(artist.meta.musicbrainz, expected);
|
||||||
|
|
||||||
// Setting a URL on an artist.
|
// Setting a URL on an artist.
|
||||||
artist.set_musicbrainz_ref(MbArtistRef::from_url_str(MUSICBRAINZ).unwrap());
|
artist
|
||||||
|
.meta
|
||||||
|
.set_musicbrainz_ref(MbArtistRef::from_url_str(MUSICBRAINZ).unwrap());
|
||||||
_ = expected.insert(MbArtistRef::from_url_str(MUSICBRAINZ).unwrap());
|
_ = expected.insert(MbArtistRef::from_url_str(MUSICBRAINZ).unwrap());
|
||||||
assert_eq!(artist.musicbrainz, expected);
|
assert_eq!(artist.meta.musicbrainz, expected);
|
||||||
|
|
||||||
artist.set_musicbrainz_ref(MbArtistRef::from_url_str(MUSICBRAINZ).unwrap());
|
artist
|
||||||
assert_eq!(artist.musicbrainz, expected);
|
.meta
|
||||||
|
.set_musicbrainz_ref(MbArtistRef::from_url_str(MUSICBRAINZ).unwrap());
|
||||||
|
assert_eq!(artist.meta.musicbrainz, expected);
|
||||||
|
|
||||||
artist.set_musicbrainz_ref(MbArtistRef::from_url_str(MUSICBRAINZ_2).unwrap());
|
artist
|
||||||
|
.meta
|
||||||
|
.set_musicbrainz_ref(MbArtistRef::from_url_str(MUSICBRAINZ_2).unwrap());
|
||||||
_ = expected.insert(MbArtistRef::from_url_str(MUSICBRAINZ_2).unwrap());
|
_ = expected.insert(MbArtistRef::from_url_str(MUSICBRAINZ_2).unwrap());
|
||||||
assert_eq!(artist.musicbrainz, expected);
|
assert_eq!(artist.meta.musicbrainz, expected);
|
||||||
|
|
||||||
// Clearing URLs.
|
// Clearing URLs.
|
||||||
artist.clear_musicbrainz_ref();
|
artist.meta.clear_musicbrainz_ref();
|
||||||
_ = expected.take();
|
_ = expected.take();
|
||||||
assert_eq!(artist.musicbrainz, expected);
|
assert_eq!(artist.meta.musicbrainz, expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -242,70 +287,98 @@ mod tests {
|
|||||||
let mut artist = Artist::new(ArtistId::new("an artist"));
|
let mut artist = Artist::new(ArtistId::new("an artist"));
|
||||||
|
|
||||||
let mut expected: Vec<String> = vec![];
|
let mut expected: Vec<String> = vec![];
|
||||||
assert!(artist.properties.is_empty());
|
assert!(artist.meta.properties.is_empty());
|
||||||
|
|
||||||
// Adding a single URL.
|
// Adding a single URL.
|
||||||
artist.add_to_property("MusicButler", vec![MUSICBUTLER]);
|
artist
|
||||||
|
.meta
|
||||||
|
.add_to_property("MusicButler", vec![MUSICBUTLER]);
|
||||||
expected.push(MUSICBUTLER.to_owned());
|
expected.push(MUSICBUTLER.to_owned());
|
||||||
assert_eq!(artist.properties.get("MusicButler"), Some(&expected));
|
assert_eq!(artist.meta.properties.get("MusicButler"), Some(&expected));
|
||||||
|
|
||||||
// Adding a URL that already exists is ok, but does not do anything.
|
// Adding a URL that already exists is ok, but does not do anything.
|
||||||
artist.add_to_property("MusicButler", vec![MUSICBUTLER]);
|
artist
|
||||||
assert_eq!(artist.properties.get("MusicButler"), Some(&expected));
|
.meta
|
||||||
|
.add_to_property("MusicButler", vec![MUSICBUTLER]);
|
||||||
|
assert_eq!(artist.meta.properties.get("MusicButler"), Some(&expected));
|
||||||
|
|
||||||
// Adding another single URL.
|
// Adding another single URL.
|
||||||
artist.add_to_property("MusicButler", vec![MUSICBUTLER_2]);
|
artist
|
||||||
|
.meta
|
||||||
|
.add_to_property("MusicButler", vec![MUSICBUTLER_2]);
|
||||||
expected.push(MUSICBUTLER_2.to_owned());
|
expected.push(MUSICBUTLER_2.to_owned());
|
||||||
assert_eq!(artist.properties.get("MusicButler"), Some(&expected));
|
assert_eq!(artist.meta.properties.get("MusicButler"), Some(&expected));
|
||||||
|
|
||||||
artist.add_to_property("MusicButler", vec![MUSICBUTLER_2]);
|
artist
|
||||||
assert_eq!(artist.properties.get("MusicButler"), Some(&expected));
|
.meta
|
||||||
|
.add_to_property("MusicButler", vec![MUSICBUTLER_2]);
|
||||||
|
assert_eq!(artist.meta.properties.get("MusicButler"), Some(&expected));
|
||||||
|
|
||||||
// Removing a URL.
|
// Removing a URL.
|
||||||
artist.remove_from_property("MusicButler", vec![MUSICBUTLER]);
|
artist
|
||||||
|
.meta
|
||||||
|
.remove_from_property("MusicButler", vec![MUSICBUTLER]);
|
||||||
expected.retain(|url| url != MUSICBUTLER);
|
expected.retain(|url| url != MUSICBUTLER);
|
||||||
assert_eq!(artist.properties.get("MusicButler"), Some(&expected));
|
assert_eq!(artist.meta.properties.get("MusicButler"), Some(&expected));
|
||||||
|
|
||||||
// Removing URls that do not exist is okay, they will be ignored.
|
// Removing URls that do not exist is okay, they will be ignored.
|
||||||
artist.remove_from_property("MusicButler", vec![MUSICBUTLER]);
|
artist
|
||||||
assert_eq!(artist.properties.get("MusicButler"), Some(&expected));
|
.meta
|
||||||
|
.remove_from_property("MusicButler", vec![MUSICBUTLER]);
|
||||||
|
assert_eq!(artist.meta.properties.get("MusicButler"), Some(&expected));
|
||||||
|
|
||||||
// Removing a URL.
|
// Removing a URL.
|
||||||
artist.remove_from_property("MusicButler", vec![MUSICBUTLER_2]);
|
artist
|
||||||
|
.meta
|
||||||
|
.remove_from_property("MusicButler", vec![MUSICBUTLER_2]);
|
||||||
expected.retain(|url| url.as_str() != MUSICBUTLER_2);
|
expected.retain(|url| url.as_str() != MUSICBUTLER_2);
|
||||||
assert!(artist.properties.is_empty());
|
assert!(artist.meta.properties.is_empty());
|
||||||
|
|
||||||
artist.remove_from_property("MusicButler", vec![MUSICBUTLER_2]);
|
artist
|
||||||
assert!(artist.properties.is_empty());
|
.meta
|
||||||
|
.remove_from_property("MusicButler", vec![MUSICBUTLER_2]);
|
||||||
|
assert!(artist.meta.properties.is_empty());
|
||||||
|
|
||||||
// Adding URLs if some exist is okay, they will be ignored.
|
// Adding URLs if some exist is okay, they will be ignored.
|
||||||
artist.add_to_property("MusicButler", vec![MUSICBUTLER]);
|
artist
|
||||||
|
.meta
|
||||||
|
.add_to_property("MusicButler", vec![MUSICBUTLER]);
|
||||||
expected.push(MUSICBUTLER.to_owned());
|
expected.push(MUSICBUTLER.to_owned());
|
||||||
assert_eq!(artist.properties.get("MusicButler"), Some(&expected));
|
assert_eq!(artist.meta.properties.get("MusicButler"), Some(&expected));
|
||||||
|
|
||||||
artist.add_to_property("MusicButler", vec![MUSICBUTLER, MUSICBUTLER_2]);
|
artist
|
||||||
|
.meta
|
||||||
|
.add_to_property("MusicButler", vec![MUSICBUTLER, MUSICBUTLER_2]);
|
||||||
expected.push(MUSICBUTLER_2.to_owned());
|
expected.push(MUSICBUTLER_2.to_owned());
|
||||||
assert_eq!(artist.properties.get("MusicButler"), Some(&expected));
|
assert_eq!(artist.meta.properties.get("MusicButler"), Some(&expected));
|
||||||
|
|
||||||
// Removing URLs if some do not exist is okay, they will be ignored.
|
// Removing URLs if some do not exist is okay, they will be ignored.
|
||||||
artist.remove_from_property("MusicButler", vec![MUSICBUTLER]);
|
artist
|
||||||
|
.meta
|
||||||
|
.remove_from_property("MusicButler", vec![MUSICBUTLER]);
|
||||||
expected.retain(|url| url.as_str() != MUSICBUTLER);
|
expected.retain(|url| url.as_str() != MUSICBUTLER);
|
||||||
assert_eq!(artist.properties.get("MusicButler"), Some(&expected));
|
assert_eq!(artist.meta.properties.get("MusicButler"), Some(&expected));
|
||||||
|
|
||||||
artist.remove_from_property("MusicButler", vec![MUSICBUTLER, MUSICBUTLER_2]);
|
artist
|
||||||
|
.meta
|
||||||
|
.remove_from_property("MusicButler", vec![MUSICBUTLER, MUSICBUTLER_2]);
|
||||||
expected.retain(|url| url.as_str() != MUSICBUTLER_2);
|
expected.retain(|url| url.as_str() != MUSICBUTLER_2);
|
||||||
assert!(artist.properties.is_empty());
|
assert!(artist.meta.properties.is_empty());
|
||||||
|
|
||||||
// Adding mutliple URLs without clashes.
|
// Adding mutliple URLs without clashes.
|
||||||
artist.add_to_property("MusicButler", vec![MUSICBUTLER, MUSICBUTLER_2]);
|
artist
|
||||||
|
.meta
|
||||||
|
.add_to_property("MusicButler", vec![MUSICBUTLER, MUSICBUTLER_2]);
|
||||||
expected.push(MUSICBUTLER.to_owned());
|
expected.push(MUSICBUTLER.to_owned());
|
||||||
expected.push(MUSICBUTLER_2.to_owned());
|
expected.push(MUSICBUTLER_2.to_owned());
|
||||||
assert_eq!(artist.properties.get("MusicButler"), Some(&expected));
|
assert_eq!(artist.meta.properties.get("MusicButler"), Some(&expected));
|
||||||
|
|
||||||
// Removing multiple URLs without clashes.
|
// Removing multiple URLs without clashes.
|
||||||
artist.remove_from_property("MusicButler", vec![MUSICBUTLER, MUSICBUTLER_2]);
|
artist
|
||||||
|
.meta
|
||||||
|
.remove_from_property("MusicButler", vec![MUSICBUTLER, MUSICBUTLER_2]);
|
||||||
expected.clear();
|
expected.clear();
|
||||||
assert!(artist.properties.is_empty());
|
assert!(artist.meta.properties.is_empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -313,40 +386,45 @@ mod tests {
|
|||||||
let mut artist = Artist::new(ArtistId::new("an artist"));
|
let mut artist = Artist::new(ArtistId::new("an artist"));
|
||||||
|
|
||||||
let mut expected: Vec<String> = vec![];
|
let mut expected: Vec<String> = vec![];
|
||||||
assert!(artist.properties.is_empty());
|
assert!(artist.meta.properties.is_empty());
|
||||||
|
|
||||||
// Set URLs.
|
// Set URLs.
|
||||||
artist.set_property("MusicButler", vec![MUSICBUTLER]);
|
artist.meta.set_property("MusicButler", vec![MUSICBUTLER]);
|
||||||
expected.push(MUSICBUTLER.to_owned());
|
expected.push(MUSICBUTLER.to_owned());
|
||||||
assert_eq!(artist.properties.get("MusicButler"), Some(&expected));
|
assert_eq!(artist.meta.properties.get("MusicButler"), Some(&expected));
|
||||||
|
|
||||||
artist.set_property("MusicButler", vec![MUSICBUTLER_2]);
|
artist.meta.set_property("MusicButler", vec![MUSICBUTLER_2]);
|
||||||
expected.clear();
|
expected.clear();
|
||||||
expected.push(MUSICBUTLER_2.to_owned());
|
expected.push(MUSICBUTLER_2.to_owned());
|
||||||
assert_eq!(artist.properties.get("MusicButler"), Some(&expected));
|
assert_eq!(artist.meta.properties.get("MusicButler"), Some(&expected));
|
||||||
|
|
||||||
artist.set_property("MusicButler", vec![MUSICBUTLER, MUSICBUTLER_2]);
|
artist
|
||||||
|
.meta
|
||||||
|
.set_property("MusicButler", vec![MUSICBUTLER, MUSICBUTLER_2]);
|
||||||
expected.clear();
|
expected.clear();
|
||||||
expected.push(MUSICBUTLER.to_owned());
|
expected.push(MUSICBUTLER.to_owned());
|
||||||
expected.push(MUSICBUTLER_2.to_owned());
|
expected.push(MUSICBUTLER_2.to_owned());
|
||||||
assert_eq!(artist.properties.get("MusicButler"), Some(&expected));
|
assert_eq!(artist.meta.properties.get("MusicButler"), Some(&expected));
|
||||||
|
|
||||||
// Clear URLs.
|
// Clear URLs.
|
||||||
artist.clear_property("MusicButler");
|
artist.meta.clear_property("MusicButler");
|
||||||
expected.clear();
|
expected.clear();
|
||||||
assert!(artist.properties.is_empty());
|
assert!(artist.meta.properties.is_empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn merge_artist_no_overlap() {
|
fn merge_artist_no_overlap() {
|
||||||
let left = FULL_COLLECTION[0].to_owned();
|
let left = FULL_COLLECTION[0].to_owned();
|
||||||
let mut right = FULL_COLLECTION[1].to_owned();
|
let mut right = FULL_COLLECTION[1].to_owned();
|
||||||
right.id = left.id.clone();
|
right.meta.id = left.meta.id.clone();
|
||||||
right.musicbrainz = None;
|
right.meta.musicbrainz = None;
|
||||||
right.properties = HashMap::new();
|
right.meta.properties = HashMap::new();
|
||||||
|
|
||||||
let mut expected = left.clone();
|
let mut expected = left.clone();
|
||||||
expected.properties = expected.properties.merge(right.clone().properties);
|
expected.meta.properties = expected
|
||||||
|
.meta
|
||||||
|
.properties
|
||||||
|
.merge(right.clone().meta.properties);
|
||||||
expected.albums.append(&mut right.albums.clone());
|
expected.albums.append(&mut right.albums.clone());
|
||||||
expected.albums.sort_unstable();
|
expected.albums.sort_unstable();
|
||||||
|
|
||||||
@ -362,12 +440,15 @@ mod tests {
|
|||||||
fn merge_artist_overlap() {
|
fn merge_artist_overlap() {
|
||||||
let mut left = FULL_COLLECTION[0].to_owned();
|
let mut left = FULL_COLLECTION[0].to_owned();
|
||||||
let mut right = FULL_COLLECTION[1].to_owned();
|
let mut right = FULL_COLLECTION[1].to_owned();
|
||||||
right.id = left.id.clone();
|
right.meta.id = left.meta.id.clone();
|
||||||
left.albums.push(right.albums[0].clone());
|
left.albums.push(right.albums[0].clone());
|
||||||
left.albums.sort_unstable();
|
left.albums.sort_unstable();
|
||||||
|
|
||||||
let mut expected = left.clone();
|
let mut expected = left.clone();
|
||||||
expected.properties = expected.properties.merge(right.clone().properties);
|
expected.meta.properties = expected
|
||||||
|
.meta
|
||||||
|
.properties
|
||||||
|
.merge(right.clone().meta.properties);
|
||||||
expected.albums.append(&mut right.albums.clone());
|
expected.albums.append(&mut right.albums.clone());
|
||||||
expected.albums.sort_unstable();
|
expected.albums.sort_unstable();
|
||||||
expected.albums.dedup();
|
expected.albums.dedup();
|
||||||
|
@ -59,14 +59,14 @@ impl<Database, Library> IMusicHoardBasePrivate for MusicHoard<Database, Library>
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn get_artist<'a>(collection: &'a Collection, artist_id: &ArtistId) -> Option<&'a Artist> {
|
fn get_artist<'a>(collection: &'a Collection, artist_id: &ArtistId) -> Option<&'a Artist> {
|
||||||
collection.iter().find(|a| &a.id == artist_id)
|
collection.iter().find(|a| &a.meta.id == artist_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_artist_mut<'a>(
|
fn get_artist_mut<'a>(
|
||||||
collection: &'a mut Collection,
|
collection: &'a mut Collection,
|
||||||
artist_id: &ArtistId,
|
artist_id: &ArtistId,
|
||||||
) -> Option<&'a mut Artist> {
|
) -> Option<&'a mut Artist> {
|
||||||
collection.iter_mut().find(|a| &a.id == artist_id)
|
collection.iter_mut().find(|a| &a.meta.id == artist_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_artist_mut_or_err<'a>(
|
fn get_artist_mut_or_err<'a>(
|
||||||
@ -79,7 +79,7 @@ impl<Database, Library> IMusicHoardBasePrivate for MusicHoard<Database, Library>
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn get_album_mut<'a>(artist: &'a mut Artist, album_id: &AlbumId) -> Option<&'a mut Album> {
|
fn get_album_mut<'a>(artist: &'a mut Artist, album_id: &AlbumId) -> Option<&'a mut Album> {
|
||||||
artist.albums.iter_mut().find(|a| &a.id == album_id)
|
artist.albums.iter_mut().find(|a| &a.meta.id == album_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_album_mut_or_err<'a>(
|
fn get_album_mut_or_err<'a>(
|
||||||
@ -115,7 +115,7 @@ mod tests {
|
|||||||
library_cache: left
|
library_cache: left
|
||||||
.clone()
|
.clone()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|a| (a.id.clone(), a))
|
.map(|a| (a.meta.id.clone(), a))
|
||||||
.collect(),
|
.collect(),
|
||||||
database_cache: right.clone(),
|
database_cache: right.clone(),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
@ -129,7 +129,7 @@ mod tests {
|
|||||||
library_cache: right
|
library_cache: right
|
||||||
.clone()
|
.clone()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|a| (a.id.clone(), a))
|
.map(|a| (a.meta.id.clone(), a))
|
||||||
.collect(),
|
.collect(),
|
||||||
database_cache: left.clone(),
|
database_cache: left.clone(),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
@ -153,7 +153,7 @@ mod tests {
|
|||||||
library_cache: left
|
library_cache: left
|
||||||
.clone()
|
.clone()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|a| (a.id.clone(), a))
|
.map(|a| (a.meta.id.clone(), a))
|
||||||
.collect(),
|
.collect(),
|
||||||
database_cache: right.clone(),
|
database_cache: right.clone(),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
@ -167,7 +167,7 @@ mod tests {
|
|||||||
library_cache: right
|
library_cache: right
|
||||||
.clone()
|
.clone()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|a| (a.id.clone(), a))
|
.map(|a| (a.meta.id.clone(), a))
|
||||||
.collect(),
|
.collect(),
|
||||||
database_cache: left.clone(),
|
database_cache: left.clone(),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
@ -191,20 +191,20 @@ mod tests {
|
|||||||
|
|
||||||
assert!(right.first().unwrap() > left.first().unwrap());
|
assert!(right.first().unwrap() > left.first().unwrap());
|
||||||
let artist_sort = Some(ArtistId::new("Album_Artist 0"));
|
let artist_sort = Some(ArtistId::new("Album_Artist 0"));
|
||||||
right[0].sort = artist_sort.clone();
|
right[0].meta.sort = artist_sort.clone();
|
||||||
assert!(right.first().unwrap() < left.first().unwrap());
|
assert!(right.first().unwrap() < left.first().unwrap());
|
||||||
|
|
||||||
// The result of the merge should be the same list of artists, but with the last artist now
|
// The result of the merge should be the same list of artists, but with the last artist now
|
||||||
// in first place.
|
// in first place.
|
||||||
let mut expected = left.to_owned();
|
let mut expected = left.to_owned();
|
||||||
expected.last_mut().as_mut().unwrap().sort = artist_sort.clone();
|
expected.last_mut().as_mut().unwrap().meta.sort = artist_sort.clone();
|
||||||
expected.rotate_right(1);
|
expected.rotate_right(1);
|
||||||
|
|
||||||
let mut mh = MusicHoard {
|
let mut mh = MusicHoard {
|
||||||
library_cache: left
|
library_cache: left
|
||||||
.clone()
|
.clone()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|a| (a.id.clone(), a))
|
.map(|a| (a.meta.id.clone(), a))
|
||||||
.collect(),
|
.collect(),
|
||||||
database_cache: right.clone(),
|
database_cache: right.clone(),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
@ -218,7 +218,7 @@ mod tests {
|
|||||||
library_cache: right
|
library_cache: right
|
||||||
.clone()
|
.clone()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|a| (a.id.clone(), a))
|
.map(|a| (a.meta.id.clone(), a))
|
||||||
.collect(),
|
.collect(),
|
||||||
database_cache: left.clone(),
|
database_cache: left.clone(),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
|
@ -91,7 +91,9 @@ impl<Database: IDatabase, Library> IMusicHoardDatabase for MusicHoard<Database,
|
|||||||
|
|
||||||
fn remove_artist<Id: AsRef<ArtistId>>(&mut self, artist_id: Id) -> Result<(), Error> {
|
fn remove_artist<Id: AsRef<ArtistId>>(&mut self, artist_id: Id) -> Result<(), Error> {
|
||||||
self.update_collection(|collection| {
|
self.update_collection(|collection| {
|
||||||
let index_opt = collection.iter().position(|a| &a.id == artist_id.as_ref());
|
let index_opt = collection
|
||||||
|
.iter()
|
||||||
|
.position(|a| &a.meta.id == artist_id.as_ref());
|
||||||
if let Some(index) = index_opt {
|
if let Some(index) = index_opt {
|
||||||
collection.remove(index);
|
collection.remove(index);
|
||||||
}
|
}
|
||||||
@ -105,7 +107,7 @@ impl<Database: IDatabase, Library> IMusicHoardDatabase for MusicHoard<Database,
|
|||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
self.update_artist_and(
|
self.update_artist_and(
|
||||||
artist_id.as_ref(),
|
artist_id.as_ref(),
|
||||||
|artist| artist.set_sort_key(artist_sort),
|
|artist| artist.meta.set_sort_key(artist_sort),
|
||||||
|collection| Self::sort_artists(collection),
|
|collection| Self::sort_artists(collection),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -113,7 +115,7 @@ impl<Database: IDatabase, Library> IMusicHoardDatabase for MusicHoard<Database,
|
|||||||
fn clear_artist_sort<Id: AsRef<ArtistId>>(&mut self, artist_id: Id) -> Result<(), Error> {
|
fn clear_artist_sort<Id: AsRef<ArtistId>>(&mut self, artist_id: Id) -> Result<(), Error> {
|
||||||
self.update_artist_and(
|
self.update_artist_and(
|
||||||
artist_id.as_ref(),
|
artist_id.as_ref(),
|
||||||
|artist| artist.clear_sort_key(),
|
|artist| artist.meta.clear_sort_key(),
|
||||||
|collection| Self::sort_artists(collection),
|
|collection| Self::sort_artists(collection),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -124,14 +126,18 @@ impl<Database: IDatabase, Library> IMusicHoardDatabase for MusicHoard<Database,
|
|||||||
url: S,
|
url: S,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
let mb = MbArtistRef::from_url_str(url)?;
|
let mb = MbArtistRef::from_url_str(url)?;
|
||||||
self.update_artist(artist_id.as_ref(), |artist| artist.set_musicbrainz_ref(mb))
|
self.update_artist(artist_id.as_ref(), |artist| {
|
||||||
|
artist.meta.set_musicbrainz_ref(mb)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn clear_artist_musicbrainz<Id: AsRef<ArtistId>>(
|
fn clear_artist_musicbrainz<Id: AsRef<ArtistId>>(
|
||||||
&mut self,
|
&mut self,
|
||||||
artist_id: Id,
|
artist_id: Id,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
self.update_artist(artist_id.as_ref(), |artist| artist.clear_musicbrainz_ref())
|
self.update_artist(artist_id.as_ref(), |artist| {
|
||||||
|
artist.meta.clear_musicbrainz_ref()
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_to_artist_property<Id: AsRef<ArtistId>, S: AsRef<str> + Into<String>>(
|
fn add_to_artist_property<Id: AsRef<ArtistId>, S: AsRef<str> + Into<String>>(
|
||||||
@ -141,7 +147,7 @@ impl<Database: IDatabase, Library> IMusicHoardDatabase for MusicHoard<Database,
|
|||||||
values: Vec<S>,
|
values: Vec<S>,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
self.update_artist(artist_id.as_ref(), |artist| {
|
self.update_artist(artist_id.as_ref(), |artist| {
|
||||||
artist.add_to_property(property, values)
|
artist.meta.add_to_property(property, values)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -152,7 +158,7 @@ impl<Database: IDatabase, Library> IMusicHoardDatabase for MusicHoard<Database,
|
|||||||
values: Vec<S>,
|
values: Vec<S>,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
self.update_artist(artist_id.as_ref(), |artist| {
|
self.update_artist(artist_id.as_ref(), |artist| {
|
||||||
artist.remove_from_property(property, values)
|
artist.meta.remove_from_property(property, values)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -163,7 +169,7 @@ impl<Database: IDatabase, Library> IMusicHoardDatabase for MusicHoard<Database,
|
|||||||
values: Vec<S>,
|
values: Vec<S>,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
self.update_artist(artist_id.as_ref(), |artist| {
|
self.update_artist(artist_id.as_ref(), |artist| {
|
||||||
artist.set_property(property, values)
|
artist.meta.set_property(property, values)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -172,7 +178,9 @@ impl<Database: IDatabase, Library> IMusicHoardDatabase for MusicHoard<Database,
|
|||||||
artist_id: Id,
|
artist_id: Id,
|
||||||
property: S,
|
property: S,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
self.update_artist(artist_id.as_ref(), |artist| artist.clear_property(property))
|
self.update_artist(artist_id.as_ref(), |artist| {
|
||||||
|
artist.meta.clear_property(property)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_album_seq<ArtistIdRef: AsRef<ArtistId>, AlbumIdRef: AsRef<AlbumId>>(
|
fn set_album_seq<ArtistIdRef: AsRef<ArtistId>, AlbumIdRef: AsRef<AlbumId>>(
|
||||||
@ -184,7 +192,7 @@ impl<Database: IDatabase, Library> IMusicHoardDatabase for MusicHoard<Database,
|
|||||||
self.update_album_and(
|
self.update_album_and(
|
||||||
artist_id.as_ref(),
|
artist_id.as_ref(),
|
||||||
album_id.as_ref(),
|
album_id.as_ref(),
|
||||||
|album| album.set_seq(AlbumSeq(seq)),
|
|album| album.meta.set_seq(AlbumSeq(seq)),
|
||||||
|artist| artist.albums.sort_unstable(),
|
|artist| artist.albums.sort_unstable(),
|
||||||
|_| {},
|
|_| {},
|
||||||
)
|
)
|
||||||
@ -198,7 +206,7 @@ impl<Database: IDatabase, Library> IMusicHoardDatabase for MusicHoard<Database,
|
|||||||
self.update_album_and(
|
self.update_album_and(
|
||||||
artist_id.as_ref(),
|
artist_id.as_ref(),
|
||||||
album_id.as_ref(),
|
album_id.as_ref(),
|
||||||
|album| album.clear_seq(),
|
|album| album.meta.clear_seq(),
|
||||||
|artist| artist.albums.sort_unstable(),
|
|artist| artist.albums.sort_unstable(),
|
||||||
|_| {},
|
|_| {},
|
||||||
)
|
)
|
||||||
@ -434,29 +442,29 @@ mod tests {
|
|||||||
assert!(music_hoard.add_artist(artist_id.clone()).is_ok());
|
assert!(music_hoard.add_artist(artist_id.clone()).is_ok());
|
||||||
|
|
||||||
let mut expected: Option<MbArtistRef> = None;
|
let mut expected: Option<MbArtistRef> = None;
|
||||||
assert_eq!(music_hoard.collection[0].musicbrainz, expected);
|
assert_eq!(music_hoard.collection[0].meta.musicbrainz, expected);
|
||||||
|
|
||||||
// Setting a URL on an artist not in the collection is an error.
|
// Setting a URL on an artist not in the collection is an error.
|
||||||
assert!(music_hoard
|
assert!(music_hoard
|
||||||
.set_artist_musicbrainz(&artist_id_2, MUSICBRAINZ)
|
.set_artist_musicbrainz(&artist_id_2, MUSICBRAINZ)
|
||||||
.is_err());
|
.is_err());
|
||||||
assert_eq!(music_hoard.collection[0].musicbrainz, expected);
|
assert_eq!(music_hoard.collection[0].meta.musicbrainz, expected);
|
||||||
|
|
||||||
// Setting a URL on an artist.
|
// Setting a URL on an artist.
|
||||||
assert!(music_hoard
|
assert!(music_hoard
|
||||||
.set_artist_musicbrainz(&artist_id, MUSICBRAINZ)
|
.set_artist_musicbrainz(&artist_id, MUSICBRAINZ)
|
||||||
.is_ok());
|
.is_ok());
|
||||||
_ = expected.insert(MbArtistRef::from_url_str(MUSICBRAINZ).unwrap());
|
_ = expected.insert(MbArtistRef::from_url_str(MUSICBRAINZ).unwrap());
|
||||||
assert_eq!(music_hoard.collection[0].musicbrainz, expected);
|
assert_eq!(music_hoard.collection[0].meta.musicbrainz, expected);
|
||||||
|
|
||||||
// Clearing URLs on an artist that does not exist is an error.
|
// Clearing URLs on an artist that does not exist is an error.
|
||||||
assert!(music_hoard.clear_artist_musicbrainz(&artist_id_2).is_err());
|
assert!(music_hoard.clear_artist_musicbrainz(&artist_id_2).is_err());
|
||||||
assert_eq!(music_hoard.collection[0].musicbrainz, expected);
|
assert_eq!(music_hoard.collection[0].meta.musicbrainz, expected);
|
||||||
|
|
||||||
// Clearing URLs.
|
// Clearing URLs.
|
||||||
assert!(music_hoard.clear_artist_musicbrainz(&artist_id).is_ok());
|
assert!(music_hoard.clear_artist_musicbrainz(&artist_id).is_ok());
|
||||||
_ = expected.take();
|
_ = expected.take();
|
||||||
assert_eq!(music_hoard.collection[0].musicbrainz, expected);
|
assert_eq!(music_hoard.collection[0].meta.musicbrainz, expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -472,13 +480,13 @@ mod tests {
|
|||||||
assert!(music_hoard.add_artist(artist_id.clone()).is_ok());
|
assert!(music_hoard.add_artist(artist_id.clone()).is_ok());
|
||||||
|
|
||||||
let mut expected: Vec<String> = vec![];
|
let mut expected: Vec<String> = vec![];
|
||||||
assert!(music_hoard.collection[0].properties.is_empty());
|
assert!(music_hoard.collection[0].meta.properties.is_empty());
|
||||||
|
|
||||||
// Adding URLs to an artist not in the collection is an error.
|
// Adding URLs to an artist not in the collection is an error.
|
||||||
assert!(music_hoard
|
assert!(music_hoard
|
||||||
.add_to_artist_property(&artist_id_2, "MusicButler", vec![MUSICBUTLER])
|
.add_to_artist_property(&artist_id_2, "MusicButler", vec![MUSICBUTLER])
|
||||||
.is_err());
|
.is_err());
|
||||||
assert!(music_hoard.collection[0].properties.is_empty());
|
assert!(music_hoard.collection[0].meta.properties.is_empty());
|
||||||
|
|
||||||
// Adding mutliple URLs without clashes.
|
// Adding mutliple URLs without clashes.
|
||||||
assert!(music_hoard
|
assert!(music_hoard
|
||||||
@ -487,7 +495,7 @@ mod tests {
|
|||||||
expected.push(MUSICBUTLER.to_owned());
|
expected.push(MUSICBUTLER.to_owned());
|
||||||
expected.push(MUSICBUTLER_2.to_owned());
|
expected.push(MUSICBUTLER_2.to_owned());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
music_hoard.collection[0].properties.get("MusicButler"),
|
music_hoard.collection[0].meta.properties.get("MusicButler"),
|
||||||
Some(&expected)
|
Some(&expected)
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -496,7 +504,7 @@ mod tests {
|
|||||||
.remove_from_artist_property(&artist_id_2, "MusicButler", vec![MUSICBUTLER])
|
.remove_from_artist_property(&artist_id_2, "MusicButler", vec![MUSICBUTLER])
|
||||||
.is_err());
|
.is_err());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
music_hoard.collection[0].properties.get("MusicButler"),
|
music_hoard.collection[0].meta.properties.get("MusicButler"),
|
||||||
Some(&expected)
|
Some(&expected)
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -509,7 +517,7 @@ mod tests {
|
|||||||
)
|
)
|
||||||
.is_ok());
|
.is_ok());
|
||||||
expected.clear();
|
expected.clear();
|
||||||
assert!(music_hoard.collection[0].properties.is_empty());
|
assert!(music_hoard.collection[0].meta.properties.is_empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -525,13 +533,13 @@ mod tests {
|
|||||||
assert!(music_hoard.add_artist(artist_id.clone()).is_ok());
|
assert!(music_hoard.add_artist(artist_id.clone()).is_ok());
|
||||||
|
|
||||||
let mut expected: Vec<String> = vec![];
|
let mut expected: Vec<String> = vec![];
|
||||||
assert!(music_hoard.collection[0].properties.is_empty());
|
assert!(music_hoard.collection[0].meta.properties.is_empty());
|
||||||
|
|
||||||
// Seting URL on an artist not in the collection is an error.
|
// Seting URL on an artist not in the collection is an error.
|
||||||
assert!(music_hoard
|
assert!(music_hoard
|
||||||
.set_artist_property(&artist_id_2, "MusicButler", vec![MUSICBUTLER])
|
.set_artist_property(&artist_id_2, "MusicButler", vec![MUSICBUTLER])
|
||||||
.is_err());
|
.is_err());
|
||||||
assert!(music_hoard.collection[0].properties.is_empty());
|
assert!(music_hoard.collection[0].meta.properties.is_empty());
|
||||||
|
|
||||||
// Set URLs.
|
// Set URLs.
|
||||||
assert!(music_hoard
|
assert!(music_hoard
|
||||||
@ -541,7 +549,7 @@ mod tests {
|
|||||||
expected.push(MUSICBUTLER.to_owned());
|
expected.push(MUSICBUTLER.to_owned());
|
||||||
expected.push(MUSICBUTLER_2.to_owned());
|
expected.push(MUSICBUTLER_2.to_owned());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
music_hoard.collection[0].properties.get("MusicButler"),
|
music_hoard.collection[0].meta.properties.get("MusicButler"),
|
||||||
Some(&expected)
|
Some(&expected)
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -555,7 +563,7 @@ mod tests {
|
|||||||
.clear_artist_property(&artist_id, "MusicButler")
|
.clear_artist_property(&artist_id, "MusicButler")
|
||||||
.is_ok());
|
.is_ok());
|
||||||
expected.clear();
|
expected.clear();
|
||||||
assert!(music_hoard.collection[0].properties.is_empty());
|
assert!(music_hoard.collection[0].meta.properties.is_empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -581,17 +589,17 @@ mod tests {
|
|||||||
database.expect_save().times(2).returning(|_| Ok(()));
|
database.expect_save().times(2).returning(|_| Ok(()));
|
||||||
|
|
||||||
let mut music_hoard = MusicHoard::database(database).unwrap();
|
let mut music_hoard = MusicHoard::database(database).unwrap();
|
||||||
assert_eq!(music_hoard.collection[0].albums[0].seq, AlbumSeq(0));
|
assert_eq!(music_hoard.collection[0].albums[0].meta.seq, AlbumSeq(0));
|
||||||
|
|
||||||
// Seting seq on an album not belonging to the artist is an error.
|
// Seting seq on an album not belonging to the artist is an error.
|
||||||
assert!(music_hoard
|
assert!(music_hoard
|
||||||
.set_album_seq(&artist_id, &album_id_2, 6)
|
.set_album_seq(&artist_id, &album_id_2, 6)
|
||||||
.is_err());
|
.is_err());
|
||||||
assert_eq!(music_hoard.collection[0].albums[0].seq, AlbumSeq(0));
|
assert_eq!(music_hoard.collection[0].albums[0].meta.seq, AlbumSeq(0));
|
||||||
|
|
||||||
// Set seq.
|
// Set seq.
|
||||||
assert!(music_hoard.set_album_seq(&artist_id, &album_id, 6).is_ok());
|
assert!(music_hoard.set_album_seq(&artist_id, &album_id, 6).is_ok());
|
||||||
assert_eq!(music_hoard.collection[0].albums[0].seq, AlbumSeq(6));
|
assert_eq!(music_hoard.collection[0].albums[0].meta.seq, AlbumSeq(6));
|
||||||
|
|
||||||
// Clearing seq on an album that does not exist is an error.
|
// Clearing seq on an album that does not exist is an error.
|
||||||
assert!(music_hoard
|
assert!(music_hoard
|
||||||
@ -600,7 +608,7 @@ mod tests {
|
|||||||
|
|
||||||
// Clear seq.
|
// Clear seq.
|
||||||
assert!(music_hoard.clear_album_seq(&artist_id, &album_id).is_ok());
|
assert!(music_hoard.clear_album_seq(&artist_id, &album_id).is_ok());
|
||||||
assert_eq!(music_hoard.collection[0].albums[0].seq, AlbumSeq(0));
|
assert_eq!(music_hoard.collection[0].albums[0].meta.seq, AlbumSeq(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -86,17 +86,17 @@ impl<Database, Library: ILibrary> MusicHoard<Database, Library> {
|
|||||||
.or_insert_with(|| Artist::new(artist_id)),
|
.or_insert_with(|| Artist::new(artist_id)),
|
||||||
};
|
};
|
||||||
|
|
||||||
if artist.sort.is_some() {
|
if artist.meta.sort.is_some() {
|
||||||
if artist_sort.is_some() && (artist.sort != artist_sort) {
|
if artist_sort.is_some() && (artist.meta.sort != artist_sort) {
|
||||||
return Err(Error::CollectionError(format!(
|
return Err(Error::CollectionError(format!(
|
||||||
"multiple album_artist_sort found for artist '{}': '{}' != '{}'",
|
"multiple album_artist_sort found for artist '{}': '{}' != '{}'",
|
||||||
artist.id,
|
artist.meta.id,
|
||||||
artist.sort.as_ref().unwrap(),
|
artist.meta.sort.as_ref().unwrap(),
|
||||||
artist_sort.as_ref().unwrap()
|
artist_sort.as_ref().unwrap()
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
} else if artist_sort.is_some() {
|
} else if artist_sort.is_some() {
|
||||||
artist.sort = artist_sort;
|
artist.meta.sort = artist_sort;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do a linear search as few artists have more than a handful of albums. Search from the
|
// Do a linear search as few artists have more than a handful of albums. Search from the
|
||||||
@ -105,7 +105,7 @@ impl<Database, Library: ILibrary> MusicHoard<Database, Library> {
|
|||||||
.albums
|
.albums
|
||||||
.iter_mut()
|
.iter_mut()
|
||||||
.rev()
|
.rev()
|
||||||
.find(|album| album.id == album_id)
|
.find(|album| album.meta.id == album_id)
|
||||||
{
|
{
|
||||||
Some(album) => album.tracks.push(track),
|
Some(album) => album.tracks.push(track),
|
||||||
None => {
|
None => {
|
||||||
@ -197,13 +197,13 @@ mod tests {
|
|||||||
assert!(music_hoard.get_collection()[0]
|
assert!(music_hoard.get_collection()[0]
|
||||||
.albums
|
.albums
|
||||||
.iter()
|
.iter()
|
||||||
.any(|album| album.id.title == "album_title a.a"));
|
.any(|album| album.meta.id.title == "album_title a.a"));
|
||||||
|
|
||||||
music_hoard.rescan_library().unwrap();
|
music_hoard.rescan_library().unwrap();
|
||||||
assert!(!music_hoard.get_collection()[0]
|
assert!(!music_hoard.get_collection()[0]
|
||||||
.albums
|
.albums
|
||||||
.iter()
|
.iter()
|
||||||
.any(|album| album.id.title == "album_title a.a"));
|
.any(|album| album.meta.id.title == "album_title a.a"));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -234,8 +234,8 @@ mod tests {
|
|||||||
let mut library = MockILibrary::new();
|
let mut library = MockILibrary::new();
|
||||||
|
|
||||||
let mut expected = LIBRARY_COLLECTION.to_owned();
|
let mut expected = LIBRARY_COLLECTION.to_owned();
|
||||||
let removed_album_id = expected[0].albums[0].id.clone();
|
let removed_album_id = expected[0].albums[0].meta.id.clone();
|
||||||
let clashed_album_id = &expected[1].albums[0].id;
|
let clashed_album_id = &expected[1].albums[0].meta.id;
|
||||||
|
|
||||||
let mut items = LIBRARY_ITEMS.to_owned();
|
let mut items = LIBRARY_ITEMS.to_owned();
|
||||||
for item in items
|
for item in items
|
||||||
@ -245,7 +245,7 @@ mod tests {
|
|||||||
item.album_title = clashed_album_id.title.clone();
|
item.album_title = clashed_album_id.title.clone();
|
||||||
}
|
}
|
||||||
|
|
||||||
expected[0].albums[0].id = clashed_album_id.clone();
|
expected[0].albums[0].meta.id = clashed_album_id.clone();
|
||||||
|
|
||||||
let library_input = Query::new();
|
let library_input = Query::new();
|
||||||
let library_result = Ok(items);
|
let library_result = Ok(items);
|
||||||
|
@ -2,8 +2,8 @@ use once_cell::sync::Lazy;
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use crate::core::collection::{
|
use crate::core::collection::{
|
||||||
album::{Album, AlbumId, AlbumPrimaryType, AlbumSeq},
|
album::{Album, AlbumId, AlbumMeta, AlbumPrimaryType, AlbumSeq},
|
||||||
artist::{Artist, ArtistId},
|
artist::{Artist, ArtistId, ArtistMeta},
|
||||||
musicbrainz::{MbAlbumRef, MbArtistRef},
|
musicbrainz::{MbAlbumRef, MbArtistRef},
|
||||||
track::{Track, TrackFormat, TrackId, TrackNum, TrackQuality},
|
track::{Track, TrackFormat, TrackId, TrackNum, TrackQuality},
|
||||||
};
|
};
|
||||||
|
2
src/external/database/json/mod.rs
vendored
2
src/external/database/json/mod.rs
vendored
@ -83,7 +83,7 @@ mod tests {
|
|||||||
let mut expected = FULL_COLLECTION.to_owned();
|
let mut expected = FULL_COLLECTION.to_owned();
|
||||||
for artist in expected.iter_mut() {
|
for artist in expected.iter_mut() {
|
||||||
for album in artist.albums.iter_mut() {
|
for album in artist.albums.iter_mut() {
|
||||||
album.date = AlbumDate::default();
|
album.meta.date = AlbumDate::default();
|
||||||
album.tracks.clear();
|
album.tracks.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
6
src/external/database/serde/deserialize.rs
vendored
6
src/external/database/serde/deserialize.rs
vendored
@ -3,7 +3,7 @@ use std::{collections::HashMap, fmt};
|
|||||||
use serde::{de::Visitor, Deserialize, Deserializer};
|
use serde::{de::Visitor, Deserialize, Deserializer};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
collection::musicbrainz::Mbid,
|
collection::{album::AlbumMeta, artist::ArtistMeta, musicbrainz::Mbid},
|
||||||
core::collection::{
|
core::collection::{
|
||||||
album::{Album, AlbumDate, AlbumId, AlbumSeq},
|
album::{Album, AlbumDate, AlbumId, AlbumSeq},
|
||||||
artist::{Artist, ArtistId},
|
artist::{Artist, ArtistId},
|
||||||
@ -86,10 +86,12 @@ impl<'de> Deserialize<'de> for DeserializeMbid {
|
|||||||
impl From<DeserializeArtist> for Artist {
|
impl From<DeserializeArtist> for Artist {
|
||||||
fn from(artist: DeserializeArtist) -> Self {
|
fn from(artist: DeserializeArtist) -> Self {
|
||||||
Artist {
|
Artist {
|
||||||
|
meta: ArtistMeta {
|
||||||
id: ArtistId::new(artist.name),
|
id: ArtistId::new(artist.name),
|
||||||
sort: artist.sort.map(ArtistId::new),
|
sort: artist.sort.map(ArtistId::new),
|
||||||
musicbrainz: artist.musicbrainz.map(Into::<Mbid>::into).map(Into::into),
|
musicbrainz: artist.musicbrainz.map(Into::<Mbid>::into).map(Into::into),
|
||||||
properties: artist.properties,
|
properties: artist.properties,
|
||||||
|
},
|
||||||
albums: artist.albums.into_iter().map(Into::into).collect(),
|
albums: artist.albums.into_iter().map(Into::into).collect(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -98,12 +100,14 @@ impl From<DeserializeArtist> for Artist {
|
|||||||
impl From<DeserializeAlbum> for Album {
|
impl From<DeserializeAlbum> for Album {
|
||||||
fn from(album: DeserializeAlbum) -> Self {
|
fn from(album: DeserializeAlbum) -> Self {
|
||||||
Album {
|
Album {
|
||||||
|
meta: AlbumMeta {
|
||||||
id: AlbumId { title: album.title },
|
id: AlbumId { title: album.title },
|
||||||
date: AlbumDate::default(),
|
date: AlbumDate::default(),
|
||||||
seq: AlbumSeq(album.seq),
|
seq: AlbumSeq(album.seq),
|
||||||
musicbrainz: album.musicbrainz.map(Into::<Mbid>::into).map(Into::into),
|
musicbrainz: album.musicbrainz.map(Into::<Mbid>::into).map(Into::into),
|
||||||
primary_type: album.primary_type.map(Into::into),
|
primary_type: album.primary_type.map(Into::into),
|
||||||
secondary_types: album.secondary_types.into_iter().map(Into::into).collect(),
|
secondary_types: album.secondary_types.into_iter().map(Into::into).collect(),
|
||||||
|
},
|
||||||
tracks: vec![],
|
tracks: vec![],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
14
src/external/database/serde/serialize.rs
vendored
14
src/external/database/serde/serialize.rs
vendored
@ -52,13 +52,15 @@ impl<'a> Serialize for SerializeMbid<'a> {
|
|||||||
impl<'a> From<&'a Artist> for SerializeArtist<'a> {
|
impl<'a> From<&'a Artist> for SerializeArtist<'a> {
|
||||||
fn from(artist: &'a Artist) -> Self {
|
fn from(artist: &'a Artist) -> Self {
|
||||||
SerializeArtist {
|
SerializeArtist {
|
||||||
name: &artist.id.name,
|
name: &artist.meta.id.name,
|
||||||
sort: artist.sort.as_ref().map(|id| id.name.as_ref()),
|
sort: artist.meta.sort.as_ref().map(|id| id.name.as_ref()),
|
||||||
musicbrainz: artist
|
musicbrainz: artist
|
||||||
|
.meta
|
||||||
.musicbrainz
|
.musicbrainz
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|mbref| SerializeMbid(mbref.mbid())),
|
.map(|mbref| SerializeMbid(mbref.mbid())),
|
||||||
properties: artist
|
properties: artist
|
||||||
|
.meta
|
||||||
.properties
|
.properties
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(k, v)| (k.as_ref(), v))
|
.map(|(k, v)| (k.as_ref(), v))
|
||||||
@ -71,14 +73,16 @@ impl<'a> From<&'a Artist> for SerializeArtist<'a> {
|
|||||||
impl<'a> From<&'a Album> for SerializeAlbum<'a> {
|
impl<'a> From<&'a Album> for SerializeAlbum<'a> {
|
||||||
fn from(album: &'a Album) -> Self {
|
fn from(album: &'a Album) -> Self {
|
||||||
SerializeAlbum {
|
SerializeAlbum {
|
||||||
title: &album.id.title,
|
title: &album.meta.id.title,
|
||||||
seq: album.seq.0,
|
seq: album.meta.seq.0,
|
||||||
musicbrainz: album
|
musicbrainz: album
|
||||||
|
.meta
|
||||||
.musicbrainz
|
.musicbrainz
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|mbref| SerializeMbid(mbref.mbid())),
|
.map(|mbref| SerializeMbid(mbref.mbid())),
|
||||||
primary_type: album.primary_type.map(Into::into),
|
primary_type: album.meta.primary_type.map(Into::into),
|
||||||
secondary_types: album
|
secondary_types: album
|
||||||
|
.meta
|
||||||
.secondary_types
|
.secondary_types
|
||||||
.iter()
|
.iter()
|
||||||
.copied()
|
.copied()
|
||||||
|
@ -2,6 +2,7 @@ macro_rules! full_collection {
|
|||||||
() => {
|
() => {
|
||||||
vec![
|
vec![
|
||||||
Artist {
|
Artist {
|
||||||
|
meta: ArtistMeta {
|
||||||
id: ArtistId {
|
id: ArtistId {
|
||||||
name: "Album_Artist ‘A’".to_string(),
|
name: "Album_Artist ‘A’".to_string(),
|
||||||
},
|
},
|
||||||
@ -19,8 +20,10 @@ macro_rules! full_collection {
|
|||||||
)
|
)
|
||||||
]),
|
]),
|
||||||
]),
|
]),
|
||||||
|
},
|
||||||
albums: vec![
|
albums: vec![
|
||||||
Album {
|
Album {
|
||||||
|
meta: AlbumMeta {
|
||||||
id: AlbumId {
|
id: AlbumId {
|
||||||
title: "album_title a.a".to_string(),
|
title: "album_title a.a".to_string(),
|
||||||
},
|
},
|
||||||
@ -31,6 +34,7 @@ macro_rules! full_collection {
|
|||||||
).unwrap()),
|
).unwrap()),
|
||||||
primary_type: Some(AlbumPrimaryType::Album),
|
primary_type: Some(AlbumPrimaryType::Album),
|
||||||
secondary_types: vec![],
|
secondary_types: vec![],
|
||||||
|
},
|
||||||
tracks: vec![
|
tracks: vec![
|
||||||
Track {
|
Track {
|
||||||
id: TrackId {
|
id: TrackId {
|
||||||
@ -82,6 +86,7 @@ macro_rules! full_collection {
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
Album {
|
Album {
|
||||||
|
meta: AlbumMeta {
|
||||||
id: AlbumId {
|
id: AlbumId {
|
||||||
title: "album_title a.b".to_string(),
|
title: "album_title a.b".to_string(),
|
||||||
},
|
},
|
||||||
@ -90,6 +95,7 @@ macro_rules! full_collection {
|
|||||||
musicbrainz: None,
|
musicbrainz: None,
|
||||||
primary_type: Some(AlbumPrimaryType::Album),
|
primary_type: Some(AlbumPrimaryType::Album),
|
||||||
secondary_types: vec![],
|
secondary_types: vec![],
|
||||||
|
},
|
||||||
tracks: vec![
|
tracks: vec![
|
||||||
Track {
|
Track {
|
||||||
id: TrackId {
|
id: TrackId {
|
||||||
@ -118,6 +124,7 @@ macro_rules! full_collection {
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
Artist {
|
Artist {
|
||||||
|
meta: ArtistMeta {
|
||||||
id: ArtistId {
|
id: ArtistId {
|
||||||
name: "Album_Artist ‘B’".to_string(),
|
name: "Album_Artist ‘B’".to_string(),
|
||||||
},
|
},
|
||||||
@ -139,8 +146,10 @@ macro_rules! full_collection {
|
|||||||
)
|
)
|
||||||
]),
|
]),
|
||||||
]),
|
]),
|
||||||
|
},
|
||||||
albums: vec![
|
albums: vec![
|
||||||
Album {
|
Album {
|
||||||
|
meta: AlbumMeta {
|
||||||
id: AlbumId {
|
id: AlbumId {
|
||||||
title: "album_title b.a".to_string(),
|
title: "album_title b.a".to_string(),
|
||||||
},
|
},
|
||||||
@ -149,6 +158,7 @@ macro_rules! full_collection {
|
|||||||
musicbrainz: None,
|
musicbrainz: None,
|
||||||
primary_type: Some(AlbumPrimaryType::Album),
|
primary_type: Some(AlbumPrimaryType::Album),
|
||||||
secondary_types: vec![],
|
secondary_types: vec![],
|
||||||
|
},
|
||||||
tracks: vec![
|
tracks: vec![
|
||||||
Track {
|
Track {
|
||||||
id: TrackId {
|
id: TrackId {
|
||||||
@ -178,6 +188,7 @@ macro_rules! full_collection {
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
Album {
|
Album {
|
||||||
|
meta: AlbumMeta {
|
||||||
id: AlbumId {
|
id: AlbumId {
|
||||||
title: "album_title b.b".to_string(),
|
title: "album_title b.b".to_string(),
|
||||||
},
|
},
|
||||||
@ -188,6 +199,7 @@ macro_rules! full_collection {
|
|||||||
).unwrap()),
|
).unwrap()),
|
||||||
primary_type: Some(AlbumPrimaryType::Album),
|
primary_type: Some(AlbumPrimaryType::Album),
|
||||||
secondary_types: vec![],
|
secondary_types: vec![],
|
||||||
|
},
|
||||||
tracks: vec![
|
tracks: vec![
|
||||||
Track {
|
Track {
|
||||||
id: TrackId {
|
id: TrackId {
|
||||||
@ -217,6 +229,7 @@ macro_rules! full_collection {
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
Album {
|
Album {
|
||||||
|
meta: AlbumMeta {
|
||||||
id: AlbumId {
|
id: AlbumId {
|
||||||
title: "album_title b.c".to_string(),
|
title: "album_title b.c".to_string(),
|
||||||
},
|
},
|
||||||
@ -227,6 +240,7 @@ macro_rules! full_collection {
|
|||||||
).unwrap()),
|
).unwrap()),
|
||||||
primary_type: Some(AlbumPrimaryType::Album),
|
primary_type: Some(AlbumPrimaryType::Album),
|
||||||
secondary_types: vec![],
|
secondary_types: vec![],
|
||||||
|
},
|
||||||
tracks: vec![
|
tracks: vec![
|
||||||
Track {
|
Track {
|
||||||
id: TrackId {
|
id: TrackId {
|
||||||
@ -256,6 +270,7 @@ macro_rules! full_collection {
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
Album {
|
Album {
|
||||||
|
meta: AlbumMeta {
|
||||||
id: AlbumId {
|
id: AlbumId {
|
||||||
title: "album_title b.d".to_string(),
|
title: "album_title b.d".to_string(),
|
||||||
},
|
},
|
||||||
@ -264,6 +279,7 @@ macro_rules! full_collection {
|
|||||||
musicbrainz: None,
|
musicbrainz: None,
|
||||||
primary_type: Some(AlbumPrimaryType::Album),
|
primary_type: Some(AlbumPrimaryType::Album),
|
||||||
secondary_types: vec![],
|
secondary_types: vec![],
|
||||||
|
},
|
||||||
tracks: vec![
|
tracks: vec![
|
||||||
Track {
|
Track {
|
||||||
id: TrackId {
|
id: TrackId {
|
||||||
@ -295,6 +311,7 @@ macro_rules! full_collection {
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
Artist {
|
Artist {
|
||||||
|
meta: ArtistMeta {
|
||||||
id: ArtistId {
|
id: ArtistId {
|
||||||
name: "The Album_Artist ‘C’".to_string(),
|
name: "The Album_Artist ‘C’".to_string(),
|
||||||
},
|
},
|
||||||
@ -305,8 +322,10 @@ macro_rules! full_collection {
|
|||||||
"https://musicbrainz.org/artist/11111111-1111-1111-1111-111111111111"
|
"https://musicbrainz.org/artist/11111111-1111-1111-1111-111111111111"
|
||||||
).unwrap()),
|
).unwrap()),
|
||||||
properties: HashMap::new(),
|
properties: HashMap::new(),
|
||||||
|
},
|
||||||
albums: vec![
|
albums: vec![
|
||||||
Album {
|
Album {
|
||||||
|
meta: AlbumMeta {
|
||||||
id: AlbumId {
|
id: AlbumId {
|
||||||
title: "album_title c.a".to_string(),
|
title: "album_title c.a".to_string(),
|
||||||
},
|
},
|
||||||
@ -315,6 +334,7 @@ macro_rules! full_collection {
|
|||||||
musicbrainz: None,
|
musicbrainz: None,
|
||||||
primary_type: Some(AlbumPrimaryType::Album),
|
primary_type: Some(AlbumPrimaryType::Album),
|
||||||
secondary_types: vec![],
|
secondary_types: vec![],
|
||||||
|
},
|
||||||
tracks: vec![
|
tracks: vec![
|
||||||
Track {
|
Track {
|
||||||
id: TrackId {
|
id: TrackId {
|
||||||
@ -344,6 +364,7 @@ macro_rules! full_collection {
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
Album {
|
Album {
|
||||||
|
meta: AlbumMeta {
|
||||||
id: AlbumId {
|
id: AlbumId {
|
||||||
title: "album_title c.b".to_string(),
|
title: "album_title c.b".to_string(),
|
||||||
},
|
},
|
||||||
@ -352,6 +373,7 @@ macro_rules! full_collection {
|
|||||||
musicbrainz: None,
|
musicbrainz: None,
|
||||||
primary_type: Some(AlbumPrimaryType::Album),
|
primary_type: Some(AlbumPrimaryType::Album),
|
||||||
secondary_types: vec![],
|
secondary_types: vec![],
|
||||||
|
},
|
||||||
tracks: vec![
|
tracks: vec![
|
||||||
Track {
|
Track {
|
||||||
id: TrackId {
|
id: TrackId {
|
||||||
@ -383,14 +405,17 @@ macro_rules! full_collection {
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
Artist {
|
Artist {
|
||||||
|
meta: ArtistMeta {
|
||||||
id: ArtistId {
|
id: ArtistId {
|
||||||
name: "Album_Artist ‘D’".to_string(),
|
name: "Album_Artist ‘D’".to_string(),
|
||||||
},
|
},
|
||||||
sort: None,
|
sort: None,
|
||||||
musicbrainz: None,
|
musicbrainz: None,
|
||||||
properties: HashMap::new(),
|
properties: HashMap::new(),
|
||||||
|
},
|
||||||
albums: vec![
|
albums: vec![
|
||||||
Album {
|
Album {
|
||||||
|
meta: AlbumMeta {
|
||||||
id: AlbumId {
|
id: AlbumId {
|
||||||
title: "album_title d.a".to_string(),
|
title: "album_title d.a".to_string(),
|
||||||
},
|
},
|
||||||
@ -399,6 +424,7 @@ macro_rules! full_collection {
|
|||||||
musicbrainz: None,
|
musicbrainz: None,
|
||||||
primary_type: Some(AlbumPrimaryType::Album),
|
primary_type: Some(AlbumPrimaryType::Album),
|
||||||
secondary_types: vec![],
|
secondary_types: vec![],
|
||||||
|
},
|
||||||
tracks: vec![
|
tracks: vec![
|
||||||
Track {
|
Track {
|
||||||
id: TrackId {
|
id: TrackId {
|
||||||
@ -428,6 +454,7 @@ macro_rules! full_collection {
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
Album {
|
Album {
|
||||||
|
meta: AlbumMeta {
|
||||||
id: AlbumId {
|
id: AlbumId {
|
||||||
title: "album_title d.b".to_string(),
|
title: "album_title d.b".to_string(),
|
||||||
},
|
},
|
||||||
@ -436,6 +463,7 @@ macro_rules! full_collection {
|
|||||||
musicbrainz: None,
|
musicbrainz: None,
|
||||||
primary_type: Some(AlbumPrimaryType::Album),
|
primary_type: Some(AlbumPrimaryType::Album),
|
||||||
secondary_types: vec![],
|
secondary_types: vec![],
|
||||||
|
},
|
||||||
tracks: vec![
|
tracks: vec![
|
||||||
Track {
|
Track {
|
||||||
id: TrackId {
|
id: TrackId {
|
||||||
|
@ -3,14 +3,17 @@ macro_rules! library_collection {
|
|||||||
() => {
|
() => {
|
||||||
vec![
|
vec![
|
||||||
Artist {
|
Artist {
|
||||||
|
meta: ArtistMeta {
|
||||||
id: ArtistId {
|
id: ArtistId {
|
||||||
name: "Album_Artist ‘A’".to_string(),
|
name: "Album_Artist ‘A’".to_string(),
|
||||||
},
|
},
|
||||||
sort: None,
|
sort: None,
|
||||||
musicbrainz: None,
|
musicbrainz: None,
|
||||||
properties: HashMap::new(),
|
properties: HashMap::new(),
|
||||||
|
},
|
||||||
albums: vec![
|
albums: vec![
|
||||||
Album {
|
Album {
|
||||||
|
meta: AlbumMeta {
|
||||||
id: AlbumId {
|
id: AlbumId {
|
||||||
title: "album_title a.a".to_string(),
|
title: "album_title a.a".to_string(),
|
||||||
},
|
},
|
||||||
@ -19,6 +22,7 @@ macro_rules! library_collection {
|
|||||||
musicbrainz: None,
|
musicbrainz: None,
|
||||||
primary_type: None,
|
primary_type: None,
|
||||||
secondary_types: vec![],
|
secondary_types: vec![],
|
||||||
|
},
|
||||||
tracks: vec![
|
tracks: vec![
|
||||||
Track {
|
Track {
|
||||||
id: TrackId {
|
id: TrackId {
|
||||||
@ -70,6 +74,7 @@ macro_rules! library_collection {
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
Album {
|
Album {
|
||||||
|
meta: AlbumMeta {
|
||||||
id: AlbumId {
|
id: AlbumId {
|
||||||
title: "album_title a.b".to_string(),
|
title: "album_title a.b".to_string(),
|
||||||
},
|
},
|
||||||
@ -78,6 +83,7 @@ macro_rules! library_collection {
|
|||||||
musicbrainz: None,
|
musicbrainz: None,
|
||||||
primary_type: None,
|
primary_type: None,
|
||||||
secondary_types: vec![],
|
secondary_types: vec![],
|
||||||
|
},
|
||||||
tracks: vec![
|
tracks: vec![
|
||||||
Track {
|
Track {
|
||||||
id: TrackId {
|
id: TrackId {
|
||||||
@ -106,14 +112,17 @@ macro_rules! library_collection {
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
Artist {
|
Artist {
|
||||||
|
meta: ArtistMeta {
|
||||||
id: ArtistId {
|
id: ArtistId {
|
||||||
name: "Album_Artist ‘B’".to_string(),
|
name: "Album_Artist ‘B’".to_string(),
|
||||||
},
|
},
|
||||||
sort: None,
|
sort: None,
|
||||||
musicbrainz: None,
|
musicbrainz: None,
|
||||||
properties: HashMap::new(),
|
properties: HashMap::new(),
|
||||||
|
},
|
||||||
albums: vec![
|
albums: vec![
|
||||||
Album {
|
Album {
|
||||||
|
meta: AlbumMeta {
|
||||||
id: AlbumId {
|
id: AlbumId {
|
||||||
title: "album_title b.a".to_string(),
|
title: "album_title b.a".to_string(),
|
||||||
},
|
},
|
||||||
@ -122,6 +131,7 @@ macro_rules! library_collection {
|
|||||||
musicbrainz: None,
|
musicbrainz: None,
|
||||||
primary_type: None,
|
primary_type: None,
|
||||||
secondary_types: vec![],
|
secondary_types: vec![],
|
||||||
|
},
|
||||||
tracks: vec![
|
tracks: vec![
|
||||||
Track {
|
Track {
|
||||||
id: TrackId {
|
id: TrackId {
|
||||||
@ -151,6 +161,7 @@ macro_rules! library_collection {
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
Album {
|
Album {
|
||||||
|
meta: AlbumMeta {
|
||||||
id: AlbumId {
|
id: AlbumId {
|
||||||
title: "album_title b.b".to_string(),
|
title: "album_title b.b".to_string(),
|
||||||
},
|
},
|
||||||
@ -159,6 +170,7 @@ macro_rules! library_collection {
|
|||||||
musicbrainz: None,
|
musicbrainz: None,
|
||||||
primary_type: None,
|
primary_type: None,
|
||||||
secondary_types: vec![],
|
secondary_types: vec![],
|
||||||
|
},
|
||||||
tracks: vec![
|
tracks: vec![
|
||||||
Track {
|
Track {
|
||||||
id: TrackId {
|
id: TrackId {
|
||||||
@ -188,6 +200,7 @@ macro_rules! library_collection {
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
Album {
|
Album {
|
||||||
|
meta: AlbumMeta {
|
||||||
id: AlbumId {
|
id: AlbumId {
|
||||||
title: "album_title b.c".to_string(),
|
title: "album_title b.c".to_string(),
|
||||||
},
|
},
|
||||||
@ -196,6 +209,7 @@ macro_rules! library_collection {
|
|||||||
musicbrainz: None,
|
musicbrainz: None,
|
||||||
primary_type: None,
|
primary_type: None,
|
||||||
secondary_types: vec![],
|
secondary_types: vec![],
|
||||||
|
},
|
||||||
tracks: vec![
|
tracks: vec![
|
||||||
Track {
|
Track {
|
||||||
id: TrackId {
|
id: TrackId {
|
||||||
@ -225,6 +239,7 @@ macro_rules! library_collection {
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
Album {
|
Album {
|
||||||
|
meta: AlbumMeta {
|
||||||
id: AlbumId {
|
id: AlbumId {
|
||||||
title: "album_title b.d".to_string(),
|
title: "album_title b.d".to_string(),
|
||||||
},
|
},
|
||||||
@ -233,6 +248,7 @@ macro_rules! library_collection {
|
|||||||
musicbrainz: None,
|
musicbrainz: None,
|
||||||
primary_type: None,
|
primary_type: None,
|
||||||
secondary_types: vec![],
|
secondary_types: vec![],
|
||||||
|
},
|
||||||
tracks: vec![
|
tracks: vec![
|
||||||
Track {
|
Track {
|
||||||
id: TrackId {
|
id: TrackId {
|
||||||
@ -264,6 +280,7 @@ macro_rules! library_collection {
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
Artist {
|
Artist {
|
||||||
|
meta: ArtistMeta {
|
||||||
id: ArtistId {
|
id: ArtistId {
|
||||||
name: "The Album_Artist ‘C’".to_string(),
|
name: "The Album_Artist ‘C’".to_string(),
|
||||||
},
|
},
|
||||||
@ -272,8 +289,10 @@ macro_rules! library_collection {
|
|||||||
}),
|
}),
|
||||||
musicbrainz: None,
|
musicbrainz: None,
|
||||||
properties: HashMap::new(),
|
properties: HashMap::new(),
|
||||||
|
},
|
||||||
albums: vec![
|
albums: vec![
|
||||||
Album {
|
Album {
|
||||||
|
meta: AlbumMeta {
|
||||||
id: AlbumId {
|
id: AlbumId {
|
||||||
title: "album_title c.a".to_string(),
|
title: "album_title c.a".to_string(),
|
||||||
},
|
},
|
||||||
@ -282,6 +301,7 @@ macro_rules! library_collection {
|
|||||||
musicbrainz: None,
|
musicbrainz: None,
|
||||||
primary_type: None,
|
primary_type: None,
|
||||||
secondary_types: vec![],
|
secondary_types: vec![],
|
||||||
|
},
|
||||||
tracks: vec![
|
tracks: vec![
|
||||||
Track {
|
Track {
|
||||||
id: TrackId {
|
id: TrackId {
|
||||||
@ -311,6 +331,7 @@ macro_rules! library_collection {
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
Album {
|
Album {
|
||||||
|
meta: AlbumMeta {
|
||||||
id: AlbumId {
|
id: AlbumId {
|
||||||
title: "album_title c.b".to_string(),
|
title: "album_title c.b".to_string(),
|
||||||
},
|
},
|
||||||
@ -319,6 +340,7 @@ macro_rules! library_collection {
|
|||||||
musicbrainz: None,
|
musicbrainz: None,
|
||||||
primary_type: None,
|
primary_type: None,
|
||||||
secondary_types: vec![],
|
secondary_types: vec![],
|
||||||
|
},
|
||||||
tracks: vec![
|
tracks: vec![
|
||||||
Track {
|
Track {
|
||||||
id: TrackId {
|
id: TrackId {
|
||||||
@ -350,14 +372,17 @@ macro_rules! library_collection {
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
Artist {
|
Artist {
|
||||||
|
meta: ArtistMeta {
|
||||||
id: ArtistId {
|
id: ArtistId {
|
||||||
name: "Album_Artist ‘D’".to_string(),
|
name: "Album_Artist ‘D’".to_string(),
|
||||||
},
|
},
|
||||||
sort: None,
|
sort: None,
|
||||||
musicbrainz: None,
|
musicbrainz: None,
|
||||||
properties: HashMap::new(),
|
properties: HashMap::new(),
|
||||||
|
},
|
||||||
albums: vec![
|
albums: vec![
|
||||||
Album {
|
Album {
|
||||||
|
meta: AlbumMeta {
|
||||||
id: AlbumId {
|
id: AlbumId {
|
||||||
title: "album_title d.a".to_string(),
|
title: "album_title d.a".to_string(),
|
||||||
},
|
},
|
||||||
@ -366,6 +391,7 @@ macro_rules! library_collection {
|
|||||||
musicbrainz: None,
|
musicbrainz: None,
|
||||||
primary_type: None,
|
primary_type: None,
|
||||||
secondary_types: vec![],
|
secondary_types: vec![],
|
||||||
|
},
|
||||||
tracks: vec![
|
tracks: vec![
|
||||||
Track {
|
Track {
|
||||||
id: TrackId {
|
id: TrackId {
|
||||||
@ -395,6 +421,7 @@ macro_rules! library_collection {
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
Album {
|
Album {
|
||||||
|
meta: AlbumMeta {
|
||||||
id: AlbumId {
|
id: AlbumId {
|
||||||
title: "album_title d.b".to_string(),
|
title: "album_title d.b".to_string(),
|
||||||
},
|
},
|
||||||
@ -403,6 +430,7 @@ macro_rules! library_collection {
|
|||||||
musicbrainz: None,
|
musicbrainz: None,
|
||||||
primary_type: None,
|
primary_type: None,
|
||||||
secondary_types: vec![],
|
secondary_types: vec![],
|
||||||
|
},
|
||||||
tracks: vec![
|
tracks: vec![
|
||||||
Track {
|
Track {
|
||||||
id: TrackId {
|
id: TrackId {
|
||||||
|
@ -93,19 +93,23 @@ impl IAppInteractBrowse for AppMachine<AppBrowse> {
|
|||||||
|
|
||||||
let (matches_tx, matches_rx) = mpsc::channel::<AppMatchesInfo>();
|
let (matches_tx, matches_rx) = mpsc::channel::<AppMatchesInfo>();
|
||||||
|
|
||||||
match artist.musicbrainz {
|
match artist.meta.musicbrainz {
|
||||||
Some(ref mbid) => {
|
Some(ref mbid) => {
|
||||||
let arid = mbid.mbid();
|
let arid = mbid.mbid();
|
||||||
|
|
||||||
let mut album_iter = artist.albums.iter().peekable();
|
let mut album_iter = artist.albums.iter().peekable();
|
||||||
while let Some(album) = album_iter.next() {
|
while let Some(album) = album_iter.next() {
|
||||||
if album.musicbrainz.is_some() {
|
if album.meta.musicbrainz.is_some() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
match self.inner.musicbrainz.search_release_group(arid, album) {
|
match self
|
||||||
|
.inner
|
||||||
|
.musicbrainz
|
||||||
|
.search_release_group(arid, &album.meta)
|
||||||
|
{
|
||||||
Ok(list) => matches_tx
|
Ok(list) => matches_tx
|
||||||
.send(AppMatchesInfo::album(album.clone(), list))
|
.send(AppMatchesInfo::album(album.meta.clone(), list))
|
||||||
.expect("send fails only if receiver is disconnected"),
|
.expect("send fails only if receiver is disconnected"),
|
||||||
Err(err) => return AppMachine::error(self.inner, err.to_string()).into(),
|
Err(err) => return AppMachine::error(self.inner, err.to_string()).into(),
|
||||||
}
|
}
|
||||||
@ -115,9 +119,9 @@ impl IAppInteractBrowse for AppMachine<AppBrowse> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None => match self.inner.musicbrainz.search_artist(artist) {
|
None => match self.inner.musicbrainz.search_artist(&artist.meta) {
|
||||||
Ok(list) => matches_tx
|
Ok(list) => matches_tx
|
||||||
.send(AppMatchesInfo::artist(artist.clone(), list))
|
.send(AppMatchesInfo::artist(artist.meta.clone(), list))
|
||||||
.expect("send fails only if receiver is disconnected"),
|
.expect("send fails only if receiver is disconnected"),
|
||||||
Err(err) => return AppMachine::error(self.inner, err.to_string()).into(),
|
Err(err) => return AppMachine::error(self.inner, err.to_string()).into(),
|
||||||
},
|
},
|
||||||
@ -134,7 +138,7 @@ impl IAppInteractBrowse for AppMachine<AppBrowse> {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use mockall::{predicate, Sequence};
|
use mockall::{predicate, Sequence};
|
||||||
use musichoard::collection::{album::Album, artist::Artist, musicbrainz::Mbid};
|
use musichoard::collection::{album::AlbumMeta, artist::ArtistMeta, musicbrainz::Mbid};
|
||||||
|
|
||||||
use crate::tui::{
|
use crate::tui::{
|
||||||
app::{
|
app::{
|
||||||
@ -223,8 +227,8 @@ mod tests {
|
|||||||
let mut mb_api = Box::new(MockIMusicBrainz::new());
|
let mut mb_api = Box::new(MockIMusicBrainz::new());
|
||||||
|
|
||||||
let arid: Mbid = "11111111-1111-1111-1111-111111111111".try_into().unwrap();
|
let arid: Mbid = "11111111-1111-1111-1111-111111111111".try_into().unwrap();
|
||||||
let album_1 = COLLECTION[1].albums[0].clone();
|
let album_1 = COLLECTION[1].albums[0].meta.clone();
|
||||||
let album_4 = COLLECTION[1].albums[3].clone();
|
let album_4 = COLLECTION[1].albums[3].meta.clone();
|
||||||
|
|
||||||
let album_match_1_1 = Match::new(100, album_1.clone());
|
let album_match_1_1 = Match::new(100, album_1.clone());
|
||||||
let album_match_1_2 = Match::new(50, album_4.clone());
|
let album_match_1_2 = Match::new(50, album_4.clone());
|
||||||
@ -233,8 +237,8 @@ mod tests {
|
|||||||
let matches_1 = vec![album_match_1_1.clone(), album_match_1_2.clone()];
|
let matches_1 = vec![album_match_1_1.clone(), album_match_1_2.clone()];
|
||||||
let matches_4 = vec![album_match_4_1.clone(), album_match_4_2.clone()];
|
let matches_4 = vec![album_match_4_1.clone(), album_match_4_2.clone()];
|
||||||
|
|
||||||
let result_1: Result<Vec<Match<Album>>, musicbrainz::Error> = Ok(matches_1.clone());
|
let result_1: Result<Vec<Match<AlbumMeta>>, musicbrainz::Error> = Ok(matches_1.clone());
|
||||||
let result_4: Result<Vec<Match<Album>>, musicbrainz::Error> = Ok(matches_4.clone());
|
let result_4: Result<Vec<Match<AlbumMeta>>, musicbrainz::Error> = Ok(matches_4.clone());
|
||||||
|
|
||||||
// Other albums have an MBID and so they will be skipped.
|
// Other albums have an MBID and so they will be skipped.
|
||||||
let mut seq = Sequence::new();
|
let mut seq = Sequence::new();
|
||||||
@ -263,7 +267,7 @@ mod tests {
|
|||||||
|
|
||||||
let public_matches = public.state.unwrap_matches();
|
let public_matches = public.state.unwrap_matches();
|
||||||
|
|
||||||
let mut matches_1: Vec<MatchOption<Album>> =
|
let mut matches_1: Vec<MatchOption<AlbumMeta>> =
|
||||||
matches_1.into_iter().map(Into::into).collect();
|
matches_1.into_iter().map(Into::into).collect();
|
||||||
matches_1.push(MatchOption::CannotHaveMbid);
|
matches_1.push(MatchOption::CannotHaveMbid);
|
||||||
let expected = Some(AppMatchesInfo::Album(AppAlbumMatches {
|
let expected = Some(AppMatchesInfo::Album(AppAlbumMatches {
|
||||||
@ -279,7 +283,7 @@ mod tests {
|
|||||||
|
|
||||||
let public_matches = public.state.unwrap_matches();
|
let public_matches = public.state.unwrap_matches();
|
||||||
|
|
||||||
let mut matches_4: Vec<MatchOption<Album>> =
|
let mut matches_4: Vec<MatchOption<AlbumMeta>> =
|
||||||
matches_4.into_iter().map(Into::into).collect();
|
matches_4.into_iter().map(Into::into).collect();
|
||||||
matches_4.push(MatchOption::CannotHaveMbid);
|
matches_4.push(MatchOption::CannotHaveMbid);
|
||||||
let expected = Some(AppMatchesInfo::Album(AppAlbumMatches {
|
let expected = Some(AppMatchesInfo::Album(AppAlbumMatches {
|
||||||
@ -303,13 +307,13 @@ mod tests {
|
|||||||
fn fetch_musicbrainz_no_artist_mbid() {
|
fn fetch_musicbrainz_no_artist_mbid() {
|
||||||
let mut mb_api = Box::new(MockIMusicBrainz::new());
|
let mut mb_api = Box::new(MockIMusicBrainz::new());
|
||||||
|
|
||||||
let artist = COLLECTION[3].clone();
|
let artist = COLLECTION[3].meta.clone();
|
||||||
|
|
||||||
let artist_match_1 = Match::new(100, artist.clone());
|
let artist_match_1 = Match::new(100, artist.clone());
|
||||||
let artist_match_2 = Match::new(50, artist.clone());
|
let artist_match_2 = Match::new(50, artist.clone());
|
||||||
let matches = vec![artist_match_1.clone(), artist_match_2.clone()];
|
let matches = vec![artist_match_1.clone(), artist_match_2.clone()];
|
||||||
|
|
||||||
let result: Result<Vec<Match<Artist>>, musicbrainz::Error> = Ok(matches.clone());
|
let result: Result<Vec<Match<ArtistMeta>>, musicbrainz::Error> = Ok(matches.clone());
|
||||||
|
|
||||||
mb_api
|
mb_api
|
||||||
.expect_search_artist()
|
.expect_search_artist()
|
||||||
@ -330,7 +334,8 @@ mod tests {
|
|||||||
|
|
||||||
let public_matches = public.state.unwrap_matches();
|
let public_matches = public.state.unwrap_matches();
|
||||||
|
|
||||||
let mut matches: Vec<MatchOption<Artist>> = matches.into_iter().map(Into::into).collect();
|
let mut matches: Vec<MatchOption<ArtistMeta>> =
|
||||||
|
matches.into_iter().map(Into::into).collect();
|
||||||
matches.push(MatchOption::CannotHaveMbid);
|
matches.push(MatchOption::CannotHaveMbid);
|
||||||
let expected = Some(AppMatchesInfo::Artist(AppArtistMatches {
|
let expected = Some(AppMatchesInfo::Artist(AppArtistMatches {
|
||||||
matching: artist.clone(),
|
matching: artist.clone(),
|
||||||
|
@ -144,8 +144,8 @@ impl IAppInteractMatchesPrivate for AppMatches {
|
|||||||
mod tests {
|
mod tests {
|
||||||
use mpsc::Receiver;
|
use mpsc::Receiver;
|
||||||
use musichoard::collection::{
|
use musichoard::collection::{
|
||||||
album::{Album, AlbumDate, AlbumId, AlbumPrimaryType, AlbumSecondaryType},
|
album::{AlbumDate, AlbumId, AlbumMeta, AlbumPrimaryType, AlbumSecondaryType},
|
||||||
artist::{Artist, ArtistId},
|
artist::{ArtistId, ArtistMeta},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::tui::{
|
use crate::tui::{
|
||||||
@ -158,20 +158,30 @@ mod tests {
|
|||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
impl<T> Match<T> {
|
||||||
|
pub fn new(score: u8, item: T) -> Self {
|
||||||
|
Match {
|
||||||
|
score,
|
||||||
|
item,
|
||||||
|
disambiguation: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn artist_matches_info_vec() -> Vec<AppMatchesInfo> {
|
fn artist_matches_info_vec() -> Vec<AppMatchesInfo> {
|
||||||
let artist_1 = Artist::new(ArtistId::new("Artist 1"));
|
let artist_1 = ArtistMeta::new(ArtistId::new("Artist 1"));
|
||||||
|
|
||||||
let artist_1_1 = artist_1.clone();
|
let artist_1_1 = artist_1.clone();
|
||||||
let artist_match_1_1 = Match::new(100, artist_1_1);
|
let artist_match_1_1 = Match::new(100, artist_1_1);
|
||||||
|
|
||||||
let artist_1_2 = artist_1.clone();
|
let artist_1_2 = artist_1.clone();
|
||||||
let mut artist_match_1_2 = Match::new(100, artist_1_2);
|
let mut artist_match_1_2 = Match::new(100, artist_1_2);
|
||||||
artist_match_1_2.set_disambiguation("some disambiguation");
|
artist_match_1_2.disambiguation = Some(String::from("some disambiguation"));
|
||||||
|
|
||||||
let list = vec![artist_match_1_1.clone(), artist_match_1_2.clone()];
|
let list = vec![artist_match_1_1.clone(), artist_match_1_2.clone()];
|
||||||
let matches_info_1 = AppMatchesInfo::artist(artist_1.clone(), list);
|
let matches_info_1 = AppMatchesInfo::artist(artist_1.clone(), list);
|
||||||
|
|
||||||
let artist_2 = Artist::new(ArtistId::new("Artist 2"));
|
let artist_2 = ArtistMeta::new(ArtistId::new("Artist 2"));
|
||||||
|
|
||||||
let artist_2_1 = artist_1.clone();
|
let artist_2_1 = artist_1.clone();
|
||||||
let album_match_2_1 = Match::new(100, artist_2_1);
|
let album_match_2_1 = Match::new(100, artist_2_1);
|
||||||
@ -183,7 +193,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn album_matches_info_vec() -> Vec<AppMatchesInfo> {
|
fn album_matches_info_vec() -> Vec<AppMatchesInfo> {
|
||||||
let album_1 = Album::new(
|
let album_1 = AlbumMeta::new(
|
||||||
AlbumId::new("Album 1"),
|
AlbumId::new("Album 1"),
|
||||||
AlbumDate::new(Some(1990), Some(5), None),
|
AlbumDate::new(Some(1990), Some(5), None),
|
||||||
Some(AlbumPrimaryType::Album),
|
Some(AlbumPrimaryType::Album),
|
||||||
@ -201,7 +211,7 @@ mod tests {
|
|||||||
let list = vec![album_match_1_1.clone(), album_match_1_2.clone()];
|
let list = vec![album_match_1_1.clone(), album_match_1_2.clone()];
|
||||||
let matches_info_1 = AppMatchesInfo::album(album_1.clone(), list);
|
let matches_info_1 = AppMatchesInfo::album(album_1.clone(), list);
|
||||||
|
|
||||||
let album_2 = Album::new(
|
let album_2 = AlbumMeta::new(
|
||||||
AlbumId::new("Album 2"),
|
AlbumId::new("Album 2"),
|
||||||
AlbumDate::new(Some(2001), None, None),
|
AlbumDate::new(Some(2001), None, None),
|
||||||
Some(AlbumPrimaryType::Album),
|
Some(AlbumPrimaryType::Album),
|
||||||
|
@ -181,10 +181,10 @@ impl IAppInteractSearchPrivate for AppMachine<AppSearch> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn predicate_artists(case_sens: bool, char_sens: bool, search: &str, probe: &Artist) -> bool {
|
fn predicate_artists(case_sens: bool, char_sens: bool, search: &str, probe: &Artist) -> bool {
|
||||||
let name = Self::normalize_search(&probe.id.name, !case_sens, !char_sens);
|
let name = Self::normalize_search(&probe.meta.id.name, !case_sens, !char_sens);
|
||||||
let mut result = name.starts_with(search);
|
let mut result = name.starts_with(search);
|
||||||
|
|
||||||
if let Some(ref probe_sort) = probe.sort {
|
if let Some(ref probe_sort) = probe.meta.sort {
|
||||||
if !result {
|
if !result {
|
||||||
let name = Self::normalize_search(&probe_sort.name, !case_sens, !char_sens);
|
let name = Self::normalize_search(&probe_sort.name, !case_sens, !char_sens);
|
||||||
result = name.starts_with(search);
|
result = name.starts_with(search);
|
||||||
@ -195,7 +195,7 @@ impl IAppInteractSearchPrivate for AppMachine<AppSearch> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn predicate_albums(case_sens: bool, char_sens: bool, search: &str, probe: &Album) -> bool {
|
fn predicate_albums(case_sens: bool, char_sens: bool, search: &str, probe: &Album) -> bool {
|
||||||
Self::predicate_title(case_sens, char_sens, search, &probe.id.title)
|
Self::predicate_title(case_sens, char_sens, search, &probe.meta.id.title)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn predicate_tracks(case_sens: bool, char_sens: bool, search: &str, probe: &Track) -> bool {
|
fn predicate_tracks(case_sens: bool, char_sens: bool, search: &str, probe: &Track) -> bool {
|
||||||
|
@ -4,7 +4,7 @@ mod selection;
|
|||||||
pub use machine::App;
|
pub use machine::App;
|
||||||
pub use selection::{Category, Delta, Selection, WidgetState};
|
pub use selection::{Category, Delta, Selection, WidgetState};
|
||||||
|
|
||||||
use musichoard::collection::{album::Album, artist::Artist, Collection};
|
use musichoard::collection::{album::AlbumMeta, artist::ArtistMeta, Collection};
|
||||||
|
|
||||||
use crate::tui::lib::interface::musicbrainz::Match;
|
use crate::tui::lib::interface::musicbrainz::Match;
|
||||||
|
|
||||||
@ -143,14 +143,14 @@ impl<T> From<Match<T>> for MatchOption<T> {
|
|||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
pub struct AppArtistMatches {
|
pub struct AppArtistMatches {
|
||||||
pub matching: Artist,
|
pub matching: ArtistMeta,
|
||||||
pub list: Vec<MatchOption<Artist>>,
|
pub list: Vec<MatchOption<ArtistMeta>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
pub struct AppAlbumMatches {
|
pub struct AppAlbumMatches {
|
||||||
pub matching: Album,
|
pub matching: AlbumMeta,
|
||||||
pub list: Vec<MatchOption<Album>>,
|
pub list: Vec<MatchOption<AlbumMeta>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
@ -160,13 +160,13 @@ pub enum AppMatchesInfo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl AppMatchesInfo {
|
impl AppMatchesInfo {
|
||||||
pub fn artist<M: Into<MatchOption<Artist>>>(matching: Artist, list: Vec<M>) -> Self {
|
pub fn artist<M: Into<MatchOption<ArtistMeta>>>(matching: ArtistMeta, list: Vec<M>) -> Self {
|
||||||
let list: Vec<MatchOption<Artist>> = list.into_iter().map(Into::into).collect();
|
let list: Vec<MatchOption<ArtistMeta>> = list.into_iter().map(Into::into).collect();
|
||||||
AppMatchesInfo::Artist(AppArtistMatches { matching, list })
|
AppMatchesInfo::Artist(AppArtistMatches { matching, list })
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn album<M: Into<MatchOption<Album>>>(matching: Album, list: Vec<M>) -> Self {
|
pub fn album<M: Into<MatchOption<AlbumMeta>>>(matching: AlbumMeta, list: Vec<M>) -> Self {
|
||||||
let list: Vec<MatchOption<Album>> = list.into_iter().map(Into::into).collect();
|
let list: Vec<MatchOption<AlbumMeta>> = list.into_iter().map(Into::into).collect();
|
||||||
AppMatchesInfo::Album(AppAlbumMatches { matching, list })
|
AppMatchesInfo::Album(AppAlbumMatches { matching, list })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,7 +28,8 @@ impl AlbumSelection {
|
|||||||
|
|
||||||
pub fn reinitialise(&mut self, albums: &[Album], album: Option<KeySelectAlbum>) {
|
pub fn reinitialise(&mut self, albums: &[Album], album: Option<KeySelectAlbum>) {
|
||||||
if let Some(album) = album {
|
if let Some(album) = album {
|
||||||
let result = albums.binary_search_by(|a| a.get_sort_key().cmp(&album.get_sort_key()));
|
let result =
|
||||||
|
albums.binary_search_by(|a| a.meta.get_sort_key().cmp(&album.get_sort_key()));
|
||||||
match result {
|
match result {
|
||||||
Ok(index) => self.reinitialise_with_index(albums, index, album.track),
|
Ok(index) => self.reinitialise_with_index(albums, index, album.track),
|
||||||
Err(index) => self.reinitialise_with_index(albums, index, None),
|
Err(index) => self.reinitialise_with_index(albums, index, None),
|
||||||
@ -169,7 +170,7 @@ impl KeySelectAlbum {
|
|||||||
pub fn get(albums: &[Album], selection: &AlbumSelection) -> Option<Self> {
|
pub fn get(albums: &[Album], selection: &AlbumSelection) -> Option<Self> {
|
||||||
selection.state.list.selected().map(|index| {
|
selection.state.list.selected().map(|index| {
|
||||||
let album = &albums[index];
|
let album = &albums[index];
|
||||||
let key = album.get_sort_key();
|
let key = album.meta.get_sort_key();
|
||||||
KeySelectAlbum {
|
KeySelectAlbum {
|
||||||
key: (key.0.to_owned(), key.1.to_owned(), key.2.to_owned()),
|
key: (key.0.to_owned(), key.1.to_owned(), key.2.to_owned()),
|
||||||
track: KeySelectTrack::get(&album.tracks, &selection.track),
|
track: KeySelectTrack::get(&album.tracks, &selection.track),
|
||||||
|
@ -29,7 +29,8 @@ impl ArtistSelection {
|
|||||||
|
|
||||||
pub fn reinitialise(&mut self, artists: &[Artist], active: Option<KeySelectArtist>) {
|
pub fn reinitialise(&mut self, artists: &[Artist], active: Option<KeySelectArtist>) {
|
||||||
if let Some(active) = active {
|
if let Some(active) = active {
|
||||||
let result = artists.binary_search_by(|a| a.get_sort_key().cmp(&active.get_sort_key()));
|
let result =
|
||||||
|
artists.binary_search_by(|a| a.meta.get_sort_key().cmp(&active.get_sort_key()));
|
||||||
match result {
|
match result {
|
||||||
Ok(index) => self.reinitialise_with_index(artists, index, active.album),
|
Ok(index) => self.reinitialise_with_index(artists, index, active.album),
|
||||||
Err(index) => self.reinitialise_with_index(artists, index, None),
|
Err(index) => self.reinitialise_with_index(artists, index, None),
|
||||||
@ -202,7 +203,7 @@ impl KeySelectArtist {
|
|||||||
pub fn get(artists: &[Artist], selection: &ArtistSelection) -> Option<Self> {
|
pub fn get(artists: &[Artist], selection: &ArtistSelection) -> Option<Self> {
|
||||||
selection.state.list.selected().map(|index| {
|
selection.state.list.selected().map(|index| {
|
||||||
let artist = &artists[index];
|
let artist = &artists[index];
|
||||||
let key = artist.get_sort_key();
|
let key = artist.meta.get_sort_key();
|
||||||
KeySelectArtist {
|
KeySelectArtist {
|
||||||
key: (key.0.to_owned(),),
|
key: (key.0.to_owned(),),
|
||||||
album: KeySelectAlbum::get(&artist.albums, &selection.album),
|
album: KeySelectAlbum::get(&artist.albums, &selection.album),
|
||||||
|
56
src/tui/lib/external/musicbrainz/mod.rs
vendored
56
src/tui/lib/external/musicbrainz/mod.rs
vendored
@ -1,9 +1,11 @@
|
|||||||
//! Module for interacting with the [MusicBrainz API](https://musicbrainz.org/doc/MusicBrainz_API).
|
//! Module for interacting with the [MusicBrainz API](https://musicbrainz.org/doc/MusicBrainz_API).
|
||||||
|
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use musichoard::{
|
use musichoard::{
|
||||||
collection::{
|
collection::{
|
||||||
album::{Album, AlbumDate},
|
album::{AlbumDate, AlbumMeta, AlbumSeq},
|
||||||
artist::Artist,
|
artist::ArtistMeta,
|
||||||
musicbrainz::Mbid,
|
musicbrainz::Mbid,
|
||||||
},
|
},
|
||||||
external::musicbrainz::{
|
external::musicbrainz::{
|
||||||
@ -32,7 +34,7 @@ impl<Http> MusicBrainz<Http> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<Http: IMusicBrainzHttp> IMusicBrainz for MusicBrainz<Http> {
|
impl<Http: IMusicBrainzHttp> IMusicBrainz for MusicBrainz<Http> {
|
||||||
fn search_artist(&mut self, artist: &Artist) -> Result<Vec<Match<Artist>>, Error> {
|
fn search_artist(&mut self, artist: &ArtistMeta) -> Result<Vec<Match<ArtistMeta>>, Error> {
|
||||||
let query = SearchArtistRequest::new().string(&artist.id.name);
|
let query = SearchArtistRequest::new().string(&artist.id.name);
|
||||||
|
|
||||||
let mb_response = self.client.search_artist(query)?;
|
let mb_response = self.client.search_artist(query)?;
|
||||||
@ -47,8 +49,8 @@ impl<Http: IMusicBrainzHttp> IMusicBrainz for MusicBrainz<Http> {
|
|||||||
fn search_release_group(
|
fn search_release_group(
|
||||||
&mut self,
|
&mut self,
|
||||||
arid: &Mbid,
|
arid: &Mbid,
|
||||||
album: &Album,
|
album: &AlbumMeta,
|
||||||
) -> Result<Vec<Match<Album>>, Error> {
|
) -> Result<Vec<Match<AlbumMeta>>, Error> {
|
||||||
// Some release groups may have a promotional early release messing up the search. Searching
|
// Some release groups may have a promotional early release messing up the search. Searching
|
||||||
// with just the year should be enough anyway.
|
// with just the year should be enough anyway.
|
||||||
let date = AlbumDate::new(album.date.year, None, None);
|
let date = AlbumDate::new(album.date.year, None, None);
|
||||||
@ -70,31 +72,33 @@ impl<Http: IMusicBrainzHttp> IMusicBrainz for MusicBrainz<Http> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn from_search_artist_response_artist(entity: SearchArtistResponseArtist) -> Match<Artist> {
|
fn from_search_artist_response_artist(entity: SearchArtistResponseArtist) -> Match<ArtistMeta> {
|
||||||
let mut artist = Artist::new(entity.name);
|
Match {
|
||||||
if let Some(sort) = entity.sort {
|
score: entity.score,
|
||||||
artist.set_sort_key(sort);
|
item: ArtistMeta {
|
||||||
|
id: entity.name,
|
||||||
|
sort: entity.sort.map(Into::into),
|
||||||
|
musicbrainz: Some(entity.id.into()),
|
||||||
|
properties: HashMap::new(),
|
||||||
|
},
|
||||||
|
disambiguation: entity.disambiguation,
|
||||||
}
|
}
|
||||||
artist.set_musicbrainz_ref(entity.id.into());
|
|
||||||
|
|
||||||
let mut artist_match = Match::new(entity.score, artist);
|
|
||||||
if let Some(disambiguation) = entity.disambiguation {
|
|
||||||
artist_match.set_disambiguation(disambiguation);
|
|
||||||
}
|
|
||||||
|
|
||||||
artist_match
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn from_search_release_group_response_release_group(
|
fn from_search_release_group_response_release_group(
|
||||||
entity: SearchReleaseGroupResponseReleaseGroup,
|
entity: SearchReleaseGroupResponseReleaseGroup,
|
||||||
) -> Match<Album> {
|
) -> Match<AlbumMeta> {
|
||||||
let mut album = Album::new(
|
Match {
|
||||||
entity.title,
|
score: entity.score,
|
||||||
entity.first_release_date,
|
item: AlbumMeta {
|
||||||
Some(entity.primary_type),
|
id: entity.title,
|
||||||
entity.secondary_types.unwrap_or_default(),
|
date: entity.first_release_date,
|
||||||
);
|
seq: AlbumSeq::default(),
|
||||||
album.set_musicbrainz_ref(entity.id.into());
|
musicbrainz: Some(entity.id.into()),
|
||||||
Match::new(entity.score, album)
|
primary_type: Some(entity.primary_type),
|
||||||
|
secondary_types: entity.secondary_types.unwrap_or_default(),
|
||||||
|
},
|
||||||
|
disambiguation: None,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// GRCOV_EXCL_STOP
|
// GRCOV_EXCL_STOP
|
||||||
|
@ -3,17 +3,17 @@
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
use mockall::automock;
|
use mockall::automock;
|
||||||
|
|
||||||
use musichoard::collection::{album::Album, artist::Artist, musicbrainz::Mbid};
|
use musichoard::collection::{album::AlbumMeta, artist::ArtistMeta, musicbrainz::Mbid};
|
||||||
|
|
||||||
/// Trait for interacting with the MusicBrainz API.
|
/// Trait for interacting with the MusicBrainz API.
|
||||||
#[cfg_attr(test, automock)]
|
#[cfg_attr(test, automock)]
|
||||||
pub trait IMusicBrainz {
|
pub trait IMusicBrainz {
|
||||||
fn search_artist(&mut self, name: &Artist) -> Result<Vec<Match<Artist>>, Error>;
|
fn search_artist(&mut self, name: &ArtistMeta) -> Result<Vec<Match<ArtistMeta>>, Error>;
|
||||||
fn search_release_group(
|
fn search_release_group(
|
||||||
&mut self,
|
&mut self,
|
||||||
arid: &Mbid,
|
arid: &Mbid,
|
||||||
album: &Album,
|
album: &AlbumMeta,
|
||||||
) -> Result<Vec<Match<Album>>, Error>;
|
) -> Result<Vec<Match<AlbumMeta>>, Error>;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
@ -23,18 +23,4 @@ pub struct Match<T> {
|
|||||||
pub disambiguation: Option<String>,
|
pub disambiguation: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Match<T> {
|
|
||||||
pub fn new(score: u8, item: T) -> Self {
|
|
||||||
Match {
|
|
||||||
score,
|
|
||||||
item,
|
|
||||||
disambiguation: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_disambiguation<S: Into<String>>(&mut self, disambiguation: S) {
|
|
||||||
self.disambiguation = Some(disambiguation.into())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub type Error = musichoard::external::musicbrainz::api::Error;
|
pub type Error = musichoard::external::musicbrainz::api::Error;
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use musichoard::collection::{
|
use musichoard::collection::{
|
||||||
album::{Album, AlbumId, AlbumPrimaryType, AlbumSeq},
|
album::{Album, AlbumId, AlbumMeta, AlbumPrimaryType, AlbumSeq},
|
||||||
artist::{Artist, ArtistId},
|
artist::{Artist, ArtistId, ArtistMeta},
|
||||||
musicbrainz::{MbAlbumRef, MbArtistRef},
|
musicbrainz::{MbAlbumRef, MbArtistRef},
|
||||||
track::{Track, TrackFormat, TrackId, TrackNum, TrackQuality},
|
track::{Track, TrackFormat, TrackId, TrackNum, TrackQuality},
|
||||||
};
|
};
|
||||||
|
@ -120,7 +120,7 @@ impl<'a, 'b> ArtistState<'a, 'b> {
|
|||||||
let list = List::new(
|
let list = List::new(
|
||||||
artists
|
artists
|
||||||
.iter()
|
.iter()
|
||||||
.map(|a| ListItem::new(a.id.name.as_str()))
|
.map(|a| ListItem::new(a.meta.id.name.as_str()))
|
||||||
.collect::<Vec<ListItem>>(),
|
.collect::<Vec<ListItem>>(),
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -158,12 +158,12 @@ impl<'a, 'b> AlbumState<'a, 'b> {
|
|||||||
Date: {}\n\
|
Date: {}\n\
|
||||||
Type: {}\n\
|
Type: {}\n\
|
||||||
Status: {}",
|
Status: {}",
|
||||||
album.map(|a| a.id.title.as_str()).unwrap_or(""),
|
album.map(|a| a.meta.id.title.as_str()).unwrap_or(""),
|
||||||
album
|
album
|
||||||
.map(|a| UiDisplay::display_date(&a.date, &a.seq))
|
.map(|a| UiDisplay::display_date(&a.meta.date, &a.meta.seq))
|
||||||
.unwrap_or_default(),
|
.unwrap_or_default(),
|
||||||
album
|
album
|
||||||
.map(|a| UiDisplay::display_type(&a.primary_type, &a.secondary_types))
|
.map(|a| UiDisplay::display_type(&a.meta.primary_type, &a.meta.secondary_types))
|
||||||
.unwrap_or_default(),
|
.unwrap_or_default(),
|
||||||
album
|
album
|
||||||
.map(|a| UiDisplay::display_album_status(&a.get_status()))
|
.map(|a| UiDisplay::display_album_status(&a.get_status()))
|
||||||
@ -180,10 +180,10 @@ impl<'a, 'b> AlbumState<'a, 'b> {
|
|||||||
|
|
||||||
fn to_list_item(album: &Album) -> ListItem {
|
fn to_list_item(album: &Album) -> ListItem {
|
||||||
let line = match album.get_status() {
|
let line = match album.get_status() {
|
||||||
AlbumStatus::None => Line::raw(album.id.title.as_str()),
|
AlbumStatus::None => Line::raw(album.meta.id.title.as_str()),
|
||||||
AlbumStatus::Owned(format) => match format {
|
AlbumStatus::Owned(format) => match format {
|
||||||
TrackFormat::Mp3 => Line::styled(album.id.title.as_str(), UiColor::FG_WARN),
|
TrackFormat::Mp3 => Line::styled(album.meta.id.title.as_str(), UiColor::FG_WARN),
|
||||||
TrackFormat::Flac => Line::styled(album.id.title.as_str(), UiColor::FG_GOOD),
|
TrackFormat::Flac => Line::styled(album.meta.id.title.as_str(), UiColor::FG_GOOD),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
ListItem::new(line)
|
ListItem::new(line)
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use musichoard::collection::{
|
use musichoard::collection::{
|
||||||
album::{Album, AlbumDate, AlbumPrimaryType, AlbumSecondaryType, AlbumSeq, AlbumStatus},
|
album::{AlbumDate, AlbumMeta, AlbumPrimaryType, AlbumSecondaryType, AlbumSeq, AlbumStatus},
|
||||||
artist::Artist,
|
artist::ArtistMeta,
|
||||||
track::{TrackFormat, TrackQuality},
|
track::{TrackFormat, TrackQuality},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -98,11 +98,11 @@ impl UiDisplay {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn display_artist_matching(artist: &Artist) -> String {
|
pub fn display_artist_matching(artist: &ArtistMeta) -> String {
|
||||||
format!("Matching artist: {}", &artist.id.name)
|
format!("Matching artist: {}", &artist.id.name)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn display_album_matching(album: &Album) -> String {
|
pub fn display_album_matching(album: &AlbumMeta) -> String {
|
||||||
format!(
|
format!(
|
||||||
"Matching album: {} | {}",
|
"Matching album: {} | {}",
|
||||||
UiDisplay::display_album_date(&album.date),
|
UiDisplay::display_album_date(&album.date),
|
||||||
@ -124,7 +124,7 @@ impl UiDisplay {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn display_artist_match(match_option: &MatchOption<Artist>) -> String {
|
pub fn display_artist_match(match_option: &MatchOption<ArtistMeta>) -> String {
|
||||||
match match_option {
|
match match_option {
|
||||||
MatchOption::Match(match_artist) => format!(
|
MatchOption::Match(match_artist) => format!(
|
||||||
"{}{} ({}%)",
|
"{}{} ({}%)",
|
||||||
@ -140,7 +140,7 @@ impl UiDisplay {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn display_album_match(match_option: &MatchOption<Album>) -> String {
|
pub fn display_album_match(match_option: &MatchOption<AlbumMeta>) -> String {
|
||||||
match match_option {
|
match match_option {
|
||||||
MatchOption::Match(match_album) => format!(
|
MatchOption::Match(match_album) => format!(
|
||||||
"{:010} | {} [{}] ({}%)",
|
"{:010} | {} [{}] ({}%)",
|
||||||
|
@ -72,12 +72,12 @@ impl<'a> ArtistOverlay<'a> {
|
|||||||
"Artist: {}\n\n{item_indent}\
|
"Artist: {}\n\n{item_indent}\
|
||||||
MusicBrainz: {}\n{item_indent}\
|
MusicBrainz: {}\n{item_indent}\
|
||||||
Properties: {}",
|
Properties: {}",
|
||||||
artist.map(|a| a.id.name.as_str()).unwrap_or(""),
|
artist.map(|a| a.meta.id.name.as_str()).unwrap_or(""),
|
||||||
artist
|
artist
|
||||||
.and_then(|a| a.musicbrainz.as_ref().map(|mb| mb.url().as_str()))
|
.and_then(|a| a.meta.musicbrainz.as_ref().map(|mb| mb.url().as_str()))
|
||||||
.unwrap_or(""),
|
.unwrap_or(""),
|
||||||
Self::opt_hashmap_to_string(
|
Self::opt_hashmap_to_string(
|
||||||
artist.map(|a| &a.properties),
|
artist.map(|a| &a.meta.properties),
|
||||||
&double_item_indent,
|
&double_item_indent,
|
||||||
&double_list_indent
|
&double_list_indent
|
||||||
),
|
),
|
||||||
@ -100,9 +100,9 @@ impl<'a> AlbumOverlay<'a> {
|
|||||||
let properties = Paragraph::new(format!(
|
let properties = Paragraph::new(format!(
|
||||||
"Album: {}\n\n{item_indent}\
|
"Album: {}\n\n{item_indent}\
|
||||||
MusicBrainz: {}",
|
MusicBrainz: {}",
|
||||||
album.map(|a| a.id.title.as_str()).unwrap_or(""),
|
album.map(|a| a.meta.id.title.as_str()).unwrap_or(""),
|
||||||
album
|
album
|
||||||
.and_then(|a| a.musicbrainz.as_ref().map(|mb| mb.url().as_str()))
|
.and_then(|a| a.meta.musicbrainz.as_ref().map(|mb| mb.url().as_str()))
|
||||||
.unwrap_or(""),
|
.unwrap_or(""),
|
||||||
));
|
));
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use musichoard::collection::{album::Album, artist::Artist};
|
use musichoard::collection::{album::AlbumMeta, artist::ArtistMeta};
|
||||||
use ratatui::widgets::{List, ListItem};
|
use ratatui::widgets::{List, ListItem};
|
||||||
|
|
||||||
use crate::tui::{
|
use crate::tui::{
|
||||||
@ -32,8 +32,8 @@ impl<'a, 'b> MatchesState<'a, 'b> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn artists(
|
fn artists(
|
||||||
matching: &Artist,
|
matching: &ArtistMeta,
|
||||||
matches: &[MatchOption<Artist>],
|
matches: &[MatchOption<ArtistMeta>],
|
||||||
state: &'b mut WidgetState,
|
state: &'b mut WidgetState,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let matching = UiDisplay::display_artist_matching(matching);
|
let matching = UiDisplay::display_artist_matching(matching);
|
||||||
@ -54,8 +54,8 @@ impl<'a, 'b> MatchesState<'a, 'b> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn albums(
|
fn albums(
|
||||||
matching: &Album,
|
matching: &AlbumMeta,
|
||||||
matches: &[MatchOption<Album>],
|
matches: &[MatchOption<AlbumMeta>],
|
||||||
state: &'b mut WidgetState,
|
state: &'b mut WidgetState,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let matching = UiDisplay::display_album_matching(matching);
|
let matching = UiDisplay::display_album_matching(matching);
|
||||||
|
@ -177,8 +177,8 @@ impl IUi for Ui {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use musichoard::collection::{
|
use musichoard::collection::{
|
||||||
album::{AlbumDate, AlbumId, AlbumPrimaryType, AlbumSecondaryType},
|
album::{AlbumDate, AlbumId, AlbumMeta, AlbumPrimaryType, AlbumSecondaryType},
|
||||||
artist::{Artist, ArtistId},
|
artist::{Artist, ArtistId, ArtistMeta},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::tui::{
|
use crate::tui::{
|
||||||
@ -224,14 +224,14 @@ mod tests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn artist_matches(matching: Artist, list: Vec<Match<Artist>>) -> AppMatchesInfo {
|
fn artist_matches(matching: ArtistMeta, list: Vec<Match<ArtistMeta>>) -> AppMatchesInfo {
|
||||||
let mut list: Vec<MatchOption<Artist>> = list.into_iter().map(Into::into).collect();
|
let mut list: Vec<MatchOption<ArtistMeta>> = list.into_iter().map(Into::into).collect();
|
||||||
list.push(MatchOption::CannotHaveMbid);
|
list.push(MatchOption::CannotHaveMbid);
|
||||||
AppMatchesInfo::artist(matching, list)
|
AppMatchesInfo::artist(matching, list)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn album_matches(matching: Album, list: Vec<Match<Album>>) -> AppMatchesInfo {
|
fn album_matches(matching: AlbumMeta, list: Vec<Match<AlbumMeta>>) -> AppMatchesInfo {
|
||||||
let mut list: Vec<MatchOption<Album>> = list.into_iter().map(Into::into).collect();
|
let mut list: Vec<MatchOption<AlbumMeta>> = list.into_iter().map(Into::into).collect();
|
||||||
list.push(MatchOption::CannotHaveMbid);
|
list.push(MatchOption::CannotHaveMbid);
|
||||||
AppMatchesInfo::album(matching, list)
|
AppMatchesInfo::album(matching, list)
|
||||||
}
|
}
|
||||||
@ -328,7 +328,7 @@ mod tests {
|
|||||||
|
|
||||||
let mut terminal = terminal();
|
let mut terminal = terminal();
|
||||||
|
|
||||||
let artist = Artist::new(ArtistId::new("an artist"));
|
let artist = ArtistMeta::new(ArtistId::new("an artist"));
|
||||||
let artist_match = Match {
|
let artist_match = Match {
|
||||||
score: 80,
|
score: 80,
|
||||||
item: artist.clone(),
|
item: artist.clone(),
|
||||||
@ -357,7 +357,7 @@ mod tests {
|
|||||||
|
|
||||||
let mut terminal = terminal();
|
let mut terminal = terminal();
|
||||||
|
|
||||||
let album = Album::new(
|
let album = AlbumMeta::new(
|
||||||
AlbumId::new("An Album"),
|
AlbumId::new("An Album"),
|
||||||
AlbumDate::new(Some(1990), Some(5), None),
|
AlbumDate::new(Some(1990), Some(5), None),
|
||||||
Some(AlbumPrimaryType::Album),
|
Some(AlbumPrimaryType::Album),
|
||||||
|
@ -18,7 +18,7 @@ fn expected() -> Collection {
|
|||||||
let mut expected = COLLECTION.to_owned();
|
let mut expected = COLLECTION.to_owned();
|
||||||
for artist in expected.iter_mut() {
|
for artist in expected.iter_mut() {
|
||||||
for album in artist.albums.iter_mut() {
|
for album in artist.albums.iter_mut() {
|
||||||
album.date = AlbumDate::default();
|
album.meta.date = AlbumDate::default();
|
||||||
album.tracks.clear();
|
album.tracks.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,8 +2,8 @@ use once_cell::sync::Lazy;
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use musichoard::collection::{
|
use musichoard::collection::{
|
||||||
album::{Album, AlbumId, AlbumPrimaryType, AlbumSecondaryType, AlbumSeq},
|
album::{Album, AlbumId, AlbumMeta, AlbumPrimaryType, AlbumSecondaryType, AlbumSeq},
|
||||||
artist::{Artist, ArtistId},
|
artist::{Artist, ArtistId, ArtistMeta},
|
||||||
musicbrainz::MbArtistRef,
|
musicbrainz::MbArtistRef,
|
||||||
track::{Track, TrackFormat, TrackId, TrackNum, TrackQuality},
|
track::{Track, TrackFormat, TrackId, TrackNum, TrackQuality},
|
||||||
Collection,
|
Collection,
|
||||||
@ -12,6 +12,7 @@ use musichoard::collection::{
|
|||||||
pub static COLLECTION: Lazy<Vec<Artist>> = Lazy::new(|| -> Collection {
|
pub static COLLECTION: Lazy<Vec<Artist>> = Lazy::new(|| -> Collection {
|
||||||
vec![
|
vec![
|
||||||
Artist {
|
Artist {
|
||||||
|
meta: ArtistMeta {
|
||||||
id: ArtistId {
|
id: ArtistId {
|
||||||
name: String::from("Аркона"),
|
name: String::from("Аркона"),
|
||||||
},
|
},
|
||||||
@ -32,7 +33,9 @@ pub static COLLECTION: Lazy<Vec<Artist>> = Lazy::new(|| -> Collection {
|
|||||||
"https://www.qobuz.com/nl-nl/interpreter/arkona/download-streaming-albums",
|
"https://www.qobuz.com/nl-nl/interpreter/arkona/download-streaming-albums",
|
||||||
)]),
|
)]),
|
||||||
]),
|
]),
|
||||||
|
},
|
||||||
albums: vec![Album {
|
albums: vec![Album {
|
||||||
|
meta: AlbumMeta {
|
||||||
id: AlbumId {
|
id: AlbumId {
|
||||||
title: String::from("Slovo"),
|
title: String::from("Slovo"),
|
||||||
},
|
},
|
||||||
@ -41,6 +44,7 @@ pub static COLLECTION: Lazy<Vec<Artist>> = Lazy::new(|| -> Collection {
|
|||||||
musicbrainz: None,
|
musicbrainz: None,
|
||||||
primary_type: Some(AlbumPrimaryType::Album),
|
primary_type: Some(AlbumPrimaryType::Album),
|
||||||
secondary_types: vec![],
|
secondary_types: vec![],
|
||||||
|
},
|
||||||
tracks: vec![
|
tracks: vec![
|
||||||
Track {
|
Track {
|
||||||
id: TrackId {
|
id: TrackId {
|
||||||
@ -200,6 +204,7 @@ pub static COLLECTION: Lazy<Vec<Artist>> = Lazy::new(|| -> Collection {
|
|||||||
}],
|
}],
|
||||||
},
|
},
|
||||||
Artist {
|
Artist {
|
||||||
|
meta: ArtistMeta {
|
||||||
id: ArtistId {
|
id: ArtistId {
|
||||||
name: String::from("Eluveitie"),
|
name: String::from("Eluveitie"),
|
||||||
},
|
},
|
||||||
@ -215,8 +220,10 @@ pub static COLLECTION: Lazy<Vec<Artist>> = Lazy::new(|| -> Collection {
|
|||||||
"https://www.qobuz.com/nl-nl/interpreter/eluveitie/download-streaming-albums",
|
"https://www.qobuz.com/nl-nl/interpreter/eluveitie/download-streaming-albums",
|
||||||
)]),
|
)]),
|
||||||
]),
|
]),
|
||||||
|
},
|
||||||
albums: vec![
|
albums: vec![
|
||||||
Album {
|
Album {
|
||||||
|
meta: AlbumMeta {
|
||||||
id: AlbumId {
|
id: AlbumId {
|
||||||
title: String::from("Vên [re‐recorded]"),
|
title: String::from("Vên [re‐recorded]"),
|
||||||
},
|
},
|
||||||
@ -225,6 +232,7 @@ pub static COLLECTION: Lazy<Vec<Artist>> = Lazy::new(|| -> Collection {
|
|||||||
musicbrainz: None,
|
musicbrainz: None,
|
||||||
primary_type: Some(AlbumPrimaryType::Ep),
|
primary_type: Some(AlbumPrimaryType::Ep),
|
||||||
secondary_types: vec![],
|
secondary_types: vec![],
|
||||||
|
},
|
||||||
tracks: vec![
|
tracks: vec![
|
||||||
Track {
|
Track {
|
||||||
id: TrackId {
|
id: TrackId {
|
||||||
@ -295,6 +303,7 @@ pub static COLLECTION: Lazy<Vec<Artist>> = Lazy::new(|| -> Collection {
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
Album {
|
Album {
|
||||||
|
meta: AlbumMeta {
|
||||||
id: AlbumId {
|
id: AlbumId {
|
||||||
title: String::from("Slania"),
|
title: String::from("Slania"),
|
||||||
},
|
},
|
||||||
@ -303,6 +312,7 @@ pub static COLLECTION: Lazy<Vec<Artist>> = Lazy::new(|| -> Collection {
|
|||||||
musicbrainz: None,
|
musicbrainz: None,
|
||||||
primary_type: Some(AlbumPrimaryType::Album),
|
primary_type: Some(AlbumPrimaryType::Album),
|
||||||
secondary_types: vec![],
|
secondary_types: vec![],
|
||||||
|
},
|
||||||
tracks: vec![
|
tracks: vec![
|
||||||
Track {
|
Track {
|
||||||
id: TrackId {
|
id: TrackId {
|
||||||
@ -441,6 +451,7 @@ pub static COLLECTION: Lazy<Vec<Artist>> = Lazy::new(|| -> Collection {
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
Artist {
|
Artist {
|
||||||
|
meta: ArtistMeta {
|
||||||
id: ArtistId {
|
id: ArtistId {
|
||||||
name: String::from("Frontside"),
|
name: String::from("Frontside"),
|
||||||
},
|
},
|
||||||
@ -456,7 +467,9 @@ pub static COLLECTION: Lazy<Vec<Artist>> = Lazy::new(|| -> Collection {
|
|||||||
"https://www.qobuz.com/nl-nl/interpreter/frontside/download-streaming-albums",
|
"https://www.qobuz.com/nl-nl/interpreter/frontside/download-streaming-albums",
|
||||||
)]),
|
)]),
|
||||||
]),
|
]),
|
||||||
|
},
|
||||||
albums: vec![Album {
|
albums: vec![Album {
|
||||||
|
meta: AlbumMeta {
|
||||||
id: AlbumId {
|
id: AlbumId {
|
||||||
title: String::from("…nasze jest królestwo, potęga i chwała na wieki…"),
|
title: String::from("…nasze jest królestwo, potęga i chwała na wieki…"),
|
||||||
},
|
},
|
||||||
@ -465,6 +478,7 @@ pub static COLLECTION: Lazy<Vec<Artist>> = Lazy::new(|| -> Collection {
|
|||||||
musicbrainz: None,
|
musicbrainz: None,
|
||||||
primary_type: Some(AlbumPrimaryType::Album),
|
primary_type: Some(AlbumPrimaryType::Album),
|
||||||
secondary_types: vec![],
|
secondary_types: vec![],
|
||||||
|
},
|
||||||
tracks: vec![
|
tracks: vec![
|
||||||
Track {
|
Track {
|
||||||
id: TrackId {
|
id: TrackId {
|
||||||
@ -591,6 +605,7 @@ pub static COLLECTION: Lazy<Vec<Artist>> = Lazy::new(|| -> Collection {
|
|||||||
}],
|
}],
|
||||||
},
|
},
|
||||||
Artist {
|
Artist {
|
||||||
|
meta: ArtistMeta {
|
||||||
id: ArtistId {
|
id: ArtistId {
|
||||||
name: String::from("Heaven’s Basement"),
|
name: String::from("Heaven’s Basement"),
|
||||||
},
|
},
|
||||||
@ -608,7 +623,9 @@ pub static COLLECTION: Lazy<Vec<Artist>> = Lazy::new(|| -> Collection {
|
|||||||
"https://www.qobuz.com/nl-nl/interpreter/heaven-s-basement/download-streaming-albums",
|
"https://www.qobuz.com/nl-nl/interpreter/heaven-s-basement/download-streaming-albums",
|
||||||
)]),
|
)]),
|
||||||
]),
|
]),
|
||||||
|
},
|
||||||
albums: vec![Album {
|
albums: vec![Album {
|
||||||
|
meta: AlbumMeta {
|
||||||
id: AlbumId {
|
id: AlbumId {
|
||||||
title: String::from("Paper Plague"),
|
title: String::from("Paper Plague"),
|
||||||
},
|
},
|
||||||
@ -617,6 +634,7 @@ pub static COLLECTION: Lazy<Vec<Artist>> = Lazy::new(|| -> Collection {
|
|||||||
musicbrainz: None,
|
musicbrainz: None,
|
||||||
primary_type: None,
|
primary_type: None,
|
||||||
secondary_types: vec![],
|
secondary_types: vec![],
|
||||||
|
},
|
||||||
tracks: vec![
|
tracks: vec![
|
||||||
Track {
|
Track {
|
||||||
id: TrackId {
|
id: TrackId {
|
||||||
@ -631,6 +649,7 @@ pub static COLLECTION: Lazy<Vec<Artist>> = Lazy::new(|| -> Collection {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
}, Album {
|
}, Album {
|
||||||
|
meta: AlbumMeta {
|
||||||
id: AlbumId {
|
id: AlbumId {
|
||||||
title: String::from("Unbreakable"),
|
title: String::from("Unbreakable"),
|
||||||
},
|
},
|
||||||
@ -639,6 +658,7 @@ pub static COLLECTION: Lazy<Vec<Artist>> = Lazy::new(|| -> Collection {
|
|||||||
musicbrainz: None,
|
musicbrainz: None,
|
||||||
primary_type: Some(AlbumPrimaryType::Album),
|
primary_type: Some(AlbumPrimaryType::Album),
|
||||||
secondary_types: vec![],
|
secondary_types: vec![],
|
||||||
|
},
|
||||||
tracks: vec![
|
tracks: vec![
|
||||||
Track {
|
Track {
|
||||||
id: TrackId {
|
id: TrackId {
|
||||||
@ -721,6 +741,7 @@ pub static COLLECTION: Lazy<Vec<Artist>> = Lazy::new(|| -> Collection {
|
|||||||
}],
|
}],
|
||||||
},
|
},
|
||||||
Artist {
|
Artist {
|
||||||
|
meta: ArtistMeta {
|
||||||
id: ArtistId {
|
id: ArtistId {
|
||||||
name: String::from("Metallica"),
|
name: String::from("Metallica"),
|
||||||
},
|
},
|
||||||
@ -736,8 +757,10 @@ pub static COLLECTION: Lazy<Vec<Artist>> = Lazy::new(|| -> Collection {
|
|||||||
"https://www.qobuz.com/nl-nl/interpreter/metallica/download-streaming-albums",
|
"https://www.qobuz.com/nl-nl/interpreter/metallica/download-streaming-albums",
|
||||||
)]),
|
)]),
|
||||||
]),
|
]),
|
||||||
|
},
|
||||||
albums: vec![
|
albums: vec![
|
||||||
Album {
|
Album {
|
||||||
|
meta: AlbumMeta {
|
||||||
id: AlbumId {
|
id: AlbumId {
|
||||||
title: String::from("Ride the Lightning"),
|
title: String::from("Ride the Lightning"),
|
||||||
},
|
},
|
||||||
@ -746,6 +769,7 @@ pub static COLLECTION: Lazy<Vec<Artist>> = Lazy::new(|| -> Collection {
|
|||||||
musicbrainz: None,
|
musicbrainz: None,
|
||||||
primary_type: Some(AlbumPrimaryType::Album),
|
primary_type: Some(AlbumPrimaryType::Album),
|
||||||
secondary_types: vec![],
|
secondary_types: vec![],
|
||||||
|
},
|
||||||
tracks: vec![
|
tracks: vec![
|
||||||
Track {
|
Track {
|
||||||
id: TrackId {
|
id: TrackId {
|
||||||
@ -838,6 +862,7 @@ pub static COLLECTION: Lazy<Vec<Artist>> = Lazy::new(|| -> Collection {
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
Album {
|
Album {
|
||||||
|
meta: AlbumMeta {
|
||||||
id: AlbumId {
|
id: AlbumId {
|
||||||
title: String::from("S&M"),
|
title: String::from("S&M"),
|
||||||
},
|
},
|
||||||
@ -846,6 +871,7 @@ pub static COLLECTION: Lazy<Vec<Artist>> = Lazy::new(|| -> Collection {
|
|||||||
musicbrainz: None,
|
musicbrainz: None,
|
||||||
primary_type: Some(AlbumPrimaryType::Album),
|
primary_type: Some(AlbumPrimaryType::Album),
|
||||||
secondary_types: vec![AlbumSecondaryType::Live],
|
secondary_types: vec![AlbumSecondaryType::Live],
|
||||||
|
},
|
||||||
tracks: vec![
|
tracks: vec![
|
||||||
Track {
|
Track {
|
||||||
id: TrackId {
|
id: TrackId {
|
||||||
|
Loading…
Reference in New Issue
Block a user