add metadata in queue list
This commit is contained in:
parent
c61b0503c7
commit
bf531facb0
|
|
@ -38,6 +38,6 @@ A MPD client in Rust
|
||||||
- [x] add to playlists
|
- [x] add to playlists
|
||||||
- [x] search for songs
|
- [x] search for songs
|
||||||
- [x] Humantime format
|
- [x] Humantime format
|
||||||
|
- [x] metadata based tree view
|
||||||
- [ ] view playlist
|
- [ ] view playlist
|
||||||
- [ ] change playlist name
|
- [ ] change playlist name
|
||||||
- [ ] metadata based tree view
|
|
||||||
|
|
|
||||||
|
|
@ -98,24 +98,27 @@ impl FileBrowser {
|
||||||
|
|
||||||
// Go to next item in filetree
|
// Go to next item in filetree
|
||||||
pub fn next(&mut self) {
|
pub fn next(&mut self) {
|
||||||
// if self.selected < self.filetree.len() - 1 {
|
if self.selected < self.filetree.len() - 1 {
|
||||||
// self.selected += 1;
|
|
||||||
// }
|
|
||||||
|
|
||||||
if self.selected == self.filetree.len() - 1 {
|
|
||||||
self.selected = 0;
|
|
||||||
} else {
|
|
||||||
self.selected += 1;
|
self.selected += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if self.selected == self.filetree.len() - 1 {
|
||||||
|
// self.selected = 0;
|
||||||
|
// } else {
|
||||||
|
// self.selected += 1;
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Go to previous item in filetree
|
/// Go to previous item in filetree
|
||||||
pub fn prev(&mut self) {
|
pub fn prev(&mut self) {
|
||||||
if self.selected == 0 {
|
if self.selected != 0 {
|
||||||
self.selected = self.filetree.len() - 1;
|
|
||||||
} else {
|
|
||||||
self.selected -= 1;
|
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<()> {
|
pub fn handle_go_back(&mut self, conn: &mut Connection) -> AppResult<()> {
|
||||||
|
|
|
||||||
|
|
@ -62,7 +62,6 @@ pub fn handle_key_events(key_event: KeyEvent, app: &mut App) -> AppResult<()> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
match key_event.code {
|
match key_event.code {
|
||||||
|
|
@ -200,7 +199,6 @@ pub fn handle_key_events(key_event: KeyEvent, app: &mut App) -> AppResult<()> {
|
||||||
app.conn
|
app.conn
|
||||||
.load_playlist(app.pl_list.list.get(app.pl_list.index).unwrap())?;
|
.load_playlist(app.pl_list.list.get(app.pl_list.index).unwrap())?;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
app.conn.update_status();
|
app.conn.update_status();
|
||||||
}
|
}
|
||||||
|
|
@ -279,6 +277,7 @@ pub fn handle_key_events(key_event: KeyEvent, app: &mut App) -> AppResult<()> {
|
||||||
KeyCode::Char('>') => {
|
KeyCode::Char('>') => {
|
||||||
if !app.queue_list.list.is_empty() {
|
if !app.queue_list.list.is_empty() {
|
||||||
app.conn.conn.next()?;
|
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('<') => {
|
KeyCode::Char('<') => {
|
||||||
if !app.queue_list.list.is_empty() {
|
if !app.queue_list.list.is_empty() {
|
||||||
app.conn.conn.prev()?;
|
app.conn.conn.prev()?;
|
||||||
|
app.update_queue();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
32
src/list.rs
32
src/list.rs
|
|
@ -14,30 +14,36 @@ impl<T> ContentList<T> {
|
||||||
|
|
||||||
// Go to next item in list
|
// Go to next item in list
|
||||||
pub fn next(&mut self) {
|
pub fn next(&mut self) {
|
||||||
// if self.index < self.list.len() - 1 {
|
|
||||||
// self.index += 1;
|
|
||||||
// }
|
|
||||||
|
|
||||||
let len = self.list.len();
|
let len = self.list.len();
|
||||||
if len != 0 {
|
if len != 0 {
|
||||||
if self.index == self.list.len() - 1 {
|
if self.index < len - 1 {
|
||||||
self.index = 0;
|
|
||||||
} else {
|
|
||||||
self.index += 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
|
/// Go to previous item in list
|
||||||
pub fn prev(&mut self) {
|
pub fn prev(&mut self) {
|
||||||
if self.index == 0 {
|
if self.index != 0 {
|
||||||
let len = self.list.len();
|
|
||||||
if len != 0 {
|
|
||||||
self.index = len - 1;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
self.index -= 1;
|
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) {
|
pub fn reset_index(&mut self) {
|
||||||
|
|
|
||||||
189
src/ui.rs
189
src/ui.rs
|
|
@ -1,6 +1,7 @@
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use crate::app::{App, SelectedTab};
|
use crate::app::{App, SelectedTab};
|
||||||
|
use mpd::Song;
|
||||||
use ratatui::{
|
use ratatui::{
|
||||||
prelude::*,
|
prelude::*,
|
||||||
widgets::{block::Title, *},
|
widgets::{block::Title, *},
|
||||||
|
|
@ -32,8 +33,7 @@ pub fn render(app: &mut App, frame: &mut Frame) {
|
||||||
match app.selected_tab {
|
match app.selected_tab {
|
||||||
SelectedTab::Queue => draw_queue(frame, app, layout[0]),
|
SelectedTab::Queue => draw_queue(frame, app, layout[0]),
|
||||||
SelectedTab::Playlists => draw_playlists(frame, app, layout[0]),
|
SelectedTab::Playlists => draw_playlists(frame, app, layout[0]),
|
||||||
SelectedTab::DirectoryBrowser => draw_song_browser(frame, app, layout[0]),
|
SelectedTab::DirectoryBrowser => draw_directory_browser(frame, app, layout[0]),
|
||||||
// SelectedTab::SongBrowser => draw_song_browser(frame, app, layout[0]),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
match app.inputmode {
|
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) {
|
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<ListItem> = 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 total_songs = app.conn.conn.stats().unwrap().songs.to_string();
|
||||||
|
|
||||||
let rows = app.browser.filetree.iter().enumerate().map(|(i, (t, s))| {
|
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(track.green()),
|
||||||
Cell::from(title),
|
Cell::from(title),
|
||||||
Cell::from(album),
|
Cell::from(album),
|
||||||
Cell::from(time.to_string().red()),
|
Cell::from(time.to_string().magenta()),
|
||||||
]);
|
]);
|
||||||
row.magenta().bold()
|
row.magenta().bold()
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -158,7 +103,7 @@ fn draw_song_browser(frame: &mut Frame, app: &mut App, size: Rect) {
|
||||||
Cell::from(track.green()),
|
Cell::from(track.green()),
|
||||||
Cell::from(title),
|
Cell::from(title),
|
||||||
Cell::from(album),
|
Cell::from(album),
|
||||||
Cell::from(time.to_string().red()),
|
Cell::from(time.to_string().magenta()),
|
||||||
]);
|
]);
|
||||||
row
|
row
|
||||||
}
|
}
|
||||||
|
|
@ -178,9 +123,9 @@ fn draw_song_browser(frame: &mut Frame, app: &mut App, size: Rect) {
|
||||||
let table = Table::new(
|
let table = Table::new(
|
||||||
rows,
|
rows,
|
||||||
[
|
[
|
||||||
Constraint::Percentage(33),
|
Constraint::Percentage(20),
|
||||||
Constraint::Percentage(3),
|
Constraint::Percentage(3),
|
||||||
Constraint::Percentage(30),
|
Constraint::Min(30),
|
||||||
Constraint::Percentage(30),
|
Constraint::Percentage(30),
|
||||||
Constraint::Percentage(3),
|
Constraint::Percentage(3),
|
||||||
],
|
],
|
||||||
|
|
@ -205,7 +150,8 @@ fn draw_song_browser(frame: &mut Frame, app: &mut App, size: Rect) {
|
||||||
.bg(Color::Black),
|
.bg(Color::Black),
|
||||||
)
|
)
|
||||||
.highlight_symbol(">>")
|
.highlight_symbol(">>")
|
||||||
.header(header);
|
.header(header)
|
||||||
|
.flex(layout::Flex::Legacy);
|
||||||
|
|
||||||
state.select(Some(app.browser.selected));
|
state.select(Some(app.browser.selected));
|
||||||
frame.render_stateful_widget(table, size, &mut state);
|
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
|
/// draws playing queue
|
||||||
fn draw_queue(frame: &mut Frame, app: &mut App, size: Rect) {
|
fn draw_queue(frame: &mut Frame, app: &mut App, size: Rect) {
|
||||||
let mut queue_state = ListState::default();
|
let rows = app.queue_list.list.iter().map(|s| {
|
||||||
let title = Block::default()
|
// metadata
|
||||||
.title(Title::from("Play Queue".green().bold()))
|
let song = app
|
||||||
.title(Title::from(
|
.conn
|
||||||
format!("({} items)", app.queue_list.list.len()).bold(),
|
.conn
|
||||||
))
|
.lsinfo(&Song {
|
||||||
.title(
|
file: app.conn.get_full_path(&s).unwrap_or_default(),
|
||||||
Title::from(format!("Volume: {}%", app.conn.volume).bold().green())
|
..Default::default()
|
||||||
.alignment(Alignment::Right),
|
})
|
||||||
|
.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::<Vec<String>>()
|
||||||
|
.join("");
|
||||||
|
|
||||||
|
let track = song
|
||||||
|
.tags
|
||||||
|
.iter()
|
||||||
|
.filter(|(x, _)| x == "Track")
|
||||||
|
.map(|(_, l)| l.clone())
|
||||||
|
.collect::<Vec<String>>()
|
||||||
|
.join("");
|
||||||
|
|
||||||
|
let time = humantime::format_duration(
|
||||||
|
song.clone().duration.unwrap_or_else(|| Duration::new(0, 0)),
|
||||||
);
|
);
|
||||||
|
|
||||||
let mut items: Vec<ListItem> = vec![];
|
if s.contains(&app.conn.current_song.file) {
|
||||||
for item in app.queue_list.list.iter() {
|
let row = Row::new(vec![
|
||||||
if item.contains(&app.conn.current_song.file) {
|
Cell::from(artist),
|
||||||
items.push(ListItem::new(item.clone().magenta().bold()))
|
Cell::from(track.green()),
|
||||||
|
Cell::from(title),
|
||||||
|
Cell::from(album),
|
||||||
|
Cell::from(time.to_string().magenta()),
|
||||||
|
]);
|
||||||
|
row.magenta().bold()
|
||||||
} else {
|
} 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));
|
let mut state = TableState::new();
|
||||||
frame.render_stateful_widget(list, size, &mut queue_state);
|
let header = ["Artist", "Track", "Title", "Album", "Time"]
|
||||||
|
.into_iter()
|
||||||
|
.map(Cell::from)
|
||||||
|
.collect::<Row>()
|
||||||
|
.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
|
/// draws all playlists
|
||||||
|
|
|
||||||
Reference in New Issue