Artist schema iteration
This commit is contained in:
parent
ae876915b4
commit
e65cee366d
3 changed files with 68 additions and 91 deletions
|
@ -28,12 +28,8 @@ pub struct ArtistHeader {
|
||||||
|
|
||||||
#[derive(Debug, Default, PartialEq, Eq)]
|
#[derive(Debug, Default, PartialEq, Eq)]
|
||||||
pub struct Artist {
|
pub struct Artist {
|
||||||
pub name: String,
|
pub header: ArtistHeader,
|
||||||
pub albums_as_performer: Vec<Album>,
|
pub albums: Vec<Album>,
|
||||||
pub albums_as_additional_performer: Vec<Album>, // Albums where this artist shows up as `artist` without being `album_artist`
|
|
||||||
pub albums_as_composer: Vec<Album>,
|
|
||||||
pub albums_as_lyricist: Vec<Album>,
|
|
||||||
pub num_songs_by_genre: HashMap<String, u32>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default, PartialEq, Eq)]
|
#[derive(Debug, Default, PartialEq, Eq)]
|
||||||
|
@ -88,37 +84,18 @@ impl Collection {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_artist(&self, strings: &RodeoReader, artist_key: ArtistKey) -> Option<Artist> {
|
pub fn get_artist(&self, strings: &RodeoReader, artist_key: ArtistKey) -> Option<Artist> {
|
||||||
self.artists.get(&artist_key).map(|a| {
|
self.artists.get(&artist_key).map(|artist| {
|
||||||
let sort_albums = |a: &Album, b: &Album| (&a.year, &a.name).cmp(&(&b.year, &b.name));
|
let header = make_artist_header(artist, strings);
|
||||||
|
let albums = {
|
||||||
let list_albums = |keys: &HashSet<AlbumKey>| {
|
let mut albums = artist
|
||||||
let mut albums = keys
|
.all_albums
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|key| self.get_album(strings, key.clone()))
|
.filter_map(|key| self.get_album(strings, key.clone()))
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
albums.sort_by(sort_albums);
|
albums.sort_by(|a, b| (&a.year, &a.name).cmp(&(&b.year, &b.name)));
|
||||||
albums
|
albums
|
||||||
};
|
};
|
||||||
|
Artist { header, albums }
|
||||||
let name = strings.resolve(&a.name).to_owned();
|
|
||||||
let albums_as_performer = list_albums(&a.albums_as_performer);
|
|
||||||
let albums_as_additional_performer = list_albums(&a.albums_as_additional_performer);
|
|
||||||
let albums_as_composer = list_albums(&a.albums_as_composer);
|
|
||||||
let albums_as_lyricist = list_albums(&a.albums_as_lyricist);
|
|
||||||
let num_songs_by_genre = a
|
|
||||||
.num_songs_by_genre
|
|
||||||
.iter()
|
|
||||||
.map(|(genre, num)| (strings.resolve(genre).to_string(), *num))
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
Artist {
|
|
||||||
name,
|
|
||||||
albums_as_performer,
|
|
||||||
albums_as_additional_performer,
|
|
||||||
albums_as_composer,
|
|
||||||
albums_as_lyricist,
|
|
||||||
num_songs_by_genre,
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -288,6 +265,9 @@ impl Builder {
|
||||||
for name in all_artists {
|
for name in all_artists {
|
||||||
let artist = self.get_or_create_artist(name);
|
let artist = self.get_or_create_artist(name);
|
||||||
artist.num_songs += 1;
|
artist.num_songs += 1;
|
||||||
|
if let Some(album_key) = &album_key {
|
||||||
|
artist.all_albums.insert(album_key.clone());
|
||||||
|
}
|
||||||
for genre in &song.genres {
|
for genre in &song.genres {
|
||||||
*artist
|
*artist
|
||||||
.num_songs_by_genre
|
.num_songs_by_genre
|
||||||
|
@ -304,6 +284,7 @@ impl Builder {
|
||||||
.entry(artist_key)
|
.entry(artist_key)
|
||||||
.or_insert_with(|| storage::Artist {
|
.or_insert_with(|| storage::Artist {
|
||||||
name,
|
name,
|
||||||
|
all_albums: HashSet::new(),
|
||||||
albums_as_performer: HashSet::new(),
|
albums_as_performer: HashSet::new(),
|
||||||
albums_as_additional_performer: HashSet::new(),
|
albums_as_additional_performer: HashSet::new(),
|
||||||
albums_as_composer: HashSet::new(),
|
albums_as_composer: HashSet::new(),
|
||||||
|
@ -550,10 +531,7 @@ mod test {
|
||||||
artists: Vec<String>,
|
artists: Vec<String>,
|
||||||
composers: Vec<String>,
|
composers: Vec<String>,
|
||||||
lyricists: Vec<String>,
|
lyricists: Vec<String>,
|
||||||
expect_performer: bool,
|
expect_listed: bool,
|
||||||
expect_additional_performer: bool,
|
|
||||||
expect_composer: bool,
|
|
||||||
expect_lyricist: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let test_cases = vec![
|
let test_cases = vec![
|
||||||
|
@ -563,43 +541,47 @@ mod test {
|
||||||
artists: vec![artist_name.to_string()],
|
artists: vec![artist_name.to_string()],
|
||||||
composers: vec![artist_name.to_string()],
|
composers: vec![artist_name.to_string()],
|
||||||
lyricists: vec![artist_name.to_string()],
|
lyricists: vec![artist_name.to_string()],
|
||||||
expect_performer: true,
|
expect_listed: true,
|
||||||
expect_composer: true,
|
|
||||||
expect_lyricist: true,
|
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
// Only tagged as artist
|
// Only tagged as artist
|
||||||
TestCase {
|
TestCase {
|
||||||
artists: vec![artist_name.to_string()],
|
artists: vec![artist_name.to_string()],
|
||||||
expect_performer: true,
|
expect_listed: true,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
// Only tagged as artist w/ distinct album artist
|
// Only tagged as artist w/ distinct album artist
|
||||||
TestCase {
|
TestCase {
|
||||||
album_artists: vec![other_artist_name.to_string()],
|
album_artists: vec![other_artist_name.to_string()],
|
||||||
artists: vec![artist_name.to_string()],
|
artists: vec![artist_name.to_string()],
|
||||||
expect_additional_performer: true,
|
expect_listed: true,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
// Tagged as artist and within album artists
|
// Tagged as artist and within album artists
|
||||||
TestCase {
|
TestCase {
|
||||||
album_artists: vec![artist_name.to_string(), other_artist_name.to_string()],
|
album_artists: vec![artist_name.to_string(), other_artist_name.to_string()],
|
||||||
artists: vec![artist_name.to_string()],
|
artists: vec![artist_name.to_string()],
|
||||||
expect_performer: true,
|
expect_listed: true,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
// Only tagged as composer
|
// Only tagged as composer
|
||||||
TestCase {
|
TestCase {
|
||||||
artists: vec![other_artist_name.to_string()],
|
artists: vec![other_artist_name.to_string()],
|
||||||
composers: vec![artist_name.to_string()],
|
composers: vec![artist_name.to_string()],
|
||||||
expect_composer: true,
|
expect_listed: true,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
// Only tagged as lyricist
|
// Only tagged as lyricist
|
||||||
TestCase {
|
TestCase {
|
||||||
artists: vec![other_artist_name.to_string()],
|
artists: vec![other_artist_name.to_string()],
|
||||||
lyricists: vec![artist_name.to_string()],
|
lyricists: vec![artist_name.to_string()],
|
||||||
expect_lyricist: true,
|
expect_listed: true,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
// Not tagged as lyricist
|
||||||
|
TestCase {
|
||||||
|
artists: vec![other_artist_name.to_string()],
|
||||||
|
expect_listed: false,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
@ -615,38 +597,12 @@ mod test {
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}]));
|
}]));
|
||||||
|
|
||||||
let artist_key = ArtistKey {
|
let artists = collection.get_artists(&strings);
|
||||||
name: strings.get(artist_name),
|
|
||||||
};
|
|
||||||
let artist = collection.get_artist(&strings, artist_key).unwrap();
|
|
||||||
|
|
||||||
let names = |a: &Vec<Album>| a.iter().map(|a| a.name.to_owned()).collect::<Vec<_>>();
|
if test.expect_listed {
|
||||||
|
assert!(artists.iter().any(|a| a.name == UniCase::new(artist_name)));
|
||||||
if test.expect_performer {
|
|
||||||
assert_eq!(names(&artist.albums_as_performer), vec![album_name]);
|
|
||||||
} else {
|
} else {
|
||||||
assert!(names(&artist.albums_as_performer).is_empty());
|
assert!(artists.iter().all(|a| a.name != UniCase::new(artist_name)));
|
||||||
}
|
|
||||||
|
|
||||||
if test.expect_additional_performer {
|
|
||||||
assert_eq!(
|
|
||||||
names(&artist.albums_as_additional_performer),
|
|
||||||
vec![album_name]
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
assert!(names(&artist.albums_as_additional_performer).is_empty());
|
|
||||||
}
|
|
||||||
|
|
||||||
if test.expect_composer {
|
|
||||||
assert_eq!(names(&artist.albums_as_composer), vec![album_name]);
|
|
||||||
} else {
|
|
||||||
assert!(names(&artist.albums_as_composer).is_empty());
|
|
||||||
}
|
|
||||||
|
|
||||||
if test.expect_lyricist {
|
|
||||||
assert_eq!(names(&artist.albums_as_lyricist), vec![album_name]);
|
|
||||||
} else {
|
|
||||||
assert!(names(&artist.albums_as_lyricist).is_empty());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -689,7 +645,7 @@ mod test {
|
||||||
|
|
||||||
let names = artist
|
let names = artist
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.albums_as_performer
|
.albums
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|a| a.name)
|
.map(|a| a.name)
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
|
@ -19,6 +19,7 @@ pub enum File {
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
pub struct Artist {
|
pub struct Artist {
|
||||||
pub name: Spur,
|
pub name: Spur,
|
||||||
|
pub all_albums: HashSet<AlbumKey>,
|
||||||
pub albums_as_performer: HashSet<AlbumKey>,
|
pub albums_as_performer: HashSet<AlbumKey>,
|
||||||
pub albums_as_additional_performer: HashSet<AlbumKey>,
|
pub albums_as_additional_performer: HashSet<AlbumKey>,
|
||||||
pub albums_as_composer: HashSet<AlbumKey>,
|
pub albums_as_composer: HashSet<AlbumKey>,
|
||||||
|
|
|
@ -344,24 +344,44 @@ impl From<index::ArtistHeader> for ArtistHeader {
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
pub struct Artist {
|
pub struct Artist {
|
||||||
pub name: String,
|
#[serde(flatten)]
|
||||||
pub albums_as_performer: Vec<AlbumHeader>,
|
pub header: ArtistHeader,
|
||||||
pub albums_as_additional_performer: Vec<AlbumHeader>,
|
pub albums: Vec<ArtistAlbum>,
|
||||||
pub albums_as_composer: Vec<AlbumHeader>,
|
}
|
||||||
pub albums_as_lyricist: Vec<AlbumHeader>,
|
|
||||||
pub num_songs_by_genre: HashMap<String, u32>,
|
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
|
pub struct ArtistAlbum {
|
||||||
|
#[serde(flatten)]
|
||||||
|
pub album: AlbumHeader,
|
||||||
|
pub contributions: Vec<Contribution>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
|
pub struct Contribution {
|
||||||
|
pub performer: bool,
|
||||||
|
pub composer: bool,
|
||||||
|
pub lyricist: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<index::Artist> for Artist {
|
impl From<index::Artist> for Artist {
|
||||||
fn from(a: index::Artist) -> Self {
|
fn from(artist: index::Artist) -> Self {
|
||||||
let convert_albums = |a: Vec<index::Album>| a.into_iter().map(|a| a.into()).collect();
|
let artist_name = artist.header.name.clone();
|
||||||
|
let convert_album = |album: index::Album| ArtistAlbum {
|
||||||
|
contributions: album
|
||||||
|
.songs
|
||||||
|
.iter()
|
||||||
|
.map(|song| Contribution {
|
||||||
|
performer: song.artists.contains(&artist_name)
|
||||||
|
|| song.album_artists.contains(&artist_name),
|
||||||
|
composer: song.composers.contains(&artist_name),
|
||||||
|
lyricist: song.lyricists.contains(&artist_name),
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
album: AlbumHeader::from(album),
|
||||||
|
};
|
||||||
Self {
|
Self {
|
||||||
name: a.name,
|
header: ArtistHeader::from(artist.header),
|
||||||
albums_as_performer: convert_albums(a.albums_as_performer),
|
albums: artist.albums.into_iter().map(convert_album).collect(),
|
||||||
albums_as_additional_performer: convert_albums(a.albums_as_additional_performer),
|
|
||||||
albums_as_composer: convert_albums(a.albums_as_composer),
|
|
||||||
albums_as_lyricist: convert_albums(a.albums_as_lyricist),
|
|
||||||
num_songs_by_genre: a.num_songs_by_genre,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -372,7 +392,7 @@ pub struct AlbumHeader {
|
||||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
pub artwork: Option<String>,
|
pub artwork: Option<String>,
|
||||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||||
pub artists: Vec<String>,
|
pub main_artists: Vec<String>,
|
||||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
pub year: Option<i64>,
|
pub year: Option<i64>,
|
||||||
}
|
}
|
||||||
|
@ -382,7 +402,7 @@ impl From<index::Album> for AlbumHeader {
|
||||||
Self {
|
Self {
|
||||||
name: a.name,
|
name: a.name,
|
||||||
artwork: a.artwork.map(|a| a.to_string_lossy().to_string()),
|
artwork: a.artwork.map(|a| a.to_string_lossy().to_string()),
|
||||||
artists: a.artists,
|
main_artists: a.artists,
|
||||||
year: a.year,
|
year: a.year,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue