From f437d19ecccb0352646d7bf539ec83aad7ec7238 Mon Sep 17 00:00:00 2001 From: kamiyaa Date: Fri, 25 Oct 2024 12:24:45 -0400 Subject: [PATCH] fix playlist entry remove not working when playing from directory --- .../symphonia/player/impl_audio_player.rs | 46 +++++++++++++------ src/bin/server/playlist/impl_playlist.rs | 23 ++++++---- src/bin/server/playlist/mod.rs | 17 +++++++ src/bin/server/server.rs | 12 +++-- src/bin/server/server_commands/playlist.rs | 41 +++++++---------- src/bin/server/util/mimetype.rs | 18 +++++--- src/playlist.rs | 6 +++ 7 files changed, 103 insertions(+), 60 deletions(-) diff --git a/src/bin/server/audio/symphonia/player/impl_audio_player.rs b/src/bin/server/audio/symphonia/player/impl_audio_player.rs index e271c74..1fa3855 100644 --- a/src/bin/server/audio/symphonia/player/impl_audio_player.rs +++ b/src/bin/server/audio/symphonia/player/impl_audio_player.rs @@ -104,12 +104,18 @@ impl AudioPlayer for SymphoniaPlayer { fn play_next(&mut self) -> DiziResult { let playlist = self.playlist_context.current_playlist_mut(); - let song_entry = playlist.next_song_peak().ok_or_else(|| { - DiziError::new(DiziErrorKind::ParseError, "Playlist error".to_string()) - })?; - playlist.order_index = Some(song_entry.order_index); - - playlist.load_current_entry_metadata()?; + // keep going through playlist until we find a song that can + // be parsed and played + loop { + let song_entry = playlist.next_song_peak().ok_or_else(|| { + DiziError::new(DiziErrorKind::ParseError, "Playlist error".to_string()) + })?; + playlist.order_index = Some(song_entry.order_index); + + if playlist.load_current_entry_metadata().is_ok() { + break; + }; + } if let Some(entry) = playlist.current_entry() { if let DiziSongEntry::Loaded(audio_file) = entry.entry { self.play(&audio_file)?; @@ -121,12 +127,22 @@ impl AudioPlayer for SymphoniaPlayer { fn play_previous(&mut self) -> DiziResult { let playlist = self.playlist_context.current_playlist_mut(); - let song_entry = playlist.previous_song_peak().ok_or_else(|| { - DiziError::new(DiziErrorKind::ParseError, "Playlist error".to_string()) - })?; - playlist.order_index = Some(song_entry.order_index); - - playlist.load_current_entry_metadata()?; + // keep going through playlist until we find a song that can + // be parsed and played + loop { + let song_entry = playlist.previous_song_peak().ok_or_else(|| { + DiziError::new(DiziErrorKind::ParseError, "Playlist error".to_string()) + })?; + playlist.order_index = Some(song_entry.order_index); + + if playlist.load_current_entry_metadata().is_ok() { + break; + }; + tracing::debug!( + "Skipping '{}' because we failed to parse it", + song_entry.entry.file_name() + ); + } if let Some(entry) = playlist.current_entry() { if let DiziSongEntry::Loaded(audio_file) = entry.entry { self.play(&audio_file)?; @@ -215,9 +231,11 @@ impl AudioPlayer for SymphoniaPlayer { self.state.shuffle = shuffle; if self.shuffle_enabled() { - self.playlist_context.current_playlist_mut().shuffle(); + self.playlist_context.directory_playlist.shuffle(); + self.playlist_context.file_playlist.shuffle(); } else { - self.playlist_context.current_playlist_mut().unshuffle(); + self.playlist_context.directory_playlist.unshuffle(); + self.playlist_context.file_playlist.unshuffle(); } } diff --git a/src/bin/server/playlist/impl_playlist.rs b/src/bin/server/playlist/impl_playlist.rs index 6b4c551..fdccdfe 100644 --- a/src/bin/server/playlist/impl_playlist.rs +++ b/src/bin/server/playlist/impl_playlist.rs @@ -52,13 +52,13 @@ impl DiziPlaylistTrait for DiziPlaylist { } fn current_entry(&self) -> Option { - let playlist_index = self.order_index?; - let song_index = self.order[playlist_index]; + let order_index = self.order_index?; + let playlist_index = self.order[order_index]; Some(DiziPlaylistEntry { - entry_index: song_index, - order_index: playlist_index, - entry: self.entry_ref(song_index).clone(), + entry_index: playlist_index, + order_index, + entry: self.entry_ref(playlist_index).clone(), }) } @@ -88,22 +88,25 @@ impl DiziPlaylistTrait for DiziPlaylist { } fn shuffle(&mut self) { - let mut new_shuffle_order: Vec = (0..self.len()).collect(); - // the current song being played should be the // first value of the random order match self.current_entry() { Some(entry) => { - new_shuffle_order.remove(entry.entry_index); + let entry_index = entry.entry_index; + let mut new_shuffle_order: Vec = + (0..self.len()).filter(|i| *i != entry_index).collect(); new_shuffle_order.shuffle(&mut thread_rng()); - new_shuffle_order.insert(0, entry.entry_index); + new_shuffle_order.insert(0, entry_index); + + self.order = new_shuffle_order; self.order_index = Some(0); } None => { + let mut new_shuffle_order: Vec = (0..self.len()).collect(); new_shuffle_order.shuffle(&mut thread_rng()); + self.order = new_shuffle_order; } } - self.order = new_shuffle_order; } fn unshuffle(&mut self) { diff --git a/src/bin/server/playlist/mod.rs b/src/bin/server/playlist/mod.rs index f0ba73c..0a128e7 100644 --- a/src/bin/server/playlist/mod.rs +++ b/src/bin/server/playlist/mod.rs @@ -83,6 +83,23 @@ impl DiziPlaylist { } Ok(()) } + + pub fn push_entry(&mut self, entry: DiziSongEntry) { + self.contents.push(entry); + self.order.push(self.contents.len() - 1); + } + + pub fn remove_entry(&mut self, index: usize) { + self.contents.remove(index); + let new_len = self.contents.len(); + let new_order: Vec = self + .order + .iter() + .filter(|i| **i < new_len) + .map(|i| *i) + .collect(); + self.order = new_order; + } } impl std::default::Default for DiziPlaylist { diff --git a/src/bin/server/server.rs b/src/bin/server/server.rs index ce61567..8911ee6 100644 --- a/src/bin/server/server.rs +++ b/src/bin/server/server.rs @@ -23,9 +23,11 @@ pub fn setup_socket(config: &AppConfig) -> DiziResult { pub fn serve(config: AppConfig) -> DiziResult { let events = Events::new(); - let event_tx2 = events.server_event_sender().clone(); - let player = SymphoniaPlayer::new(&config, event_tx2)?; + let player = { + let server_event_tx = events.server_event_sender().clone(); + SymphoniaPlayer::new(&config, server_event_tx)? + }; let mut context = AppContext { events, @@ -35,10 +37,10 @@ pub fn serve(config: AppConfig) -> DiziResult { }; let listener = setup_socket(context.config_ref())?; + // thread for listening to new client connections { - // thread for listening to new client connections - let server_tx2 = context.events.server_event_sender().clone(); - thread::spawn(|| listen_for_clients(listener, server_tx2)); + let server_event_tx = context.events.server_event_sender().clone(); + thread::spawn(|| listen_for_clients(listener, server_event_tx)); } while context.quit == QuitType::DoNot { diff --git a/src/bin/server/server_commands/playlist.rs b/src/bin/server/server_commands/playlist.rs index 3d8c3af..cc72ffd 100644 --- a/src/bin/server/server_commands/playlist.rs +++ b/src/bin/server/server_commands/playlist.rs @@ -18,13 +18,7 @@ pub fn playlist_play(context: &mut AppContext, index: usize) -> DiziResult { } pub fn playlist_load(context: &mut AppContext, cwd: &Path, path: &Path) -> DiziResult { - let shuffle_enabled = context.player.shuffle_enabled(); - if !context - .player - .playlist_context_mut() - .file_playlist - .is_empty() - { + if !context.player.playlist_context.file_playlist.is_empty() { return Err(DiziError::new( DiziErrorKind::InvalidParameters, "playlist cannot be loaded because current playlist is not empty".to_string(), @@ -32,6 +26,7 @@ pub fn playlist_load(context: &mut AppContext, cwd: &Path, path: &Path) -> DiziR } let mut new_playlist = DiziPlaylist::from_file(cwd, path)?; + let shuffle_enabled = context.player.shuffle_enabled(); if shuffle_enabled { new_playlist.shuffle(); } @@ -45,41 +40,37 @@ pub fn playlist_clear(context: &mut AppContext) -> DiziResult { } pub fn playlist_append(context: &mut AppContext, path: &Path) -> DiziResult> { + let playlist = &mut context.player.playlist_context_mut().file_playlist; if path.is_dir() { let audio_files = recursively_find_songs(path); for audio_file in audio_files.iter() { let entry = DiziSongEntry::Loaded(audio_file.clone()); - context - .player - .playlist_context_mut() - .file_playlist - .push(entry); + playlist.push_entry(entry); } Ok(audio_files) } else if is_playable(path)? { let file = DiziFile::new(path); let audio_file = DiziAudioFile::try_from(file)?; let entry = DiziSongEntry::Loaded(audio_file.clone()); - context - .player - .playlist_context_mut() - .file_playlist - .push(entry); + playlist.push_entry(entry); Ok(vec![audio_file]) } else { - Ok(vec![]) + Err(DiziError::new( + DiziErrorKind::InvalidParameters, + "File not playable".to_string(), + )) } } pub fn playlist_remove(context: &mut AppContext, index: usize) -> DiziResult { - let len = context.player.playlist_context.current_playlist_ref().len(); - if index <= len { - context - .player - .playlist_context_mut() - .file_playlist - .remove(index); + let playlist = &mut context.player.playlist_context_mut().file_playlist; + if index >= playlist.len() { + return Err(DiziError::new( + DiziErrorKind::InvalidParameters, + "Playlist index out of range".to_string(), + )); } + playlist.remove_entry(index); Ok(()) } diff --git a/src/bin/server/util/mimetype.rs b/src/bin/server/util/mimetype.rs index 62e1194..f26b6d6 100644 --- a/src/bin/server/util/mimetype.rs +++ b/src/bin/server/util/mimetype.rs @@ -8,19 +8,25 @@ pub fn get_mimetype(p: &Path) -> io::Result { .arg("--mime-type") .arg(p) .output()?; - let stdout = std::str::from_utf8(&output.stdout).unwrap(); - + let stdout = std::str::from_utf8(&output.stdout).expect("Failed to read from stdout"); let mimetype = stdout.to_string(); - tracing::debug!("{:?} mimetype: {}", p, mimetype); - Ok(mimetype) } pub fn is_playable(p: &Path) -> io::Result { let mimetype = get_mimetype(p)?; - - Ok(is_mimetype_audio(&mimetype) || is_mimetype_video(&mimetype)) + let is_audio_mimetype = is_mimetype_audio(&mimetype) || is_mimetype_video(&mimetype); + if is_audio_mimetype { + return Ok(true); + } + match p.extension() { + None => Ok(false), + Some(s) => match s.to_string_lossy().as_ref() { + "aac" | "flac" | "mp3" | "mp4" | "m4a" | "ogg" | "opus" | "wav" | "webm" => Ok(true), + _ => Ok(false), + }, + } } pub fn is_mimetype_audio(s: &str) -> bool { diff --git a/src/playlist.rs b/src/playlist.rs index 4393cc9..fd96114 100644 --- a/src/playlist.rs +++ b/src/playlist.rs @@ -54,6 +54,12 @@ impl FilePlaylist { pub fn remove_song(&mut self, index: usize) -> DiziSongEntry { let song = self.list_mut().remove(index); + + if let Some(playing_index) = self.playing_index { + if playing_index == index { + self.set_playing_index(None); + } + } if self.list_ref().is_empty() { self.set_cursor_index(None); } else {