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