Drop support for blank album names
This commit is contained in:
parent
4072e3b07d
commit
7be9f25cb3
6 changed files with 70 additions and 84 deletions
|
@ -140,21 +140,21 @@ impl Manager {
|
||||||
.unwrap()
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_album(
|
pub async fn get_album(&self, artists: Vec<String>, name: String) -> Result<Album, Error> {
|
||||||
&self,
|
|
||||||
artists: Vec<String>,
|
|
||||||
name: Option<String>,
|
|
||||||
) -> Result<Album, Error> {
|
|
||||||
spawn_blocking({
|
spawn_blocking({
|
||||||
let index_manager = self.clone();
|
let index_manager = self.clone();
|
||||||
move || {
|
move || {
|
||||||
let index = index_manager.index.read().unwrap();
|
let index = index_manager.index.read().unwrap();
|
||||||
|
let name = index
|
||||||
|
.strings
|
||||||
|
.get(&name)
|
||||||
|
.ok_or_else(|| Error::AlbumNotFound)?;
|
||||||
let album_key = AlbumKey {
|
let album_key = AlbumKey {
|
||||||
artists: artists
|
artists: artists
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter_map(|a| index.strings.get(a))
|
.filter_map(|a| index.strings.get(a))
|
||||||
.collect(),
|
.collect(),
|
||||||
name: name.and_then(|n| index.strings.get(n)),
|
name,
|
||||||
};
|
};
|
||||||
index
|
index
|
||||||
.collection
|
.collection
|
||||||
|
|
|
@ -38,7 +38,7 @@ pub struct Artist {
|
||||||
|
|
||||||
#[derive(Debug, Default, PartialEq, Eq)]
|
#[derive(Debug, Default, PartialEq, Eq)]
|
||||||
pub struct Album {
|
pub struct Album {
|
||||||
pub name: Option<String>,
|
pub name: String,
|
||||||
pub artwork: Option<PathBuf>,
|
pub artwork: Option<PathBuf>,
|
||||||
pub artists: Vec<String>,
|
pub artists: Vec<String>,
|
||||||
pub year: Option<i64>,
|
pub year: Option<i64>,
|
||||||
|
@ -140,7 +140,7 @@ impl Collection {
|
||||||
songs.sort_by_key(|s| (s.disc_number.unwrap_or(-1), s.track_number.unwrap_or(-1)));
|
songs.sort_by_key(|s| (s.disc_number.unwrap_or(-1), s.track_number.unwrap_or(-1)));
|
||||||
|
|
||||||
Album {
|
Album {
|
||||||
name: a.name.map(|s| strings.resolve(&s).to_string()),
|
name: strings.resolve(&a.name).to_string(),
|
||||||
artwork: a
|
artwork: a
|
||||||
.artwork
|
.artwork
|
||||||
.as_ref()
|
.as_ref()
|
||||||
|
@ -243,37 +243,45 @@ impl Builder {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_song_to_artists(&mut self, song: &storage::Song) {
|
fn add_song_to_artists(&mut self, song: &storage::Song) {
|
||||||
let album_key: AlbumKey = song.album_key();
|
let album_key = song.album_key();
|
||||||
|
|
||||||
let mut all_artists = TinyVec::<[Spur; 8]>::new();
|
let mut all_artists = TinyVec::<[Spur; 8]>::new();
|
||||||
|
|
||||||
for name in &song.album_artists {
|
for name in &song.album_artists {
|
||||||
let artist = self.get_or_create_artist(*name);
|
|
||||||
artist.albums_as_performer.insert(album_key.clone());
|
|
||||||
all_artists.push(*name);
|
all_artists.push(*name);
|
||||||
|
if let Some(album_key) = &album_key {
|
||||||
|
let artist = self.get_or_create_artist(*name);
|
||||||
|
artist.albums_as_performer.insert(album_key.clone());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for name in &song.composers {
|
for name in &song.composers {
|
||||||
let artist = self.get_or_create_artist(*name);
|
|
||||||
artist.albums_as_composer.insert(album_key.clone());
|
|
||||||
all_artists.push(*name);
|
all_artists.push(*name);
|
||||||
|
if let Some(album_key) = &album_key {
|
||||||
|
let artist = self.get_or_create_artist(*name);
|
||||||
|
artist.albums_as_composer.insert(album_key.clone());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for name in &song.lyricists {
|
for name in &song.lyricists {
|
||||||
let artist = self.get_or_create_artist(*name);
|
|
||||||
artist.albums_as_lyricist.insert(album_key.clone());
|
|
||||||
all_artists.push(*name);
|
all_artists.push(*name);
|
||||||
|
if let Some(album_key) = &album_key {
|
||||||
|
let artist = self.get_or_create_artist(*name);
|
||||||
|
artist.albums_as_lyricist.insert(album_key.clone());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for name in &song.artists {
|
for name in &song.artists {
|
||||||
let artist = self.get_or_create_artist(*name);
|
|
||||||
all_artists.push(*name);
|
all_artists.push(*name);
|
||||||
if song.album_artists.is_empty() {
|
if let Some(album_key) = &album_key {
|
||||||
artist.albums_as_performer.insert(album_key.clone());
|
let artist = self.get_or_create_artist(*name);
|
||||||
} else if !song.album_artists.contains(name) {
|
if song.album_artists.is_empty() {
|
||||||
artist
|
artist.albums_as_performer.insert(album_key.clone());
|
||||||
.albums_as_additional_performer
|
} else if !song.album_artists.contains(name) {
|
||||||
.insert(album_key.clone());
|
artist
|
||||||
|
.albums_as_additional_performer
|
||||||
|
.insert(album_key.clone());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -307,12 +315,13 @@ impl Builder {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_song_to_album(&mut self, song: &storage::Song) {
|
fn add_song_to_album(&mut self, song: &storage::Song) {
|
||||||
let album_key = song.album_key();
|
let Some(album_key) = song.album_key() else {
|
||||||
let album = self.albums.entry(album_key).or_default().borrow_mut();
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
if album.name.is_none() {
|
let name = album_key.name;
|
||||||
album.name = song.album;
|
let album = self.albums.entry(album_key).or_default().borrow_mut();
|
||||||
}
|
album.name = name;
|
||||||
|
|
||||||
if album.artwork.is_none() {
|
if album.artwork.is_none() {
|
||||||
album.artwork = song.artwork;
|
album.artwork = song.artwork;
|
||||||
|
@ -500,10 +509,7 @@ mod test {
|
||||||
assert_eq!(albums.len(), 2);
|
assert_eq!(albums.len(), 2);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
albums
|
albums.into_iter().map(|a| a.name).collect::<HashSet<_>>(),
|
||||||
.into_iter()
|
|
||||||
.map(|a| a.name.unwrap())
|
|
||||||
.collect::<HashSet<_>>(),
|
|
||||||
HashSet::from_iter(["ISDN".to_owned(), "Lifeforms".to_owned()])
|
HashSet::from_iter(["ISDN".to_owned(), "Lifeforms".to_owned()])
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -527,10 +533,7 @@ mod test {
|
||||||
assert_eq!(albums.len(), 2);
|
assert_eq!(albums.len(), 2);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
albums
|
albums.into_iter().map(|a| a.name).collect::<Vec<_>>(),
|
||||||
.into_iter()
|
|
||||||
.map(|a| a.name.unwrap())
|
|
||||||
.collect::<Vec<_>>(),
|
|
||||||
vec!["ISDN".to_owned(), "Lifeforms".to_owned()]
|
vec!["ISDN".to_owned(), "Lifeforms".to_owned()]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -617,11 +620,7 @@ mod test {
|
||||||
};
|
};
|
||||||
let artist = collection.get_artist(&strings, artist_key).unwrap();
|
let artist = collection.get_artist(&strings, artist_key).unwrap();
|
||||||
|
|
||||||
let names = |a: &Vec<Album>| {
|
let names = |a: &Vec<Album>| a.iter().map(|a| a.name.to_owned()).collect::<Vec<_>>();
|
||||||
a.iter()
|
|
||||||
.map(|a| a.name.to_owned().unwrap())
|
|
||||||
.collect::<Vec<_>>()
|
|
||||||
};
|
|
||||||
|
|
||||||
if test.expect_performer {
|
if test.expect_performer {
|
||||||
assert_eq!(names(&artist.albums_as_performer), vec![album_name]);
|
assert_eq!(names(&artist.albums_as_performer), vec![album_name]);
|
||||||
|
@ -692,7 +691,7 @@ mod test {
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.albums_as_performer
|
.albums_as_performer
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|a| a.name.unwrap())
|
.map(|a| a.name)
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
@ -747,7 +746,7 @@ mod test {
|
||||||
&strings,
|
&strings,
|
||||||
AlbumKey {
|
AlbumKey {
|
||||||
artists: TinyVec::new(),
|
artists: TinyVec::new(),
|
||||||
name: strings.get("Lifeforms"),
|
name: strings.get("Lifeforms").unwrap(),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -29,7 +29,7 @@ pub struct Artist {
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
|
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
|
||||||
pub struct Album {
|
pub struct Album {
|
||||||
pub name: Option<Spur>,
|
pub name: Spur,
|
||||||
pub artwork: Option<PathKey>,
|
pub artwork: Option<PathKey>,
|
||||||
pub artists: TinyVec<[Spur; 1]>,
|
pub artists: TinyVec<[Spur; 1]>,
|
||||||
pub year: Option<i64>,
|
pub year: Option<i64>,
|
||||||
|
@ -70,7 +70,7 @@ pub struct ArtistKey {
|
||||||
#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
|
#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
|
||||||
pub struct AlbumKey {
|
pub struct AlbumKey {
|
||||||
pub artists: TinyVec<[Spur; 4]>,
|
pub artists: TinyVec<[Spur; 4]>,
|
||||||
pub name: Option<Spur>,
|
pub name: Spur,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Default, Eq, Hash, PartialEq, Serialize, Deserialize)]
|
#[derive(Copy, Clone, Debug, Default, Eq, Hash, PartialEq, Serialize, Deserialize)]
|
||||||
|
@ -79,15 +79,18 @@ pub struct SongKey {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Song {
|
impl Song {
|
||||||
pub fn album_key(&self) -> AlbumKey {
|
pub fn album_key(&self) -> Option<AlbumKey> {
|
||||||
let album_artists = match self.album_artists.is_empty() {
|
let album_artists = match self.album_artists.is_empty() {
|
||||||
true => &self.artists,
|
true => &self.artists,
|
||||||
false => &self.album_artists,
|
false => &self.album_artists,
|
||||||
};
|
};
|
||||||
|
|
||||||
AlbumKey {
|
match self.album {
|
||||||
artists: album_artists.iter().cloned().collect(),
|
None => None,
|
||||||
name: self.album.clone(),
|
Some(name) => Some(AlbumKey {
|
||||||
|
artists: album_artists.iter().cloned().collect(),
|
||||||
|
name,
|
||||||
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -119,10 +122,15 @@ pub fn store_song(
|
||||||
' ' | '_' | '-' | '\'' => false,
|
' ' | '_' | '-' | '\'' => false,
|
||||||
_ => true,
|
_ => true,
|
||||||
});
|
});
|
||||||
minuscules
|
match cleaned.is_empty() {
|
||||||
.entry(cleaned.to_lowercase())
|
true => None,
|
||||||
.or_insert_with(|| strings.get_or_intern(s))
|
false => Some(
|
||||||
.to_owned()
|
minuscules
|
||||||
|
.entry(cleaned.to_lowercase())
|
||||||
|
.or_insert_with(|| strings.get_or_intern(s))
|
||||||
|
.to_owned(),
|
||||||
|
),
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Some(Song {
|
Some(Song {
|
||||||
|
@ -130,47 +138,29 @@ pub fn store_song(
|
||||||
virtual_path,
|
virtual_path,
|
||||||
track_number: song.track_number,
|
track_number: song.track_number,
|
||||||
disc_number: song.disc_number,
|
disc_number: song.disc_number,
|
||||||
title: song.title.as_ref().map(&mut canonicalize),
|
title: song.title.as_ref().and_then(&mut canonicalize),
|
||||||
artists: song
|
artists: song.artists.iter().filter_map(&mut canonicalize).collect(),
|
||||||
.artists
|
|
||||||
.iter()
|
|
||||||
.filter(|s| s.len() > 0)
|
|
||||||
.map(&mut canonicalize)
|
|
||||||
.collect(),
|
|
||||||
album_artists: song
|
album_artists: song
|
||||||
.album_artists
|
.album_artists
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|s| s.len() > 0)
|
.filter_map(&mut canonicalize)
|
||||||
.map(&mut canonicalize)
|
|
||||||
.collect(),
|
.collect(),
|
||||||
year: song.year,
|
year: song.year,
|
||||||
album: song.album.as_ref().map(&mut canonicalize),
|
album: song.album.as_ref().and_then(&mut canonicalize),
|
||||||
artwork: artwork,
|
artwork: artwork,
|
||||||
duration: song.duration,
|
duration: song.duration,
|
||||||
lyricists: song
|
lyricists: song
|
||||||
.lyricists
|
.lyricists
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|s| s.len() > 0)
|
.filter_map(&mut canonicalize)
|
||||||
.map(&mut canonicalize)
|
|
||||||
.collect(),
|
.collect(),
|
||||||
composers: song
|
composers: song
|
||||||
.composers
|
.composers
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|s| s.len() > 0)
|
.filter_map(&mut canonicalize)
|
||||||
.map(&mut canonicalize)
|
|
||||||
.collect(),
|
|
||||||
genres: song
|
|
||||||
.genres
|
|
||||||
.iter()
|
|
||||||
.filter(|s| s.len() > 0)
|
|
||||||
.map(&mut canonicalize)
|
|
||||||
.collect(),
|
|
||||||
labels: song
|
|
||||||
.labels
|
|
||||||
.iter()
|
|
||||||
.filter(|s| s.len() > 0)
|
|
||||||
.map(&mut canonicalize)
|
|
||||||
.collect(),
|
.collect(),
|
||||||
|
genres: song.genres.iter().filter_map(&mut canonicalize).collect(),
|
||||||
|
labels: song.labels.iter().filter_map(&mut canonicalize).collect(),
|
||||||
date_added: song.date_added,
|
date_added: song.date_added,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -419,9 +419,7 @@ async fn get_album(
|
||||||
.split(API_ARRAY_SEPARATOR)
|
.split(API_ARRAY_SEPARATOR)
|
||||||
.map(str::to_owned)
|
.map(str::to_owned)
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
Ok(Json(
|
Ok(Json(index_manager.get_album(artists, name).await?.into()))
|
||||||
index_manager.get_album(artists, Some(name)).await?.into(),
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_songs(
|
async fn get_songs(
|
||||||
|
|
|
@ -363,7 +363,7 @@ impl From<index::Album> for Directory {
|
||||||
false => Some(album.artists.join("")),
|
false => Some(album.artists.join("")),
|
||||||
},
|
},
|
||||||
year: album.year,
|
year: album.year,
|
||||||
album: album.name,
|
album: Some(album.name),
|
||||||
artwork: album.artwork,
|
artwork: album.artwork,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -368,8 +368,7 @@ impl From<index::Artist> for Artist {
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
pub struct AlbumHeader {
|
pub struct AlbumHeader {
|
||||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
pub name: String,
|
||||||
pub name: Option<String>,
|
|
||||||
#[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")]
|
||||||
|
|
Loading…
Add table
Reference in a new issue