rmptuibf939a4
Major changes to queue pushing added search feature to playlist and queue
+136 −53
5 files changed
- M README.md +1 −0
- M src/app.rs +48 −1
- M src/browser.rs +23 −32
- M src/handler.rs +60 −20
- M src/list.rs +4 −0
| @@ -35,6 +35,7 @@ A MPD client in Rust | |||
| 35 | 35 | - [ ] improvements on queue control | |
| 36 | 36 | - [x] add to playlists | |
| 37 | 37 | - [ ] view playlist | |
| 38 | + | - [ ] change playlist name | |
| 38 | 39 | - [x] search for songs | |
| 39 | 40 | - [ ] metadata based tree view | |
| 40 | 41 | - [x] Humantime format | |
| @@ -46,6 +46,7 @@ impl App { | |||
| 46 | 46 | Self::get_queue(&mut conn, &mut queue_list.list); | |
| 47 | 47 | ||
| 48 | 48 | let browser = FileBrowser::new(); | |
| 49 | + | ||
| 49 | 50 | Ok(Self { | |
| 50 | 51 | running: true, | |
| 51 | 52 | conn, | |
| @@ -122,7 +123,12 @@ impl App { | |||
| 122 | 123 | } | |
| 123 | 124 | ||
| 124 | 125 | SelectedTab::Queue => { | |
| 125 | − | file = self.queue_list.list.get(self.queue_list.index).unwrap().to_string(); | |
| 126 | + | file = self | |
| 127 | + | .queue_list | |
| 128 | + | .list | |
| 129 | + | .get(self.queue_list.index) | |
| 130 | + | .unwrap() | |
| 131 | + | .to_string(); | |
| 126 | 132 | } | |
| 127 | 133 | ||
| 128 | 134 | _ => {} | |
| @@ -145,6 +151,47 @@ impl App { | |||
| 145 | 151 | }; | |
| 146 | 152 | } | |
| 147 | 153 | ||
| 154 | + | pub fn handle_enter(&mut self) -> AppResult<()> { | |
| 155 | + | let browser = &mut self.browser; | |
| 156 | + | let (t, path) = browser.filetree.get(browser.selected).unwrap(); | |
| 157 | + | if t == "directory" { | |
| 158 | + | if path != "." { | |
| 159 | + | browser.prev_path = browser.path.clone(); | |
| 160 | + | browser.path = browser.prev_path.clone() + "/" + path; | |
| 161 | + | browser.update_directory(&mut self.conn)?; | |
| 162 | + | // self.get_all_rsongs(conn)?; | |
| 163 | + | browser.prev_selected = browser.selected; | |
| 164 | + | browser.selected = 0; | |
| 165 | + | } | |
| 166 | + | } else { | |
| 167 | + | // let list = conn | |
| 168 | + | // .songs_filenames | |
| 169 | + | // .iter() | |
| 170 | + | // .map(|f| f.as_str()) | |
| 171 | + | // .collect::<Vec<&str>>(); | |
| 172 | + | // let (filename, _) = rust_fuzzy_search::fuzzy_search_sorted(&path, &list) | |
| 173 | + | // .get(0) | |
| 174 | + | // .unwrap() | |
| 175 | + | // .clone(); | |
| 176 | + | let index = self.queue_list.list.iter().position(|x| x.contains(path)); | |
| 177 | + | ||
| 178 | + | if index.is_some() { | |
| 179 | + | self.conn.conn.switch(index.unwrap() as u32)?; | |
| 180 | + | } else { | |
| 181 | + | for filename in self.conn.songs_filenames.clone().iter() { | |
| 182 | + | if filename.contains(path) { | |
| 183 | + | let song = self.conn.get_song_with_only_filename(filename); | |
| 184 | + | self.conn.push(&song)?; | |
| 185 | + | } | |
| 186 | + | } | |
| 187 | + | ||
| 188 | + | // updating queue, to avoid multiple pushes of the same songs if we enter multiple times before the queue gets updated | |
| 189 | + | self.update_queue(); | |
| 190 | + | } | |
| 191 | + | } | |
| 192 | + | Ok(()) | |
| 193 | + | } | |
| 194 | + | ||
| 148 | 195 | pub fn search_song(&mut self) -> AppResult<()> { | |
| 149 | 196 | let filename = self.conn.get_full_path(&self.search_input)?; | |
| 150 | 197 | let song = self.conn.get_song_with_only_filename(&filename); | |
| @@ -1,4 +1,8 @@ | |||
| 1 | − | use crate::{app::AppResult, connection::Connection}; | |
| 1 | + | use crate::{ | |
| 2 | + | app::{App, AppResult}, | |
| 3 | + | connection::Connection, | |
| 4 | + | song::RSong, | |
| 5 | + | }; | |
| 2 | 6 | ||
| 3 | 7 | #[derive(Debug)] | |
| 4 | 8 | pub struct FileBrowser { | |
| @@ -7,6 +11,8 @@ pub struct FileBrowser { | |||
| 7 | 11 | pub prev_selected: usize, | |
| 8 | 12 | pub path: String, | |
| 9 | 13 | pub prev_path: String, | |
| 14 | + | ||
| 15 | + | pub rsongs: Vec<Option<RSong>>, | |
| 10 | 16 | } | |
| 11 | 17 | ||
| 12 | 18 | impl FileBrowser { | |
| @@ -17,6 +23,7 @@ impl FileBrowser { | |||
| 17 | 23 | prev_selected: 0, | |
| 18 | 24 | path: ".".to_string(), | |
| 19 | 25 | prev_path: ".".to_string(), | |
| 26 | + | rsongs: Vec::new(), | |
| 20 | 27 | } | |
| 21 | 28 | } | |
| 22 | 29 | ||
| @@ -32,6 +39,21 @@ impl FileBrowser { | |||
| 32 | 39 | Ok(()) | |
| 33 | 40 | } | |
| 34 | 41 | ||
| 42 | + | // read all songs into a vec of RSongs | |
| 43 | + | pub fn get_all_rsongs(&mut self, conn: &mut Connection) -> AppResult<Vec<Option<RSong>>> { | |
| 44 | + | for (t, s) in self.filetree.iter() { | |
| 45 | + | if t == "file" { | |
| 46 | + | let s = conn.get_full_path(s)?; | |
| 47 | + | let s = RSong::new(&mut conn.conn, s); | |
| 48 | + | self.rsongs.push(Some(s)); | |
| 49 | + | } else if t == "directory" { | |
| 50 | + | self.rsongs.push(None) | |
| 51 | + | } | |
| 52 | + | } | |
| 53 | + | ||
| 54 | + | Ok(self.rsongs.clone()) | |
| 55 | + | } | |
| 56 | + | ||
| 35 | 57 | // Go to next item in filetree | |
| 36 | 58 | pub fn next(&mut self) { | |
| 37 | 59 | // if self.selected < self.filetree.len() - 1 { | |
| @@ -54,38 +76,7 @@ impl FileBrowser { | |||
| 54 | 76 | } | |
| 55 | 77 | } | |
| 56 | 78 | ||
| 57 | − | pub fn handle_enter(&mut self, conn: &mut Connection) -> AppResult<()> { | |
| 58 | − | let (t, path) = self.filetree.get(self.selected).unwrap(); | |
| 59 | − | if t == "directory" { | |
| 60 | − | if path != "." { | |
| 61 | − | self.prev_path = self.path.clone(); | |
| 62 | − | self.path = self.prev_path.clone() + "/" + path; | |
| 63 | − | self.update_directory(conn)?; | |
| 64 | − | self.prev_selected = self.selected; | |
| 65 | − | self.selected = 0; | |
| 66 | − | // println!("self.path: {}", self.path); | |
| 67 | − | // println!("self.prev_pat: {}", self.prev_path); | |
| 68 | − | } | |
| 69 | − | } else { | |
| 70 | − | // let list = conn | |
| 71 | − | // .songs_filenames | |
| 72 | − | // .iter() | |
| 73 | − | // .map(|f| f.as_str()) | |
| 74 | − | // .collect::<Vec<&str>>(); | |
| 75 | − | // let (filename, _) = rust_fuzzy_search::fuzzy_search_sorted(&path, &list) | |
| 76 | − | // .get(0) | |
| 77 | − | // .unwrap() | |
| 78 | − | // .clone(); | |
| 79 | 79 | ||
| 80 | − | for filename in conn.songs_filenames.clone().iter() { | |
| 81 | − | if filename.contains(path) { | |
| 82 | − | let song = conn.get_song_with_only_filename(filename); | |
| 83 | − | conn.push(&song)?; | |
| 84 | − | } | |
| 85 | − | } | |
| 86 | − | } | |
| 87 | − | Ok(()) | |
| 88 | − | } | |
| 89 | 80 | ||
| 90 | 81 | pub fn handle_go_back(&mut self, conn: &mut Connection) -> AppResult<()> { | |
| 91 | 82 | if self.prev_path != "." { | |
| @@ -9,20 +9,58 @@ use rust_fuzzy_search::{self, fuzzy_search_sorted}; | |||
| 9 | 9 | ||
| 10 | 10 | pub fn handle_key_events(key_event: KeyEvent, app: &mut App) -> AppResult<()> { | |
| 11 | 11 | if app.inputmode == InputMode::Editing { | |
| 12 | − | // Live update | |
| 13 | − | let list: Vec<&str> = app | |
| 14 | − | .browser | |
| 15 | − | .filetree | |
| 16 | − | .iter() | |
| 17 | − | .map(|(_, f)| f.as_str()) | |
| 18 | − | .collect::<Vec<&str>>(); | |
| 19 | − | ||
| 20 | − | let res: Vec<(&str, f32)> = fuzzy_search_sorted(&app.search_input, &list); | |
| 21 | − | let res = res.iter().map(|(x, _)| *x).collect::<Vec<&str>>(); | |
| 22 | − | ||
| 23 | − | for (i, (_, item)) in app.browser.filetree.iter().enumerate() { | |
| 24 | − | if item.contains(res.get(0).unwrap()) { | |
| 25 | − | app.browser.selected = i; | |
| 12 | + | // Live search update | |
| 13 | + | match app.selected_tab { | |
| 14 | + | SelectedTab::DirectoryBrowser => { | |
| 15 | + | let list: Vec<&str> = app | |
| 16 | + | .browser | |
| 17 | + | .filetree | |
| 18 | + | .iter() | |
| 19 | + | .map(|(_, f)| f.as_str()) | |
| 20 | + | .collect::<Vec<&str>>(); | |
| 21 | + | ||
| 22 | + | let res: Vec<(&str, f32)> = fuzzy_search_sorted(&app.search_input, &list); | |
| 23 | + | let res = res.iter().map(|(x, _)| *x).collect::<Vec<&str>>(); | |
| 24 | + | ||
| 25 | + | for (i, (_, item)) in app.browser.filetree.iter().enumerate() { | |
| 26 | + | if item.contains(res.get(0).unwrap()) { | |
| 27 | + | app.browser.selected = i; | |
| 28 | + | } | |
| 29 | + | } | |
| 30 | + | } | |
| 31 | + | ||
| 32 | + | SelectedTab::Queue => { | |
| 33 | + | let list: Vec<&str> = app | |
| 34 | + | .queue_list | |
| 35 | + | .list | |
| 36 | + | .iter() | |
| 37 | + | .map(|f| f.as_str()) | |
| 38 | + | .collect::<Vec<&str>>(); | |
| 39 | + | let res: Vec<(&str, f32)> = fuzzy_search_sorted(&app.search_input, &list); | |
| 40 | + | let res = res.iter().map(|(x, _)| *x).collect::<Vec<&str>>(); | |
| 41 | + | ||
| 42 | + | for (i, item) in app.queue_list.list.iter().enumerate() { | |
| 43 | + | if item.contains(res.get(0).unwrap()) { | |
| 44 | + | app.queue_list.index = i; | |
| 45 | + | } | |
| 46 | + | } | |
| 47 | + | } | |
| 48 | + | ||
| 49 | + | SelectedTab::Playlists => { | |
| 50 | + | let list: Vec<&str> = app | |
| 51 | + | .pl_list | |
| 52 | + | .list | |
| 53 | + | .iter() | |
| 54 | + | .map(|f| f.as_str()) | |
| 55 | + | .collect::<Vec<&str>>(); | |
| 56 | + | let res: Vec<(&str, f32)> = fuzzy_search_sorted(&app.search_input, &list); | |
| 57 | + | let res = res.iter().map(|(x, _)| *x).collect::<Vec<&str>>(); | |
| 58 | + | ||
| 59 | + | for (i, item) in app.pl_list.list.iter().enumerate() { | |
| 60 | + | if item.contains(res.get(0).unwrap()) { | |
| 61 | + | app.pl_list.index = i; | |
| 62 | + | } | |
| 63 | + | } | |
| 26 | 64 | } | |
| 27 | 65 | } | |
| 28 | 66 | ||
| @@ -126,6 +164,7 @@ pub fn handle_key_events(key_event: KeyEvent, app: &mut App) -> AppResult<()> { | |||
| 126 | 164 | app.conn.conn.clear()?; | |
| 127 | 165 | app.conn.update_status(); | |
| 128 | 166 | app.queue_list.list.clear(); | |
| 167 | + | app.queue_list.reset_index(); | |
| 129 | 168 | } | |
| 130 | 169 | } | |
| 131 | 170 | ||
| @@ -149,15 +188,13 @@ pub fn handle_key_events(key_event: KeyEvent, app: &mut App) -> AppResult<()> { | |||
| 149 | 188 | ||
| 150 | 189 | match app.selected_tab { | |
| 151 | 190 | SelectedTab::DirectoryBrowser => { | |
| 152 | − | app.browser.handle_enter(&mut app.conn)?; | |
| 191 | + | app.handle_enter()?; | |
| 153 | 192 | } | |
| 154 | 193 | ||
| 155 | 194 | SelectedTab::Queue => { | |
| 156 | − | let song = app.conn.get_song_with_only_filename( | |
| 157 | − | app.queue_list.list.get(app.queue_list.index).unwrap(), | |
| 158 | − | ); | |
| 159 | − | app.conn.push(&song)?; | |
| 195 | + | app.conn.conn.switch(app.queue_list.index as u32)?; | |
| 160 | 196 | } | |
| 197 | + | ||
| 161 | 198 | SelectedTab::Playlists => { | |
| 162 | 199 | app.conn | |
| 163 | 200 | .push_playlist(app.pl_list.list.get(app.pl_list.index).unwrap())?; | |
| @@ -285,10 +322,13 @@ pub fn handle_key_events(key_event: KeyEvent, app: &mut App) -> AppResult<()> { | |||
| 285 | 322 | } | |
| 286 | 323 | ||
| 287 | 324 | // Remove from Current Playlsit | |
| 288 | − | KeyCode::Backspace => { | |
| 325 | + | KeyCode::Char(' ') | KeyCode::Backspace => { | |
| 289 | 326 | app.remove_from_current_playlist(); | |
| 290 | 327 | } | |
| 291 | 328 | ||
| 329 | + | // Change playlist name | |
| 330 | + | KeyCode::Char('e') => if app.selected_tab == SelectedTab::Playlists {}, | |
| 331 | + | ||
| 292 | 332 | _ => {} | |
| 293 | 333 | } | |
| 294 | 334 | } | |
| @@ -39,4 +39,8 @@ impl<T> ContentList<T> { | |||
| 39 | 39 | self.index -= 1; | |
| 40 | 40 | } | |
| 41 | 41 | } | |
| 42 | + | ||
| 43 | + | pub fn reset_index(&mut self) { | |
| 44 | + | self.index = 0; | |
| 45 | + | } | |
| 42 | 46 | } | |