diff --git a/src/player/ui.rs b/src/player/ui.rs index db4bf8f..6a5879c 100644 --- a/src/player/ui.rs +++ b/src/player/ui.rs @@ -1,9 +1,11 @@ use std::{io::stderr, sync::Arc, time::Duration}; +use crate::tracks::TrackInfo; + use super::Player; use crossterm::{ - cursor::{MoveToColumn, MoveUp, Show}, - style::Print, + cursor::{Hide, MoveToColumn, MoveUp, Show}, + style::{Print, Stylize}, terminal::{Clear, ClearType}, }; use tokio::{ @@ -14,53 +16,109 @@ use tokio::{ use super::Messages; -async fn interface(queue: Arc) -> eyre::Result<()> { - const WIDTH: usize = 25; - const PROGRESS_WIDTH: usize = WIDTH - 4; +fn format_duration(duration: &Duration) -> String { + let seconds = duration.as_secs() % 60; + let minutes = duration.as_secs() / 60; - loop { - // We can get away with only redrawing every 0.25 seconds - // since it's just an audio player. - sleep(Duration::from_secs_f32(0.25)).await; - crossterm::execute!(stderr(), Clear(ClearType::FromCursorDown))?; + format!("{:02}:{:02}", minutes, seconds) +} - let mut main = match queue.current.load().as_ref() { - Some(x) => { - if queue.sink.is_paused() { - format!("paused {}", x.format_name()) - } else { - format!("playing {}", x.format_name()) - } - } - None => "loading...".to_owned(), +enum Action { + Paused(TrackInfo), + Playing(TrackInfo), + Loading, +} + +impl Action { + fn format(&self) -> (String, usize) { + let (word, subject) = match self { + Action::Playing(x) => ("playing", Some(x.format_name())), + Action::Paused(x) => ("paused", Some(x.format_name())), + Action::Loading => ("loading", None), }; - main.push_str("\r\n"); + if let Some(subject) = subject { + ( + format!("{} {}", word, subject.bold()), + word.len() + 1 + subject.len(), + ) + } else { + (format!("{}", word), word.len()) + } + } +} + +async fn interface(queue: Arc) -> eyre::Result<()> { + const WIDTH: usize = 27; + const PROGRESS_WIDTH: usize = WIDTH - 16; + + loop { + let (mut main, len) = match queue.current.load().as_ref() { + Some(x) => { + if queue.sink.is_paused() { + Action::Paused(*x.clone()) + } else { + Action::Playing(*x.clone()) + } + } + None => Action::Loading, + } + .format(); + + if len > WIDTH { + main = format!("{}...", &main[..=WIDTH]); + } else { + main = format!("{}{}", main, " ".repeat(WIDTH - len)); + } + + let mut duration = Duration::new(0, 0); + let elapsed = queue.sink.get_pos(); let mut filled = 0; if let Some(current) = queue.current.load().as_ref() { - if let Some(duration) = current.duration { - let elapsed = queue.sink.get_pos().as_secs() as f32 / duration.as_secs() as f32; + if let Some(x) = current.duration { + duration = x; + + let elapsed = elapsed.as_secs() as f32 / duration.as_secs() as f32; filled = (elapsed * PROGRESS_WIDTH as f32).round() as usize; } }; let progress = format!( - " [{}{}] ", + " [{}{}] {}/{} ", "/".repeat(filled as usize), - " ".repeat(PROGRESS_WIDTH - filled) + " ".repeat(PROGRESS_WIDTH - filled), + format_duration(&elapsed), + format_duration(&duration), ); - let bar = ["[s]kip", "[p]ause", "[q]uit"]; + let bar = [ + format!("{}kip", "[s]".bold()), + format!("{}ause", "[p]".bold()), + format!("{}uit", "[q]".bold()), + ]; - crossterm::execute!(stderr(), MoveToColumn(0), Print(main))?; - crossterm::execute!(stderr(), Print(progress), Print("\r\n"))?; - crossterm::execute!(stderr(), Print(bar.join(" ")), Print("\r\n"))?; - crossterm::execute!(stderr(), MoveToColumn(0), MoveUp(3))?; + // Formats the menu properly + let menu = + [main, progress, bar.join(" ")].map(|x| format!("│ {x} │\r\n").reset().to_string()); + + crossterm::execute!(stderr(), Clear(ClearType::FromCursorDown))?; + crossterm::execute!( + stderr(), + MoveToColumn(0), + Print(format!("┌{}┐\r\n", "─".repeat(WIDTH + 2))), + Print(menu.join("")), + Print(format!("└{}┘", "─".repeat(WIDTH + 2))), + MoveToColumn(0), + MoveUp(4) + )?; + + sleep(Duration::from_secs_f32(0.25)).await; } } pub async fn start(queue: Arc, sender: Sender) -> eyre::Result<()> { crossterm::terminal::enable_raw_mode()?; + crossterm::execute!(stderr(), Hide)?; //crossterm::execute!(stderr(), EnterAlternateScreen, MoveTo(0, 0))?; task::spawn(interface(queue.clone())); @@ -90,6 +148,7 @@ pub async fn start(queue: Arc, sender: Sender) -> eyre::Result } } + //crossterm::execute!(stderr(), LeaveAlternateScreen)?; crossterm::execute!(stderr(), Clear(ClearType::FromCursorDown), Show)?; crossterm::terminal::disable_raw_mode()?; diff --git a/src/tracks.rs b/src/tracks.rs index eace3eb..9832f03 100644 --- a/src/tracks.rs +++ b/src/tracks.rs @@ -34,7 +34,12 @@ pub struct TrackInfo { impl TrackInfo { pub fn format_name(&self) -> &'static str { - self.name.split("/").nth(2).unwrap() + self.name + .split("/") + .nth(2) + .unwrap() + .strip_suffix(".mp3") + .unwrap() } }