From cd16a3b3401fae08fb90f5c3b21dc9c2a5f303d8 Mon Sep 17 00:00:00 2001 From: Jika Date: Sun, 30 Mar 2025 23:36:25 +0200 Subject: [PATCH] Pretify queue widget --- Cargo.lock | 7 +++++++ Cargo.toml | 1 + src/app.rs | 6 +++++- src/player.rs | 9 ++++----- src/widgets/queue.rs | 46 ++++++++++++++++++++++++++++++++------------ 5 files changed, 51 insertions(+), 18 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 67be149..e510b13 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -37,6 +37,7 @@ dependencies = [ "dotenv", "figment", "futures", + "humantime", "itertools 0.14.0", "md5", "rand", @@ -780,6 +781,12 @@ version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" +[[package]] +name = "humantime" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b112acc8b3adf4b107a8ec20977da0273a8c386765a3ec0229bd500a1443f9f" + [[package]] name = "hyper" version = "1.6.0" diff --git a/Cargo.toml b/Cargo.toml index 74a3857..8254b79 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,6 +23,7 @@ directories = "6" ratatui = "0.29" crossterm = { version = "0.28.1", features = ["event-stream", "serde"] } +humantime = "2.2" tokio = { version = "1.43", features = ["macros", "rt"] } tokio-stream = "0.1" diff --git a/src/app.rs b/src/app.rs index 2ff71bc..722c0ca 100644 --- a/src/app.rs +++ b/src/app.rs @@ -160,7 +160,11 @@ impl App { Action::Init => self.tx.send(Action::RandomQueue)?, Action::TryPlaySong(id) => self.player.try_play(id, self.client.clone())?, Action::AddToSink(id) => { - self.player.add_to_sink(id)?; + let res = self.player.add_to_sink(id); + if res.is_err(){ + self.tx.send(Action::TrackEnded)?; + } + res? } Action::ScrollUp => { if self.mode == Mode::Queue { diff --git a/src/player.rs b/src/player.rs index dc600ff..4e3c20f 100644 --- a/src/player.rs +++ b/src/player.rs @@ -84,6 +84,10 @@ impl Player { /// Add the song specified by the index to the sink pub fn add_to_sink(&mut self, index: usize) -> Result<()> { + if self.sink.empty() { + // set only if is first to be added to sink + self.current_playing = index; + } // Retrieve file from index let id = self.songs_list.lock().unwrap()[index].id.clone(); let mut path = config::temp_dir(); @@ -94,11 +98,6 @@ impl Player { // This help having a constant volume let agc_source = decoder.automatic_gain_control(1.0, 4.0, 0.005, 5.0); - if self.sink.empty() { - // set only if is first to be added to sink - self.current_playing = index; - } - self.sink.append(agc_source); debug!("{id} added to sink"); diff --git a/src/widgets/queue.rs b/src/widgets/queue.rs index 6511cf7..1199e9a 100644 --- a/src/widgets/queue.rs +++ b/src/widgets/queue.rs @@ -1,3 +1,4 @@ +use humantime::format_duration; use itertools::Itertools; use ratatui::{layout::Constraint::*, prelude::*, widgets::*}; use ratatui::{ @@ -5,6 +6,7 @@ use ratatui::{ widgets::{StatefulWidget, Table, TableState}, }; use std::sync::{Arc, Mutex}; +use std::time::Duration; use style::Styled; use subsonic_types::response::Child; @@ -29,13 +31,10 @@ impl StatefulWidget for &Queue { type State = TableState; fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) { if !self.songs_list.lock().unwrap().is_empty() { - let vertical_pad = |line| Text::from(vec!["".into(), line, "".into()]); - let header_cells = ["#", "Title", "Artist", "Duration"] - .map(|h| h.bold().into()) - .map(vertical_pad); - let header = Row::new(header_cells).height(3); + let header_cells = ["#", "Title", "Artist", "Duration"].map(|h| h.bold()); + let header = Row::new(header_cells).height(2); - let column_widths = [Max(4), Max(20), Max(20), Max(6)]; + let column_widths = [Max(4), Min(20), Min(20), Max(8)]; let list = self.songs_list.lock().unwrap(); let rows = list @@ -50,14 +49,36 @@ impl StatefulWidget for &Queue { }) .collect_vec(); + let queue_duration = list.iter().fold(Duration::ZERO, |mut acc, song| { + acc += song + .duration + .map(|sec| sec.to_duration()) + .unwrap_or(Duration::ZERO); + acc + }); + + let block = Block::new() + .border_type(BorderType::Rounded) + .borders(Borders::all()) + .border_style(Style::default().fg(Color::White)) + .padding(Padding::right(1)) + .title("Queue") + .title( + Line::from(format!( + "| Total {} songs | {} |", + list.len(), + format_duration(queue_duration) + )) + .left_aligned(), + ); + let table = Table::new(rows, column_widths) .header(header) - .column_spacing(3) + .column_spacing(2) .highlight_spacing(HighlightSpacing::Always) - .row_highlight_style(Style::new().reversed()) - .column_highlight_style(Style::new().red()) - .cell_highlight_style(Style::new().blue()) - .highlight_symbol(">>"); + .row_highlight_style(Style::new().fg(Color::Magenta)) + .highlight_symbol(">>") + .block(block); StatefulWidget::render(table, area, buf, state); } else { @@ -72,6 +93,7 @@ fn row_from_song(index: usize, song: &Child) -> Row { Text::from(index.to_string()), Text::from(song.title.clone()), Text::from(song.artist.clone().unwrap()), - Text::from(format!("{:0>2}:{:0>2}", duration / 60, duration % 60)), + Text::from(format!("{:0>2}:{:0>2}", duration / 60, duration % 60)) + .alignment(Alignment::Right), ]) }