mirror of
https://github.com/Dummi26/musicdb.git
synced 2025-03-10 14:13:53 +01:00
albums must have an artist + some client gui_edit changes
This commit is contained in:
parent
c93b933037
commit
a465f2be79
@ -46,11 +46,8 @@ pub enum Editing {
|
|||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub enum ArtistChange {
|
pub enum ArtistChange {
|
||||||
SetName(String),
|
SetName(String),
|
||||||
SetCover(Option<ArtistId>),
|
SetCover(Option<CoverId>),
|
||||||
RemoveAlbum(AlbumId),
|
|
||||||
AddAlbum(AlbumId),
|
AddAlbum(AlbumId),
|
||||||
RemoveSong(SongId),
|
|
||||||
AddSong(SongId),
|
|
||||||
}
|
}
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub enum AlbumChange {
|
pub enum AlbumChange {
|
||||||
@ -163,39 +160,59 @@ impl GuiElemTrait for GuiEdit {
|
|||||||
}
|
}
|
||||||
if self.send {
|
if self.send {
|
||||||
self.send = false;
|
self.send = false;
|
||||||
match &self.editing {
|
self.rebuild_main = true;
|
||||||
|
self.rebuild_changes = true;
|
||||||
|
self.config.redraw = true;
|
||||||
|
match &mut self.editing {
|
||||||
Editing::NotLoaded => {}
|
Editing::NotLoaded => {}
|
||||||
Editing::Artist(v, changes) => {
|
Editing::Artist(v, changes) => {
|
||||||
for v in v {
|
for change in changes.iter() {
|
||||||
let mut v = v.clone();
|
match change {
|
||||||
for change in changes.iter() {
|
ArtistChange::SetName(n) => {
|
||||||
match change {
|
for artist in v.iter_mut() {
|
||||||
ArtistChange::SetName(n) => v.name = n.clone(),
|
artist.name = n.clone();
|
||||||
ArtistChange::SetCover(c) => v.cover = c.clone(),
|
info.actions.push(GuiAction::SendToServer(
|
||||||
ArtistChange::RemoveAlbum(id) => {
|
Command::ModifyArtist(artist.clone()),
|
||||||
if let Some(i) = v.albums.iter().position(|id| id == id) {
|
));
|
||||||
v.albums.remove(i);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
ArtistChange::AddAlbum(id) => {
|
}
|
||||||
if !v.albums.contains(id) {
|
ArtistChange::SetCover(c) => {
|
||||||
v.albums.push(*id);
|
for artist in v.iter_mut() {
|
||||||
}
|
artist.cover = c.clone();
|
||||||
|
info.actions.push(GuiAction::SendToServer(
|
||||||
|
Command::ModifyArtist(artist.clone()),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
ArtistChange::RemoveSong(id) => {
|
}
|
||||||
if let Some(i) = v.singles.iter().position(|id| id == id) {
|
ArtistChange::AddAlbum(id) => {
|
||||||
v.singles.remove(i);
|
// use the first artist for the artist fields
|
||||||
|
let mut editing = v.first().unwrap().clone();
|
||||||
|
if let Some(album) = info.database.albums().get(id) {
|
||||||
|
let mut album = album.clone();
|
||||||
|
// find the previous artist for this album and remove them
|
||||||
|
if let Some(prev) = info.database.artists().get(&album.artist) {
|
||||||
|
let mut prev = prev.clone();
|
||||||
|
if let Some(i) = prev.albums.iter().position(|v| v == id) {
|
||||||
|
prev.albums.remove(i);
|
||||||
|
info.actions.push(GuiAction::SendToServer(
|
||||||
|
Command::ModifyArtist(prev),
|
||||||
|
));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
// update the artist field on the album so it points to the new artist
|
||||||
ArtistChange::AddSong(id) => {
|
album.artist = editing.id;
|
||||||
if !v.singles.contains(id) {
|
info.actions
|
||||||
v.singles.push(*id);
|
.push(GuiAction::SendToServer(Command::ModifyAlbum(album)));
|
||||||
|
// add the album to the artist we are editing
|
||||||
|
if !editing.albums.contains(id) {
|
||||||
|
editing.albums.push(*id);
|
||||||
|
info.actions.push(GuiAction::SendToServer(
|
||||||
|
Command::ModifyArtist(editing),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
info.actions
|
|
||||||
.push(GuiAction::SendToServer(Command::ModifyArtist(v)));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Editing::Album(v, changes) => {
|
Editing::Album(v, changes) => {
|
||||||
@ -487,18 +504,19 @@ impl GuiEdit {
|
|||||||
let add_button = {
|
let add_button = {
|
||||||
let apply_change = self.apply_change.clone();
|
let apply_change = self.apply_change.clone();
|
||||||
GuiElem::new(Button::new(
|
GuiElem::new(Button::new(
|
||||||
GuiElemCfg::at(Rectangle::from_tuples((0.8, 0.0), (0.9, 1.0))),
|
GuiElemCfg::at(Rectangle::from_tuples((0.9, 0.0), (1.0, 1.0))),
|
||||||
move |_| {
|
move |_| {
|
||||||
_ = apply_change.send(Box::new(move |s| {
|
_ = apply_change.send(Box::new(move |s| {
|
||||||
if let Some(album_id) = get_id(s) {
|
if let Some(album_id) = get_id(s) {
|
||||||
if let Editing::Artist(_, c) = &mut s.editing {
|
if let Editing::Artist(_, c) = &mut s.editing {
|
||||||
if let Some(i) = c.iter().position(|c| {
|
if let Some(i) = c.iter().position(|c| {
|
||||||
matches!(c, ArtistChange::AddAlbum(id) if *id == album_id)
|
matches!(c, ArtistChange::AddAlbum(id) if *id == album_id)
|
||||||
}) {
|
}) {
|
||||||
c.remove(i);
|
c.remove(i);
|
||||||
|
}
|
||||||
|
c.push(ArtistChange::AddAlbum(album_id));
|
||||||
|
s.rebuild_changes = true;
|
||||||
}
|
}
|
||||||
c.push(ArtistChange::AddAlbum(album_id));
|
|
||||||
s.rebuild_changes = true;}
|
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
vec![]
|
vec![]
|
||||||
@ -512,111 +530,21 @@ impl GuiEdit {
|
|||||||
))],
|
))],
|
||||||
))
|
))
|
||||||
};
|
};
|
||||||
let remove_button = {
|
|
||||||
let apply_change = self.apply_change.clone();
|
|
||||||
GuiElem::new(Button::new(
|
|
||||||
GuiElemCfg::at(Rectangle::from_tuples((0.9, 0.0), (1.0, 1.0))),
|
|
||||||
move |_| {
|
|
||||||
_ = apply_change.send(Box::new(move |s| {
|
|
||||||
if let Some(album_id) = get_id(s) {
|
|
||||||
if let Editing::Artist(_, c) = &mut s.editing {
|
|
||||||
if let Some(i) = c.iter().position(|c| {
|
|
||||||
matches!(c, ArtistChange::RemoveAlbum(id) if *id == album_id)
|
|
||||||
}) {
|
|
||||||
c.remove(i);
|
|
||||||
}
|
|
||||||
c.push(ArtistChange::RemoveAlbum(album_id));
|
|
||||||
s.rebuild_changes = true;}
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
vec![]
|
|
||||||
},
|
|
||||||
vec![GuiElem::new(Label::new(
|
|
||||||
GuiElemCfg::default(),
|
|
||||||
format!("remove"),
|
|
||||||
Color::RED,
|
|
||||||
None,
|
|
||||||
Vec2::new(0.5, 0.5),
|
|
||||||
))],
|
|
||||||
))
|
|
||||||
};
|
|
||||||
let name = GuiElem::new(TextField::new(
|
let name = GuiElem::new(TextField::new(
|
||||||
GuiElemCfg::at(Rectangle::from_tuples((0.0, 0.0), (0.8, 1.0))),
|
GuiElemCfg::at(Rectangle::from_tuples((0.0, 0.0), (0.9, 1.0))),
|
||||||
"add or remove album by id".to_string(),
|
"add album by id".to_string(),
|
||||||
Color::LIGHT_GRAY,
|
Color::LIGHT_GRAY,
|
||||||
Color::WHITE,
|
Color::WHITE,
|
||||||
));
|
));
|
||||||
sb.children.push((
|
sb.children.push((
|
||||||
GuiElem::new(Panel::new(
|
GuiElem::new(Panel::new(GuiElemCfg::default(), vec![name, add_button])),
|
||||||
GuiElemCfg::default(),
|
|
||||||
vec![name, add_button, remove_button],
|
|
||||||
)),
|
|
||||||
info.line_height * 2.0,
|
info.line_height * 2.0,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
for (album_id, count) in albums {
|
for (album_id, count) in albums {
|
||||||
let album = info.database.albums().get(&album_id);
|
let album = info.database.albums().get(&album_id);
|
||||||
let add_button = if count == v.len() {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
let apply_change = self.apply_change.clone();
|
|
||||||
Some(GuiElem::new(Button::new(
|
|
||||||
GuiElemCfg::at(Rectangle::from_tuples((0.8, 0.0), (0.9, 1.0))),
|
|
||||||
move |_| {
|
|
||||||
_ = apply_change.send(Box::new(move |s| {
|
|
||||||
if let Editing::Artist(_, c) = &mut s.editing {
|
|
||||||
if let Some(i) = c.iter().position(|c| {
|
|
||||||
matches!(c, ArtistChange::AddAlbum(id) if *id == album_id)
|
|
||||||
}) {
|
|
||||||
c.remove(i);
|
|
||||||
}
|
|
||||||
c.push(ArtistChange::AddAlbum(album_id));
|
|
||||||
s.rebuild_changes = true;
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
vec![]
|
|
||||||
},
|
|
||||||
vec![GuiElem::new(Label::new(
|
|
||||||
GuiElemCfg::default(),
|
|
||||||
format!("add"),
|
|
||||||
Color::GREEN,
|
|
||||||
None,
|
|
||||||
Vec2::new(0.5, 0.5),
|
|
||||||
))],
|
|
||||||
)))
|
|
||||||
};
|
|
||||||
let remove_button = {
|
|
||||||
let apply_change = self.apply_change.clone();
|
|
||||||
GuiElem::new(Button::new(
|
|
||||||
GuiElemCfg::at(Rectangle::from_tuples((0.9, 0.0), (1.0, 1.0))),
|
|
||||||
move |_| {
|
|
||||||
_ = apply_change.send(Box::new(move |s| {
|
|
||||||
if let Editing::Artist(_, c) = &mut s.editing {
|
|
||||||
if let Some(i) = c.iter().position(|c| {
|
|
||||||
matches!(c, ArtistChange::RemoveAlbum(id) if *id == album_id)
|
|
||||||
}) {
|
|
||||||
c.remove(i);
|
|
||||||
}
|
|
||||||
c.push(ArtistChange::RemoveAlbum(album_id));
|
|
||||||
s.rebuild_changes = true;
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
vec![]
|
|
||||||
},
|
|
||||||
vec![GuiElem::new(Label::new(
|
|
||||||
GuiElemCfg::default(),
|
|
||||||
format!("remove"),
|
|
||||||
Color::RED,
|
|
||||||
None,
|
|
||||||
Vec2::new(0.5, 0.5),
|
|
||||||
))],
|
|
||||||
))
|
|
||||||
};
|
|
||||||
let name = GuiElem::new(Button::new(
|
let name = GuiElem::new(Button::new(
|
||||||
GuiElemCfg::at(Rectangle::from_tuples(
|
GuiElemCfg::at(Rectangle::from_tuples((0.0, 0.0), (1.0, 1.0))),
|
||||||
(0.0, 0.0),
|
|
||||||
(if add_button.is_some() { 0.8 } else { 0.9 }, 1.0),
|
|
||||||
)),
|
|
||||||
move |_| {
|
move |_| {
|
||||||
vec![GuiAction::OpenEditPanel(GuiElem::new(GuiEdit::new(
|
vec![GuiAction::OpenEditPanel(GuiElem::new(GuiEdit::new(
|
||||||
GuiElemCfg::default(),
|
GuiElemCfg::default(),
|
||||||
@ -636,14 +564,7 @@ impl GuiEdit {
|
|||||||
))],
|
))],
|
||||||
));
|
));
|
||||||
sb.children.push((
|
sb.children.push((
|
||||||
GuiElem::new(Panel::new(
|
GuiElem::new(Panel::new(GuiElemCfg::default(), vec![name])),
|
||||||
GuiElemCfg::default(),
|
|
||||||
if let Some(add_button) = add_button {
|
|
||||||
vec![name, add_button, remove_button]
|
|
||||||
} else {
|
|
||||||
vec![name, remove_button]
|
|
||||||
},
|
|
||||||
)),
|
|
||||||
info.line_height,
|
info.line_height,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
@ -670,10 +591,7 @@ impl GuiEdit {
|
|||||||
"remove cover".to_string()
|
"remove cover".to_string()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ArtistChange::RemoveAlbum(v) => format!("remove album {v}"),
|
|
||||||
ArtistChange::AddAlbum(v) => format!("add album {v}"),
|
ArtistChange::AddAlbum(v) => format!("add album {v}"),
|
||||||
ArtistChange::RemoveSong(v) => format!("remove song {v}"),
|
|
||||||
ArtistChange::AddSong(v) => format!("add song {v}"),
|
|
||||||
};
|
};
|
||||||
let s = self.apply_change.clone();
|
let s = self.apply_change.clone();
|
||||||
sb.children.push((
|
sb.children.push((
|
||||||
|
@ -130,11 +130,7 @@ impl GuiElemTrait for CurrentSong {
|
|||||||
(None, None) => String::new(),
|
(None, None) => String::new(),
|
||||||
(Some(artist), None) => format!("by {}", artist.name),
|
(Some(artist), None) => format!("by {}", artist.name),
|
||||||
(None, Some(album)) => {
|
(None, Some(album)) => {
|
||||||
if let Some(artist) = album
|
if let Some(artist) = info.database.artists().get(&album.artist) {
|
||||||
.artist
|
|
||||||
.as_ref()
|
|
||||||
.and_then(|id| info.database.artists().get(id))
|
|
||||||
{
|
|
||||||
format!("on {} by {}", album.name, artist.name)
|
format!("on {} by {}", album.name, artist.name)
|
||||||
} else {
|
} else {
|
||||||
format!("on {}", album.name)
|
format!("on {}", album.name)
|
||||||
|
@ -404,9 +404,7 @@ impl QueueSong {
|
|||||||
(None, None) => String::new(),
|
(None, None) => String::new(),
|
||||||
(Some(artist), None) => format!("by {}", artist.name),
|
(Some(artist), None) => format!("by {}", artist.name),
|
||||||
(None, Some(album)) => {
|
(None, Some(album)) => {
|
||||||
if let Some(artist) =
|
if let Some(artist) = db.artists().get(&album.artist) {
|
||||||
album.artist.as_ref().and_then(|id| db.artists().get(id))
|
|
||||||
{
|
|
||||||
format!("on {} by {}", album.name, artist.name)
|
format!("on {} by {}", album.name, artist.name)
|
||||||
} else {
|
} else {
|
||||||
format!("on {}", album.name)
|
format!("on {}", album.name)
|
||||||
|
@ -47,65 +47,83 @@ fn main() {
|
|||||||
}
|
}
|
||||||
eprintln!("\nloaded metadata of {} files.", songs.len());
|
eprintln!("\nloaded metadata of {} files.", songs.len());
|
||||||
let mut database = Database::new_empty(PathBuf::from("dbfile"), PathBuf::from(&lib_dir));
|
let mut database = Database::new_empty(PathBuf::from("dbfile"), PathBuf::from(&lib_dir));
|
||||||
|
let unknown_artist = database.add_artist_new(Artist {
|
||||||
|
id: 0,
|
||||||
|
name: format!("<unknown>"),
|
||||||
|
cover: None,
|
||||||
|
albums: vec![],
|
||||||
|
singles: vec![],
|
||||||
|
general: GeneralData::default(),
|
||||||
|
});
|
||||||
eprintln!("searching for artists...");
|
eprintln!("searching for artists...");
|
||||||
let mut artists = HashMap::new();
|
let mut artists = HashMap::new();
|
||||||
for song in songs {
|
for song in songs {
|
||||||
let (artist_id, album_id) =
|
let (artist_id, album_id) = if let Some(artist) = song
|
||||||
if let Some(artist) = song.1.album_artist().or_else(|| song.1.artist()) {
|
.1
|
||||||
let artist_id = if !artists.contains_key(artist) {
|
.album_artist()
|
||||||
let artist_id = database.add_artist_new(Artist {
|
.or_else(|| song.1.artist())
|
||||||
|
.filter(|v| !v.trim().is_empty())
|
||||||
|
{
|
||||||
|
let artist_id = if !artists.contains_key(artist) {
|
||||||
|
let artist_id = database.add_artist_new(Artist {
|
||||||
|
id: 0,
|
||||||
|
name: artist.to_string(),
|
||||||
|
cover: None,
|
||||||
|
albums: vec![],
|
||||||
|
singles: vec![],
|
||||||
|
general: GeneralData::default(),
|
||||||
|
});
|
||||||
|
artists.insert(artist.to_string(), (artist_id, HashMap::new()));
|
||||||
|
artist_id
|
||||||
|
} else {
|
||||||
|
artists.get(artist).unwrap().0
|
||||||
|
};
|
||||||
|
if let Some(album) = song.1.album() {
|
||||||
|
let (_, albums) = artists.get_mut(artist).unwrap();
|
||||||
|
let album_id = if !albums.contains_key(album) {
|
||||||
|
let album_id = database.add_album_new(Album {
|
||||||
id: 0,
|
id: 0,
|
||||||
name: artist.to_string(),
|
artist: artist_id,
|
||||||
|
name: album.to_string(),
|
||||||
cover: None,
|
cover: None,
|
||||||
albums: vec![],
|
songs: vec![],
|
||||||
singles: vec![],
|
|
||||||
general: GeneralData::default(),
|
general: GeneralData::default(),
|
||||||
});
|
});
|
||||||
artists.insert(artist.to_string(), (artist_id, HashMap::new()));
|
albums.insert(
|
||||||
artist_id
|
album.to_string(),
|
||||||
|
(album_id, song.0.parent().map(|dir| dir.to_path_buf())),
|
||||||
|
);
|
||||||
|
album_id
|
||||||
} else {
|
} else {
|
||||||
artists.get(artist).unwrap().0
|
let album = albums.get_mut(album).unwrap();
|
||||||
|
if album
|
||||||
|
.1
|
||||||
|
.as_ref()
|
||||||
|
.is_some_and(|dir| Some(dir.as_path()) != song.0.parent())
|
||||||
|
{
|
||||||
|
// album directory is inconsistent
|
||||||
|
album.1 = None;
|
||||||
|
}
|
||||||
|
album.0
|
||||||
};
|
};
|
||||||
if let Some(album) = song.1.album() {
|
(artist_id, Some(album_id))
|
||||||
let (_, albums) = artists.get_mut(artist).unwrap();
|
|
||||||
let album_id = if !albums.contains_key(album) {
|
|
||||||
let album_id = database.add_album_new(Album {
|
|
||||||
id: 0,
|
|
||||||
artist: Some(artist_id),
|
|
||||||
name: album.to_string(),
|
|
||||||
cover: None,
|
|
||||||
songs: vec![],
|
|
||||||
general: GeneralData::default(),
|
|
||||||
});
|
|
||||||
albums.insert(
|
|
||||||
album.to_string(),
|
|
||||||
(album_id, song.0.parent().map(|dir| dir.to_path_buf())),
|
|
||||||
);
|
|
||||||
album_id
|
|
||||||
} else {
|
|
||||||
let album = albums.get_mut(album).unwrap();
|
|
||||||
if album
|
|
||||||
.1
|
|
||||||
.as_ref()
|
|
||||||
.is_some_and(|dir| Some(dir.as_path()) != song.0.parent())
|
|
||||||
{
|
|
||||||
// album directory is inconsistent
|
|
||||||
album.1 = None;
|
|
||||||
}
|
|
||||||
album.0
|
|
||||||
};
|
|
||||||
(Some(artist_id), Some(album_id))
|
|
||||||
} else {
|
|
||||||
(Some(artist_id), None)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
(None, None)
|
(artist_id, None)
|
||||||
};
|
}
|
||||||
|
} else {
|
||||||
|
(unknown_artist, None)
|
||||||
|
};
|
||||||
let path = song.0.strip_prefix(&lib_dir).unwrap();
|
let path = song.0.strip_prefix(&lib_dir).unwrap();
|
||||||
let title = song
|
let title = song
|
||||||
.1
|
.1
|
||||||
.title()
|
.title()
|
||||||
.map(|title| title.to_string())
|
.map_or(None, |title| {
|
||||||
|
if title.trim().is_empty() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(title.to_string())
|
||||||
|
}
|
||||||
|
})
|
||||||
.unwrap_or_else(|| song.0.file_stem().unwrap().to_string_lossy().into_owned());
|
.unwrap_or_else(|| song.0.file_stem().unwrap().to_string_lossy().into_owned());
|
||||||
database.add_song_new(Song {
|
database.add_song_new(Song {
|
||||||
id: 0,
|
id: 0,
|
||||||
@ -152,7 +170,15 @@ fn main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
eprintln!("\nsaving dbfile...");
|
eprintln!();
|
||||||
|
if let Some(uka) = database.artists().get(&unknown_artist) {
|
||||||
|
if uka.albums.is_empty() && uka.singles.is_empty() {
|
||||||
|
database.artists_mut().remove(&unknown_artist);
|
||||||
|
} else {
|
||||||
|
eprintln!("Added the <unknown> artist as a fallback!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
eprintln!("saving dbfile...");
|
||||||
database.save_database(None).unwrap();
|
database.save_database(None).unwrap();
|
||||||
eprintln!("done!");
|
eprintln!("done!");
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@ use super::{AlbumId, ArtistId, CoverId, GeneralData, SongId};
|
|||||||
pub struct Album {
|
pub struct Album {
|
||||||
pub id: AlbumId,
|
pub id: AlbumId,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub artist: Option<ArtistId>,
|
pub artist: ArtistId,
|
||||||
pub cover: Option<CoverId>,
|
pub cover: Option<CoverId>,
|
||||||
pub songs: Vec<SongId>,
|
pub songs: Vec<SongId>,
|
||||||
pub general: GeneralData,
|
pub general: GeneralData,
|
||||||
|
@ -121,7 +121,7 @@ impl Database {
|
|||||||
pub fn add_album_new(&mut self, album: Album) -> AlbumId {
|
pub fn add_album_new(&mut self, album: Album) -> AlbumId {
|
||||||
let artist = album.artist.clone();
|
let artist = album.artist.clone();
|
||||||
let id = self.add_album_new_nomagic(album);
|
let id = self.add_album_new_nomagic(album);
|
||||||
if let Some(Some(artist)) = artist.map(|v| self.artists.get_mut(&v)) {
|
if let Some(artist) = self.artists.get_mut(&artist) {
|
||||||
artist.albums.push(id);
|
artist.albums.push(id);
|
||||||
}
|
}
|
||||||
id
|
id
|
||||||
|
Loading…
Reference in New Issue
Block a user