feat: make ui much more asthetically pleasing

This commit is contained in:
Tal 2024-09-26 16:03:43 +02:00
parent e9857e5c11
commit 1b2466e1f8
2 changed files with 94 additions and 30 deletions

View File

@ -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<Player>) -> 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<Player>) -> 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<Player>, sender: Sender<Messages>) -> 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<Player>, sender: Sender<Messages>) -> eyre::Result
}
}
//crossterm::execute!(stderr(), LeaveAlternateScreen)?;
crossterm::execute!(stderr(), Clear(ClearType::FromCursorDown), Show)?;
crossterm::terminal::disable_raw_mode()?;

View File

@ -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()
}
}