diff --git a/musicdb-client/src/gui.rs b/musicdb-client/src/gui.rs index b8dec35..1c85c58 100755 --- a/musicdb-client/src/gui.rs +++ b/musicdb-client/src/gui.rs @@ -1106,6 +1106,8 @@ impl Gui { } } GuiAction::SendToServer(cmd) => { + #[cfg(debug_assertions)] + eprintln!("[DEBUG] Sending command to server: {cmd:?}"); if let Err(e) = cmd.to_bytes(&mut self.connection) { eprintln!("Error sending command to server: {e}"); } diff --git a/musicdb-client/src/gui_library.rs b/musicdb-client/src/gui_library.rs index 034bc22..ec13d59 100755 --- a/musicdb-client/src/gui_library.rs +++ b/musicdb-client/src/gui_library.rs @@ -2157,7 +2157,7 @@ mod selected { let lock = self.0.lock().unwrap(); let (sel_artists, sel_albums, sel_songs) = &*lock; let mut out = vec![]; - for (artist, singles, albums, _) in &lb.library_filtered { + for (artist, singles, albums) in &lb.library_sorted { let artist_selected = sel_artists.contains(artist); let mut local_artist_owned = vec![]; let mut local_artist = if artist_selected { @@ -2165,13 +2165,13 @@ mod selected { } else { &mut out }; - for (song, _) in singles { + for song in singles { let song_selected = sel_songs.contains(song); if song_selected { local_artist.push(QueueContent::Song(*song).into()); } } - for (album, songs, _) in albums { + for (album, songs) in albums { let album_selected = sel_albums.contains(album); let mut local_album_owned = vec![]; let local_album = if album_selected { @@ -2179,7 +2179,7 @@ mod selected { } else { &mut local_artist }; - for (song, _) in songs { + for song in songs { let song_selected = sel_songs.contains(song); if song_selected { local_album.push(QueueContent::Song(*song).into()); diff --git a/musicdb-client/src/gui_queue.rs b/musicdb-client/src/gui_queue.rs index d6f2c1c..d59380d 100755 --- a/musicdb-client/src/gui_queue.rs +++ b/musicdb-client/src/gui_queue.rs @@ -408,7 +408,7 @@ impl GuiElem for QueueEmptySpaceDragHandler { self } fn dragged(&mut self, dragged: Dragging) -> Vec { - dragged_add_to_queue(dragged, |q, _| Command::QueueAdd(vec![], q)) + dragged_add_to_queue(dragged, |q| Command::QueueAdd(vec![], q)) } } @@ -623,9 +623,9 @@ impl GuiElem for QueueSong { if !self.always_copy { let mut p = self.path.clone(); let insert_below = self.insert_below; - dragged_add_to_queue(dragged, move |q, i| { + dragged_add_to_queue(dragged, move |q| { if let Some(j) = p.pop() { - Command::QueueInsert(p.clone(), if insert_below { j + 1 } else { j } + i, q) + Command::QueueInsert(p.clone(), if insert_below { j + 1 } else { j }, q) } else { Command::QueueAdd(p.clone(), q) } @@ -782,13 +782,11 @@ impl GuiElem for QueueFolder { if !self.always_copy { if self.insert_into { let p = self.path.clone(); - dragged_add_to_queue(dragged, move |q, _| Command::QueueAdd(p.clone(), q)) + dragged_add_to_queue(dragged, move |q| Command::QueueAdd(p.clone(), q)) } else { let mut p = self.path.clone(); let j = p.pop().unwrap_or(0); - dragged_add_to_queue(dragged, move |q, i| { - Command::QueueInsert(p.clone(), j + i, q) - }) + dragged_add_to_queue(dragged, move |q| Command::QueueInsert(p.clone(), j, q)) } } else { vec![] @@ -850,9 +848,7 @@ impl GuiElem for QueueIndentEnd { } fn dragged(&mut self, dragged: Dragging) -> Vec { let (p, j) = self.path_insert.clone(); - dragged_add_to_queue(dragged, move |q, i| { - Command::QueueInsert(p.clone(), j + i, q) - }) + dragged_add_to_queue(dragged, move |q| Command::QueueInsert(p.clone(), j, q)) } } @@ -1004,7 +1000,7 @@ impl GuiElem for QueueLoop { if !self.always_copy { let mut p = self.path.clone(); p.push(0); - dragged_add_to_queue(dragged, move |q, _| Command::QueueAdd(p.clone(), q)) + dragged_add_to_queue(dragged, move |q| Command::QueueAdd(p.clone(), q)) } else { vec![] } @@ -1130,7 +1126,7 @@ impl GuiElem for QueueRandom { fn dragged(&mut self, dragged: Dragging) -> Vec { if !self.always_copy { let p = self.path.clone(); - dragged_add_to_queue(dragged, move |q, _| Command::QueueAdd(p.clone(), q)) + dragged_add_to_queue(dragged, move |q| Command::QueueAdd(p.clone(), q)) } else { vec![] } @@ -1257,22 +1253,22 @@ impl GuiElem for QueueShuffle { if !self.always_copy { let mut p = self.path.clone(); p.push(0); - dragged_add_to_queue(dragged, move |q, _| Command::QueueAdd(p.clone(), q)) + dragged_add_to_queue(dragged, move |q| Command::QueueAdd(p.clone(), q)) } else { vec![] } } } -fn dragged_add_to_queue Command + 'static>( +fn dragged_add_to_queue) -> Command + 'static>( dragged: Dragging, - mut f: F, + f: F, ) -> Vec { match dragged { Dragging::Artist(id) => { vec![GuiAction::Build(Box::new(move |db| { if let Some(q) = add_to_queue_artist_by_id(id, db) { - vec![GuiAction::SendToServer(f(q, 0))] + vec![GuiAction::SendToServer(f(vec![q]))] } else { vec![] } @@ -1281,7 +1277,7 @@ fn dragged_add_to_queue Command + 'static>( Dragging::Album(id) => { vec![GuiAction::Build(Box::new(move |db| { if let Some(q) = add_to_queue_album_by_id(id, db) { - vec![GuiAction::SendToServer(f(q, 0))] + vec![GuiAction::SendToServer(f(vec![q]))] } else { vec![] } @@ -1289,16 +1285,12 @@ fn dragged_add_to_queue Command + 'static>( } Dragging::Song(id) => { let q = QueueContent::Song(id).into(); - vec![GuiAction::SendToServer(f(q, 0))] + vec![GuiAction::SendToServer(f(vec![q]))] } Dragging::Queue(q) => { - vec![GuiAction::SendToServer(f(q, 0))] + vec![GuiAction::SendToServer(f(vec![q]))] } - Dragging::Queues(q) => q - .into_iter() - .enumerate() - .map(|(i, q)| GuiAction::SendToServer(f(q, i))) - .collect(), + Dragging::Queues(q) => vec![GuiAction::SendToServer(f(q))], } } diff --git a/musicdb-lib/Cargo.toml b/musicdb-lib/Cargo.toml index d044e26..d633306 100644 --- a/musicdb-lib/Cargo.toml +++ b/musicdb-lib/Cargo.toml @@ -8,6 +8,7 @@ edition = "2021" [dependencies] awedio = { version = "0.2.0", optional = true } base64 = "0.21.2" +colorize = "0.1.0" rand = "0.8.5" rc-u8-reader = "2.0.16" tokio = { version = "1.29.1", features = ["sync"] } diff --git a/musicdb-lib/src/data/database.rs b/musicdb-lib/src/data/database.rs index c52d3de..4017945 100755 --- a/musicdb-lib/src/data/database.rs +++ b/musicdb-lib/src/data/database.rs @@ -7,6 +7,8 @@ use std::{ time::{Duration, Instant}, }; +use colorize::AnsiColor; + use crate::{load::ToFromBytes, server::Command}; use super::{ @@ -263,7 +265,7 @@ impl Database { } Command::Save => { if let Err(e) = self.save_database(None) { - eprintln!("Couldn't save: {e}"); + eprintln!("[{}] Couldn't save: {e}", "ERR!".red()); } } Command::SyncDatabase(a, b, c) => self.sync(a, b, c), @@ -274,28 +276,17 @@ impl Database { } Queue::handle_actions(self, actions); } - Command::QueueAdd(mut index, new_data) => { + Command::QueueAdd(index, new_data) => { let mut actions = vec![]; if let Some(v) = self.queue.get_item_at_index_mut(&index, 0, &mut actions) { - if let Some(i) = v.add_to_end(new_data) { - index.push(i); - if let Some(q) = self.queue.get_item_at_index_mut(&index, 0, &mut actions) { - let mut actions = Vec::new(); - q.init(index, &mut actions); - Queue::handle_actions(self, actions); - } - } + v.add_to_end(new_data, index, &mut actions); } Queue::handle_actions(self, actions); } - Command::QueueInsert(mut index, pos, mut new_data) => { + Command::QueueInsert(index, pos, new_data) => { let mut actions = vec![]; if let Some(v) = self.queue.get_item_at_index_mut(&index, 0, &mut actions) { - index.push(pos); - let mut actions = Vec::new(); - new_data.init(index, &mut actions); - v.insert(new_data, pos); - Queue::handle_actions(self, actions); + v.insert(new_data, pos, index, &mut actions); } Queue::handle_actions(self, actions); } @@ -316,7 +307,7 @@ impl Database { if let Some(a) = o.get_mut(i).and_then(Option::take) { v.push(a); } else { - eprintln!("[warn] Can't properly apply requested order to Queue/Shuffle: no element at index {i}. Index may be out of bounds or used twice. Len: {}, Order: {order:?}.", v.len()); + eprintln!("[{}] Can't properly apply requested order to Queue/Shuffle: no element at index {i}. Index may be out of bounds or used twice. Len: {}, Order: {order:?}.", "WARN".yellow(), v.len()); } } } @@ -327,7 +318,10 @@ impl Database { ); } } else { - eprintln!("[warn] can't QueueSetShuffle - no element at path {path:?}"); + eprintln!( + "[{}] can't QueueSetShuffle - no element at path {path:?}", + "WARN".yellow() + ); } Queue::handle_actions(self, actions); } @@ -365,42 +359,42 @@ impl Database { v.general.tags.push(tag); } } - }, + } Command::TagSongFlagUnset(id, tag) => { if let Some(v) = self.get_song_mut(&id) { if let Some(i) = v.general.tags.iter().position(|v| v == &tag) { v.general.tags.remove(i); } } - }, + } Command::TagAlbumFlagSet(id, tag) => { if let Some(v) = self.albums.get_mut(&id) { if !v.general.tags.contains(&tag) { v.general.tags.push(tag); } } - }, + } Command::TagAlbumFlagUnset(id, tag) => { if let Some(v) = self.albums.get_mut(&id) { if let Some(i) = v.general.tags.iter().position(|v| v == &tag) { v.general.tags.remove(i); } } - }, + } Command::TagArtistFlagSet(id, tag) => { if let Some(v) = self.artists.get_mut(&id) { if !v.general.tags.contains(&tag) { v.general.tags.push(tag); } } - }, + } Command::TagArtistFlagUnset(id, tag) => { if let Some(v) = self.artists.get_mut(&id) { if let Some(i) = v.general.tags.iter().position(|v| v == &tag) { v.general.tags.remove(i); } } - }, + } Command::TagSongPropertySet(id, key, val) => { if let Some(v) = self.get_song_mut(&id) { let new = format!("{key}{val}"); @@ -507,8 +501,8 @@ impl Database { } pub fn load_database(path: PathBuf, lib_directory: PathBuf) -> Result { let mut file = BufReader::new(File::open(&path)?); - eprintln!("[info] loading library from {file:?}"); - Ok(Self { + eprintln!("[{}] loading library from {file:?}", "INFO".cyan()); + let s = Self { db_file: path, lib_directory, artists: ToFromBytes::from_bytes(&mut file)?, @@ -524,7 +518,9 @@ impl Database { command_sender: None, remote_server_as_song_file_source: None, client_is_init: false, - }) + }; + eprintln!("[{}] loaded library", "INFO".green()); + Ok(s) } /// saves the database's contents. save path can be overridden pub fn save_database(&self, path: Option) -> Result { @@ -537,7 +533,7 @@ impl Database { if path.as_os_str().is_empty() { return Ok(path); } - eprintln!("[info] saving db to {path:?}."); + eprintln!("[{}] saving db to {path:?}", "INFO".cyan()); let mut file = fs::OpenOptions::new() .write(true) .truncate(true) @@ -547,6 +543,7 @@ impl Database { self.albums.to_bytes(&mut file)?; self.songs.to_bytes(&mut file)?; self.covers.to_bytes(&mut file)?; + eprintln!("[{}] saved db", "INFO".green()); Ok(path) } pub fn broadcast_update(&mut self, update: &Command) { diff --git a/musicdb-lib/src/data/queue.rs b/musicdb-lib/src/data/queue.rs index 65f5346..1038244 100755 --- a/musicdb-lib/src/data/queue.rs +++ b/musicdb-lib/src/data/queue.rs @@ -31,6 +31,7 @@ pub enum ShuffleState { pub enum QueueAction { AddRandomSong(Vec), + /// `partial: bool`, if true, indicates that we only shuffle what is beyond the current index SetShuffle(Vec, bool), } @@ -45,30 +46,64 @@ impl Queue { &mut self.content } - pub fn add_to_end(&mut self, v: Self) -> Option { + pub fn add_to_end( + &mut self, + v: Vec, + mut path: Vec, + actions: &mut Vec, + ) -> Option { match &mut self.content { QueueContent::Song(_) => None, QueueContent::Folder(_, vec, _) => { - vec.push(v); - Some(vec.len() - 1) + let len = vec.len(); + for (i, mut v) in v.into_iter().enumerate() { + path.push(len + i); + v.init(path.clone(), actions); + vec.push(v); + path.pop(); + } + Some(len) } QueueContent::Loop(..) => None, QueueContent::Random(q) => { - q.push_back(v); - Some(q.len() - 1) + // insert new elements + let len = q.len(); + for (i, mut v) in v.into_iter().enumerate() { + path.push(len + i); + v.init(path.clone(), actions); + q.push_back(v); + path.pop(); + } + Some(len) } QueueContent::Shuffle { .. } => None, } } - pub fn insert(&mut self, v: Self, index: usize) -> bool { + pub fn insert( + &mut self, + v: Vec, + index: usize, + mut path: Vec, + actions: &mut Vec, + ) -> bool { match &mut self.content { QueueContent::Song(_) => false, QueueContent::Folder(current, vec, _) => { if index <= vec.len() { if *current >= index { - *current += 1; + *current += v.len(); } - vec.insert(index, v); + // remove the elements starting at the insertion point + let end = vec.split_off(index); + // insert new elements + for (i, mut v) in v.into_iter().enumerate() { + path.push(index + i); + v.init(path.clone(), actions); + vec.push(v); + path.pop(); + } + // re-add previously removed elements + vec.extend(end); true } else { false @@ -268,7 +303,9 @@ impl Queue { QueueContent::Shuffle { inner, state } => { let mut p = path.clone(); p.push(0); - if matches!(state, ShuffleState::NotShuffled | ShuffleState::Modified) { + if inner.len() == 0 { + *state = ShuffleState::NotShuffled; + } else if matches!(state, ShuffleState::NotShuffled | ShuffleState::Modified) { actions.push(QueueAction::SetShuffle( path, matches!(state, ShuffleState::Modified), @@ -287,7 +324,7 @@ impl Queue { if let Some(song) = db.songs().keys().choose(&mut rand::thread_rng()) { db.apply_command(Command::QueueAdd( path, - QueueContent::Song(*song).into(), + vec![QueueContent::Song(*song).into()], )); } } @@ -394,6 +431,7 @@ impl Queue { let mut p = path.clone(); p.push(0); if !inner.advance_index_inner(p, actions) { + // end of inner Folder element, reshuffle for next time *state = ShuffleState::Shuffled; actions.push(QueueAction::SetShuffle(path, false)); false diff --git a/musicdb-lib/src/data/song.rs b/musicdb-lib/src/data/song.rs index 3b2aa6d..85c1bd1 100755 --- a/musicdb-lib/src/data/song.rs +++ b/musicdb-lib/src/data/song.rs @@ -6,6 +6,8 @@ use std::{ thread::JoinHandle, }; +use colorize::AnsiColor; + use crate::load::ToFromBytes; use super::{ @@ -139,20 +141,20 @@ impl Song { ) -> Option> { match src { Ok(path) => { - eprintln!("[info] loading song from {:?}", path); + eprintln!("[{}] loading song from {:?}", "INFO".cyan(), path); match std::fs::read(&path) { Ok(v) => { - eprintln!("[info] loaded song from {:?}", path); + eprintln!("[{}] loaded song from {:?}", "INFO".green(), path); Some(v) } Err(e) => { - eprintln!("[info] error loading {:?}: {e:?}", path); + eprintln!("[{}] error loading {:?}: {e:?}", "ERR!".red(), path); None } } } Err((id, dlcon)) => { - eprintln!("[info] loading song {id}"); + eprintln!("[{}] loading song {id}", "INFO".cyan()); match dlcon .lock() .unwrap() @@ -161,7 +163,7 @@ impl Song { { Ok(data) => Some(data), Err(e) => { - eprintln!("[WARN] error loading song {id}: {e}"); + eprintln!("[{}] error loading song {id}: {e}", "ERR!".red()); None } } diff --git a/musicdb-lib/src/player/mod.rs b/musicdb-lib/src/player/mod.rs index 36ec86d..e6ff308 100755 --- a/musicdb-lib/src/player/mod.rs +++ b/musicdb-lib/src/player/mod.rs @@ -6,6 +6,7 @@ use awedio::{ sounds::wrappers::{AsyncCompletionNotifier, Controller, Pausable}, Sound, }; +use colorize::AnsiColor; use rc_u8_reader::ArcU8Reader; use crate::{ @@ -148,7 +149,10 @@ impl Player { self.manager.play(Box::new(sound)); } Err(e) => { - eprintln!("[player] Can't play, skipping! {e}"); + eprintln!( + "[{}] [player] Can't play, skipping! {e}", + "INFO".blue() + ); apply_command!(Command::NextSong); } } diff --git a/musicdb-lib/src/server/mod.rs b/musicdb-lib/src/server/mod.rs index fd63bad..efa19a1 100755 --- a/musicdb-lib/src/server/mod.rs +++ b/musicdb-lib/src/server/mod.rs @@ -5,6 +5,8 @@ use std::{ sync::{mpsc, Arc, Mutex}, }; +use colorize::AnsiColor; + use crate::{ data::{ album::Album, @@ -34,8 +36,8 @@ pub enum Command { NextSong, SyncDatabase(Vec, Vec, Vec), QueueUpdate(Vec, Queue), - QueueAdd(Vec, Queue), - QueueInsert(Vec, usize, Queue), + QueueAdd(Vec, Vec), + QueueInsert(Vec, usize, Vec), QueueRemove(Vec), QueueGoto(Vec), QueueSetShuffle(Vec, Vec), @@ -165,7 +167,7 @@ pub fn run_server( }); } Err(e) => { - eprintln!("[WARN] Couldn't start TCP listener: {e}"); + eprintln!("[{}] Couldn't start TCP listener: {e}", "ERR!".red()); } } } @@ -242,7 +244,7 @@ const BYTE_TAG_SONG_FLAG_SET: u8 = 0b11100000; const BYTE_TAG_SONG_FLAG_UNSET: u8 = 0b11100001; const BYTE_TAG_ALBUM_FLAG_SET: u8 = 0b11100010; const BYTE_TAG_ALBUM_FLAG_UNSET: u8 = 0b11100011; -const BYTE_TAG_ARTIST_FLAG_SET: u8 = 0b11100110; +const BYTE_TAG_ARTIST_FLAG_SET: u8 = 0b11100110; const BYTE_TAG_ARTIST_FLAG_UNSET: u8 = 0b11100111; const BYTE_TAG_SONG_PROPERTY_SET: u8 = 0b11101001; const BYTE_TAG_SONG_PROPERTY_UNSET: u8 = 0b11101010; @@ -484,7 +486,10 @@ impl ToFromBytes for Command { BYTE_SAVE => Self::Save, BYTE_ERRORINFO => Self::ErrorInfo(from_bytes!(), from_bytes!()), _ => { - eprintln!("unexpected byte when reading command; stopping playback."); + eprintln!( + "[{}] unexpected byte when reading command; stopping playback.", + "WARN".yellow() + ); Self::Stop } }) diff --git a/musicdb-server/src/web.rs b/musicdb-server/src/web.rs index ae0961d..ed2ca12 100755 --- a/musicdb-server/src/web.rs +++ b/musicdb-server/src/web.rs @@ -287,7 +287,7 @@ pub async fn main(db: Arc>, sender: mpsc::Sender, addr: post(move |Path(song_id)| async move { _ = s6.send(Command::QueueAdd( vec![], - QueueContent::Song(song_id).into(), + vec![QueueContent::Song(song_id).into()], )); }), ) @@ -297,7 +297,7 @@ pub async fn main(db: Arc>, sender: mpsc::Sender, addr: if let Some(album) = db1.lock().unwrap().albums().get(&album_id) { _ = s7.send(Command::QueueAdd( vec![], - QueueContent::Folder( + vec![QueueContent::Folder( 0, album .songs @@ -306,7 +306,7 @@ pub async fn main(db: Arc>, sender: mpsc::Sender, addr: .collect(), album.name.clone(), ) - .into(), + .into()], )); } }),