diff --git a/README.md b/README.md index 67d599a..5997eb4 100755 --- a/README.md +++ b/README.md @@ -38,6 +38,6 @@ A MPD client in Rust - [x] add to playlists - [x] search for songs - [x] Humantime format +- [x] metadata based tree view - [ ] view playlist - [ ] change playlist name -- [ ] metadata based tree view diff --git a/src/browser.rs b/src/browser.rs index e82692a..212618e 100755 --- a/src/browser.rs +++ b/src/browser.rs @@ -98,24 +98,27 @@ impl FileBrowser { // Go to next item in filetree pub fn next(&mut self) { - // if self.selected < self.filetree.len() - 1 { - // self.selected += 1; - // } - - if self.selected == self.filetree.len() - 1 { - self.selected = 0; - } else { + if self.selected < self.filetree.len() - 1 { self.selected += 1; } + + // if self.selected == self.filetree.len() - 1 { + // self.selected = 0; + // } else { + // self.selected += 1; + // } } /// Go to previous item in filetree pub fn prev(&mut self) { - if self.selected == 0 { - self.selected = self.filetree.len() - 1; - } else { + if self.selected != 0 { self.selected -= 1; } + // if self.selected == 0 { + // self.selected = self.filetree.len() - 1; + // } else { + // self.selected -= 1; + // } } pub fn handle_go_back(&mut self, conn: &mut Connection) -> AppResult<()> { diff --git a/src/handler.rs b/src/handler.rs index 6d8c32c..cf5df27 100755 --- a/src/handler.rs +++ b/src/handler.rs @@ -62,7 +62,6 @@ pub fn handle_key_events(key_event: KeyEvent, app: &mut App) -> AppResult<()> { } } } - } match key_event.code { @@ -200,7 +199,6 @@ pub fn handle_key_events(key_event: KeyEvent, app: &mut App) -> AppResult<()> { app.conn .load_playlist(app.pl_list.list.get(app.pl_list.index).unwrap())?; } - } app.conn.update_status(); } @@ -279,6 +277,7 @@ pub fn handle_key_events(key_event: KeyEvent, app: &mut App) -> AppResult<()> { KeyCode::Char('>') => { if !app.queue_list.list.is_empty() { app.conn.conn.next()?; + app.update_queue(); } } @@ -286,6 +285,7 @@ pub fn handle_key_events(key_event: KeyEvent, app: &mut App) -> AppResult<()> { KeyCode::Char('<') => { if !app.queue_list.list.is_empty() { app.conn.conn.prev()?; + app.update_queue(); } } diff --git a/src/list.rs b/src/list.rs index 6294f44..219b9a4 100755 --- a/src/list.rs +++ b/src/list.rs @@ -14,30 +14,36 @@ impl ContentList { // Go to next item in list pub fn next(&mut self) { - // if self.index < self.list.len() - 1 { - // self.index += 1; - // } - let len = self.list.len(); if len != 0 { - if self.index == self.list.len() - 1 { - self.index = 0; - } else { + if self.index < len - 1 { self.index += 1; } } + + // let len = self.list.len(); + // if len != 0 { + // if self.index == self.list.len() - 1 { + // self.index = 0; + // } else { + // self.index += 1; + // } + // } } /// Go to previous item in list pub fn prev(&mut self) { - if self.index == 0 { - let len = self.list.len(); - if len != 0 { - self.index = len - 1; - } - } else { + if self.index != 0 { self.index -= 1; } + // if self.index == 0 { + // let len = self.list.len(); + // if len != 0 { + // self.index = len - 1; + // } + // } else { + // self.index -= 1; + // } } pub fn reset_index(&mut self) { diff --git a/src/ui.rs b/src/ui.rs index 93dcd53..b4b789d 100755 --- a/src/ui.rs +++ b/src/ui.rs @@ -1,6 +1,7 @@ use std::time::Duration; use crate::app::{App, SelectedTab}; +use mpd::Song; use ratatui::{ prelude::*, widgets::{block::Title, *}, @@ -32,8 +33,7 @@ pub fn render(app: &mut App, frame: &mut Frame) { match app.selected_tab { SelectedTab::Queue => draw_queue(frame, app, layout[0]), SelectedTab::Playlists => draw_playlists(frame, app, layout[0]), - SelectedTab::DirectoryBrowser => draw_song_browser(frame, app, layout[0]), - // SelectedTab::SongBrowser => draw_song_browser(frame, app, layout[0]), + SelectedTab::DirectoryBrowser => draw_directory_browser(frame, app, layout[0]), } match app.inputmode { @@ -50,63 +50,8 @@ pub fn render(app: &mut App, frame: &mut Frame) { } } -/// Draws the file tree browser +/// Draws the directory fn draw_directory_browser(frame: &mut Frame, app: &mut App, size: Rect) { - let mut song_state = ListState::default(); - let total_songs = app.conn.conn.stats().unwrap().songs.to_string(); - let mut list: Vec = vec![]; - for (t, s) in app.browser.filetree.iter() { - if t == "file" { - let mut status: bool = false; - for sn in app.queue_list.list.iter() { - if sn.contains(s) { - status = true; - } - } - if status { - list.push(ListItem::new(s.clone().magenta().bold())); - } else { - // list.push(ListItem::new(s.clone())); - list.push(ListItem::new(s.clone())); - } - } else { - list.push(ListItem::new(Line::styled( - format!("[{}]", *s), - Style::default(), - ))); - } - } - let list = List::new(list) - .block( - Block::default() - .title(format!("Directory Browser: {}", app.browser.path.clone()).bold()) - .title( - Title::from(format!("Total Songs: {}", total_songs).bold().green()) - .alignment(Alignment::Center), - ) - .title( - Title::from(format!("Volume: {}%", app.conn.volume).bold().green()) - .alignment(Alignment::Right), - ) - .borders(Borders::ALL), - ) - .highlight_style( - Style::new() - .fg(Color::Cyan) - .bg(Color::Black) - .add_modifier(Modifier::BOLD) - .add_modifier(Modifier::REVERSED), - ) - .highlight_symbol(">>") - .repeat_highlight_symbol(true) - .scroll_padding(20); - - song_state.select(Some(app.browser.selected)); - frame.render_stateful_widget(list, size, &mut song_state); -} - -/// Draws the song browser -fn draw_song_browser(frame: &mut Frame, app: &mut App, size: Rect) { let total_songs = app.conn.conn.stats().unwrap().songs.to_string(); 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) { Cell::from(track.green()), Cell::from(title), Cell::from(album), - Cell::from(time.to_string().red()), + Cell::from(time.to_string().magenta()), ]); row.magenta().bold() } else { @@ -158,7 +103,7 @@ fn draw_song_browser(frame: &mut Frame, app: &mut App, size: Rect) { Cell::from(track.green()), Cell::from(title), Cell::from(album), - Cell::from(time.to_string().red()), + Cell::from(time.to_string().magenta()), ]); row } @@ -178,9 +123,9 @@ fn draw_song_browser(frame: &mut Frame, app: &mut App, size: Rect) { let table = Table::new( rows, [ - Constraint::Percentage(33), + Constraint::Percentage(20), Constraint::Percentage(3), - Constraint::Percentage(30), + Constraint::Min(30), Constraint::Percentage(30), Constraint::Percentage(3), ], @@ -205,7 +150,8 @@ fn draw_song_browser(frame: &mut Frame, app: &mut App, size: Rect) { .bg(Color::Black), ) .highlight_symbol(">>") - .header(header); + .header(header) + .flex(layout::Flex::Legacy); state.select(Some(app.browser.selected)); frame.render_stateful_widget(table, size, &mut state); @@ -213,38 +159,101 @@ fn draw_song_browser(frame: &mut Frame, app: &mut App, size: Rect) { /// draws playing queue fn draw_queue(frame: &mut Frame, app: &mut App, size: Rect) { - let mut queue_state = ListState::default(); - let title = Block::default() - .title(Title::from("Play Queue".green().bold())) - .title(Title::from( - format!("({} items)", app.queue_list.list.len()).bold(), - )) - .title( - Title::from(format!("Volume: {}%", app.conn.volume).bold().green()) - .alignment(Alignment::Right), + let rows = app.queue_list.list.iter().map(|s| { + // metadata + let song = app + .conn + .conn + .lsinfo(&Song { + file: app.conn.get_full_path(&s).unwrap_or_default(), + ..Default::default() + }) + .unwrap_or_default(); + let song = song.get(0).unwrap(); + let title = song.clone().title.unwrap_or_else(|| song.clone().file); + let artist = song.clone().artist.unwrap_or_default().cyan(); + let album = song + .tags + .iter() + .filter(|(x, _)| x == "Album") + .map(|(_, l)| l.clone()) + .collect::>() + .join(""); + + let track = song + .tags + .iter() + .filter(|(x, _)| x == "Track") + .map(|(_, l)| l.clone()) + .collect::>() + .join(""); + + let time = humantime::format_duration( + song.clone().duration.unwrap_or_else(|| Duration::new(0, 0)), ); - let mut items: Vec = vec![]; - for item in app.queue_list.list.iter() { - if item.contains(&app.conn.current_song.file) { - items.push(ListItem::new(item.clone().magenta().bold())) + if s.contains(&app.conn.current_song.file) { + let row = Row::new(vec![ + Cell::from(artist), + Cell::from(track.green()), + Cell::from(title), + Cell::from(album), + Cell::from(time.to_string().magenta()), + ]); + row.magenta().bold() } else { - items.push(ListItem::new(item.clone())); + let row = Row::new(vec![ + Cell::from(artist), + Cell::from(track.green()), + Cell::from(title), + Cell::from(album), + Cell::from(time.to_string().magenta()), + ]); + row } - } - let list = List::new(items) - .block(title.borders(Borders::ALL)) - .highlight_style( - Style::new() - .fg(Color::Cyan) - .bg(Color::Black) - .add_modifier(Modifier::BOLD) - .add_modifier(Modifier::REVERSED), - ) - .highlight_symbol(">>"); + }); - queue_state.select(Some(app.queue_list.index)); - frame.render_stateful_widget(list, size, &mut queue_state); + let mut state = TableState::new(); + let header = ["Artist", "Track", "Title", "Album", "Time"] + .into_iter() + .map(Cell::from) + .collect::() + .bold() + .height(1); + let table = Table::new( + rows, + [ + Constraint::Percentage(20), + Constraint::Percentage(3), + Constraint::Min(30), + Constraint::Percentage(30), + Constraint::Percentage(3), + ], + ) + .block( + Block::default() + .title(Title::from("Play Queue".green().bold())) + .title(Title::from( + format!("({} items)", app.queue_list.list.len()).bold(), + )) + .title( + Title::from(format!("Volume: {}%", app.conn.volume).bold().green()) + .alignment(Alignment::Right), + ) + .borders(Borders::ALL), + ) + .highlight_style( + Style::default() + .add_modifier(Modifier::REVERSED) + .fg(Color::Cyan) + .bg(Color::Black), + ) + .highlight_symbol(">>") + .header(header) + .flex(layout::Flex::Legacy); + + state.select(Some(app.queue_list.index)); + frame.render_stateful_widget(table, size, &mut state); } /// draws all playlists