| @@ -1,6 +1,7 @@ |
| 1 | 1 | | use std::time::Duration; |
| 2 | 2 | | |
| 3 | 3 | | use crate::app::{App, SelectedTab}; |
| 4 | + | use mpd::Song; |
| 4 | 5 | | use ratatui::{ |
| 5 | 6 | | prelude::*, |
| 6 | 7 | | widgets::{block::Title, *}, |
| @@ -32,8 +33,7 @@ pub fn render(app: &mut App, frame: &mut Frame) { |
| 32 | 33 | | match app.selected_tab { |
| 33 | 34 | | SelectedTab::Queue => draw_queue(frame, app, layout[0]), |
| 34 | 35 | | SelectedTab::Playlists => draw_playlists(frame, app, layout[0]), |
| 35 | | − | SelectedTab::DirectoryBrowser => draw_song_browser(frame, app, layout[0]), |
| 36 | | − | // SelectedTab::SongBrowser => draw_song_browser(frame, app, layout[0]), |
| 36 | + | SelectedTab::DirectoryBrowser => draw_directory_browser(frame, app, layout[0]), |
| 37 | 37 | | } |
| 38 | 38 | | |
| 39 | 39 | | match app.inputmode { |
| @@ -50,63 +50,8 @@ pub fn render(app: &mut App, frame: &mut Frame) { |
| 50 | 50 | | } |
| 51 | 51 | | } |
| 52 | 52 | | |
| 53 | | − | /// Draws the file tree browser |
| 53 | + | /// Draws the directory |
| 54 | 54 | | fn draw_directory_browser(frame: &mut Frame, app: &mut App, size: Rect) { |
| 55 | | − | let mut song_state = ListState::default(); |
| 56 | | − | let total_songs = app.conn.conn.stats().unwrap().songs.to_string(); |
| 57 | | − | let mut list: Vec<ListItem> = vec![]; |
| 58 | | − | for (t, s) in app.browser.filetree.iter() { |
| 59 | | − | if t == "file" { |
| 60 | | − | let mut status: bool = false; |
| 61 | | − | for sn in app.queue_list.list.iter() { |
| 62 | | − | if sn.contains(s) { |
| 63 | | − | status = true; |
| 64 | | − | } |
| 65 | | − | } |
| 66 | | − | if status { |
| 67 | | − | list.push(ListItem::new(s.clone().magenta().bold())); |
| 68 | | − | } else { |
| 69 | | − | // list.push(ListItem::new(s.clone())); |
| 70 | | − | list.push(ListItem::new(s.clone())); |
| 71 | | − | } |
| 72 | | − | } else { |
| 73 | | − | list.push(ListItem::new(Line::styled( |
| 74 | | − | format!("[{}]", *s), |
| 75 | | − | Style::default(), |
| 76 | | − | ))); |
| 77 | | − | } |
| 78 | | − | } |
| 79 | | − | let list = List::new(list) |
| 80 | | − | .block( |
| 81 | | − | Block::default() |
| 82 | | − | .title(format!("Directory Browser: {}", app.browser.path.clone()).bold()) |
| 83 | | − | .title( |
| 84 | | − | Title::from(format!("Total Songs: {}", total_songs).bold().green()) |
| 85 | | − | .alignment(Alignment::Center), |
| 86 | | − | ) |
| 87 | | − | .title( |
| 88 | | − | Title::from(format!("Volume: {}%", app.conn.volume).bold().green()) |
| 89 | | − | .alignment(Alignment::Right), |
| 90 | | − | ) |
| 91 | | − | .borders(Borders::ALL), |
| 92 | | − | ) |
| 93 | | − | .highlight_style( |
| 94 | | − | Style::new() |
| 95 | | − | .fg(Color::Cyan) |
| 96 | | − | .bg(Color::Black) |
| 97 | | − | .add_modifier(Modifier::BOLD) |
| 98 | | − | .add_modifier(Modifier::REVERSED), |
| 99 | | − | ) |
| 100 | | − | .highlight_symbol(">>") |
| 101 | | − | .repeat_highlight_symbol(true) |
| 102 | | − | .scroll_padding(20); |
| 103 | | − | |
| 104 | | − | song_state.select(Some(app.browser.selected)); |
| 105 | | − | frame.render_stateful_widget(list, size, &mut song_state); |
| 106 | | − | } |
| 107 | | − | |
| 108 | | − | /// Draws the song browser |
| 109 | | − | fn draw_song_browser(frame: &mut Frame, app: &mut App, size: Rect) { |
| 110 | 55 | | let total_songs = app.conn.conn.stats().unwrap().songs.to_string(); |
| 111 | 56 | | |
| 112 | 57 | | let rows = app.browser.filetree.iter().enumerate().map(|(i, (t, s))| { |
| @@ -149,7 +94,7 @@ fn draw_song_browser(frame: &mut Frame, app: &mut App, size: Rect) { |
| 149 | 94 | | Cell::from(track.green()), |
| 150 | 95 | | Cell::from(title), |
| 151 | 96 | | Cell::from(album), |
| 152 | | − | Cell::from(time.to_string().red()), |
| 97 | + | Cell::from(time.to_string().magenta()), |
| 153 | 98 | | ]); |
| 154 | 99 | | row.magenta().bold() |
| 155 | 100 | | } else { |
| @@ -158,7 +103,7 @@ fn draw_song_browser(frame: &mut Frame, app: &mut App, size: Rect) { |
| 158 | 103 | | Cell::from(track.green()), |
| 159 | 104 | | Cell::from(title), |
| 160 | 105 | | Cell::from(album), |
| 161 | | − | Cell::from(time.to_string().red()), |
| 106 | + | Cell::from(time.to_string().magenta()), |
| 162 | 107 | | ]); |
| 163 | 108 | | row |
| 164 | 109 | | } |
| @@ -178,9 +123,9 @@ fn draw_song_browser(frame: &mut Frame, app: &mut App, size: Rect) { |
| 178 | 123 | | let table = Table::new( |
| 179 | 124 | | rows, |
| 180 | 125 | | [ |
| 181 | | − | Constraint::Percentage(33), |
| 126 | + | Constraint::Percentage(20), |
| 182 | 127 | | Constraint::Percentage(3), |
| 183 | | − | Constraint::Percentage(30), |
| 128 | + | Constraint::Min(30), |
| 184 | 129 | | Constraint::Percentage(30), |
| 185 | 130 | | Constraint::Percentage(3), |
| 186 | 131 | | ], |
| @@ -205,7 +150,8 @@ fn draw_song_browser(frame: &mut Frame, app: &mut App, size: Rect) { |
| 205 | 150 | | .bg(Color::Black), |
| 206 | 151 | | ) |
| 207 | 152 | | .highlight_symbol(">>") |
| 208 | | − | .header(header); |
| 153 | + | .header(header) |
| 154 | + | .flex(layout::Flex::Legacy); |
| 209 | 155 | | |
| 210 | 156 | | state.select(Some(app.browser.selected)); |
| 211 | 157 | | frame.render_stateful_widget(table, size, &mut state); |
| @@ -213,38 +159,101 @@ fn draw_song_browser(frame: &mut Frame, app: &mut App, size: Rect) { |
| 213 | 159 | | |
| 214 | 160 | | /// draws playing queue |
| 215 | 161 | | fn draw_queue(frame: &mut Frame, app: &mut App, size: Rect) { |
| 216 | | − | let mut queue_state = ListState::default(); |
| 217 | | − | let title = Block::default() |
| 218 | | − | .title(Title::from("Play Queue".green().bold())) |
| 219 | | − | .title(Title::from( |
| 220 | | − | format!("({} items)", app.queue_list.list.len()).bold(), |
| 221 | | − | )) |
| 222 | | − | .title( |
| 223 | | − | Title::from(format!("Volume: {}%", app.conn.volume).bold().green()) |
| 224 | | − | .alignment(Alignment::Right), |
| 162 | + | let rows = app.queue_list.list.iter().map(|s| { |
| 163 | + | // metadata |
| 164 | + | let song = app |
| 165 | + | .conn |
| 166 | + | .conn |
| 167 | + | .lsinfo(&Song { |
| 168 | + | file: app.conn.get_full_path(&s).unwrap_or_default(), |
| 169 | + | ..Default::default() |
| 170 | + | }) |
| 171 | + | .unwrap_or_default(); |
| 172 | + | let song = song.get(0).unwrap(); |
| 173 | + | let title = song.clone().title.unwrap_or_else(|| song.clone().file); |
| 174 | + | let artist = song.clone().artist.unwrap_or_default().cyan(); |
| 175 | + | let album = song |
| 176 | + | .tags |
| 177 | + | .iter() |
| 178 | + | .filter(|(x, _)| x == "Album") |
| 179 | + | .map(|(_, l)| l.clone()) |
| 180 | + | .collect::<Vec<String>>() |
| 181 | + | .join(""); |
| 182 | + | |
| 183 | + | let track = song |
| 184 | + | .tags |
| 185 | + | .iter() |
| 186 | + | .filter(|(x, _)| x == "Track") |
| 187 | + | .map(|(_, l)| l.clone()) |
| 188 | + | .collect::<Vec<String>>() |
| 189 | + | .join(""); |
| 190 | + | |
| 191 | + | let time = humantime::format_duration( |
| 192 | + | song.clone().duration.unwrap_or_else(|| Duration::new(0, 0)), |
| 225 | 193 | | ); |
| 226 | 194 | | |
| 227 | | − | let mut items: Vec<ListItem> = vec![]; |
| 228 | | − | for item in app.queue_list.list.iter() { |
| 229 | | − | if item.contains(&app.conn.current_song.file) { |
| 230 | | − | items.push(ListItem::new(item.clone().magenta().bold())) |
| 195 | + | if s.contains(&app.conn.current_song.file) { |
| 196 | + | let row = Row::new(vec![ |
| 197 | + | Cell::from(artist), |
| 198 | + | Cell::from(track.green()), |
| 199 | + | Cell::from(title), |
| 200 | + | Cell::from(album), |
| 201 | + | Cell::from(time.to_string().magenta()), |
| 202 | + | ]); |
| 203 | + | row.magenta().bold() |
| 231 | 204 | | } else { |
| 232 | | − | items.push(ListItem::new(item.clone())); |
| 205 | + | let row = Row::new(vec![ |
| 206 | + | Cell::from(artist), |
| 207 | + | Cell::from(track.green()), |
| 208 | + | Cell::from(title), |
| 209 | + | Cell::from(album), |
| 210 | + | Cell::from(time.to_string().magenta()), |
| 211 | + | ]); |
| 212 | + | row |
| 233 | 213 | | } |
| 234 | | − | } |
| 235 | | − | let list = List::new(items) |
| 236 | | − | .block(title.borders(Borders::ALL)) |
| 237 | | − | .highlight_style( |
| 238 | | − | Style::new() |
| 239 | | − | .fg(Color::Cyan) |
| 240 | | − | .bg(Color::Black) |
| 241 | | − | .add_modifier(Modifier::BOLD) |
| 242 | | − | .add_modifier(Modifier::REVERSED), |
| 243 | | − | ) |
| 244 | | − | .highlight_symbol(">>"); |
| 214 | + | }); |
| 215 | + | |
| 216 | + | let mut state = TableState::new(); |
| 217 | + | let header = ["Artist", "Track", "Title", "Album", "Time"] |
| 218 | + | .into_iter() |
| 219 | + | .map(Cell::from) |
| 220 | + | .collect::<Row>() |
| 221 | + | .bold() |
| 222 | + | .height(1); |
| 223 | + | let table = Table::new( |
| 224 | + | rows, |
| 225 | + | [ |
| 226 | + | Constraint::Percentage(20), |
| 227 | + | Constraint::Percentage(3), |
| 228 | + | Constraint::Min(30), |
| 229 | + | Constraint::Percentage(30), |
| 230 | + | Constraint::Percentage(3), |
| 231 | + | ], |
| 232 | + | ) |
| 233 | + | .block( |
| 234 | + | Block::default() |
| 235 | + | .title(Title::from("Play Queue".green().bold())) |
| 236 | + | .title(Title::from( |
| 237 | + | format!("({} items)", app.queue_list.list.len()).bold(), |
| 238 | + | )) |
| 239 | + | .title( |
| 240 | + | Title::from(format!("Volume: {}%", app.conn.volume).bold().green()) |
| 241 | + | .alignment(Alignment::Right), |
| 242 | + | ) |
| 243 | + | .borders(Borders::ALL), |
| 244 | + | ) |
| 245 | + | .highlight_style( |
| 246 | + | Style::default() |
| 247 | + | .add_modifier(Modifier::REVERSED) |
| 248 | + | .fg(Color::Cyan) |
| 249 | + | .bg(Color::Black), |
| 250 | + | ) |
| 251 | + | .highlight_symbol(">>") |
| 252 | + | .header(header) |
| 253 | + | .flex(layout::Flex::Legacy); |
| 245 | 254 | | |
| 246 | | − | queue_state.select(Some(app.queue_list.index)); |
| 247 | | − | frame.render_stateful_widget(list, size, &mut queue_state); |
| 255 | + | state.select(Some(app.queue_list.index)); |
| 256 | + | frame.render_stateful_widget(table, size, &mut state); |
| 248 | 257 | | } |
| 249 | 258 | | |
| 250 | 259 | | /// draws all playlists |