mirror of
https://github.com/talwat/lowfi
synced 2025-02-10 17:42:03 +00:00
feat: make ui much more asthetically pleasing
This commit is contained in:
parent
e9857e5c11
commit
1b2466e1f8
117
src/player/ui.rs
117
src/player/ui.rs
@ -1,9 +1,11 @@
|
|||||||
use std::{io::stderr, sync::Arc, time::Duration};
|
use std::{io::stderr, sync::Arc, time::Duration};
|
||||||
|
|
||||||
|
use crate::tracks::TrackInfo;
|
||||||
|
|
||||||
use super::Player;
|
use super::Player;
|
||||||
use crossterm::{
|
use crossterm::{
|
||||||
cursor::{MoveToColumn, MoveUp, Show},
|
cursor::{Hide, MoveToColumn, MoveUp, Show},
|
||||||
style::Print,
|
style::{Print, Stylize},
|
||||||
terminal::{Clear, ClearType},
|
terminal::{Clear, ClearType},
|
||||||
};
|
};
|
||||||
use tokio::{
|
use tokio::{
|
||||||
@ -14,53 +16,109 @@ use tokio::{
|
|||||||
|
|
||||||
use super::Messages;
|
use super::Messages;
|
||||||
|
|
||||||
async fn interface(queue: Arc<Player>) -> eyre::Result<()> {
|
fn format_duration(duration: &Duration) -> String {
|
||||||
const WIDTH: usize = 25;
|
let seconds = duration.as_secs() % 60;
|
||||||
const PROGRESS_WIDTH: usize = WIDTH - 4;
|
let minutes = duration.as_secs() / 60;
|
||||||
|
|
||||||
loop {
|
format!("{:02}:{:02}", minutes, seconds)
|
||||||
// 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))?;
|
|
||||||
|
|
||||||
let mut main = match queue.current.load().as_ref() {
|
enum Action {
|
||||||
Some(x) => {
|
Paused(TrackInfo),
|
||||||
if queue.sink.is_paused() {
|
Playing(TrackInfo),
|
||||||
format!("paused {}", x.format_name())
|
Loading,
|
||||||
} else {
|
}
|
||||||
format!("playing {}", x.format_name())
|
|
||||||
}
|
impl Action {
|
||||||
}
|
fn format(&self) -> (String, usize) {
|
||||||
None => "loading...".to_owned(),
|
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;
|
let mut filled = 0;
|
||||||
if let Some(current) = queue.current.load().as_ref() {
|
if let Some(current) = queue.current.load().as_ref() {
|
||||||
if let Some(duration) = current.duration {
|
if let Some(x) = current.duration {
|
||||||
let elapsed = queue.sink.get_pos().as_secs() as f32 / duration.as_secs() as f32;
|
duration = x;
|
||||||
|
|
||||||
|
let elapsed = elapsed.as_secs() as f32 / duration.as_secs() as f32;
|
||||||
filled = (elapsed * PROGRESS_WIDTH as f32).round() as usize;
|
filled = (elapsed * PROGRESS_WIDTH as f32).round() as usize;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let progress = format!(
|
let progress = format!(
|
||||||
" [{}{}] ",
|
" [{}{}] {}/{} ",
|
||||||
"/".repeat(filled as usize),
|
"/".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))?;
|
// Formats the menu properly
|
||||||
crossterm::execute!(stderr(), Print(progress), Print("\r\n"))?;
|
let menu =
|
||||||
crossterm::execute!(stderr(), Print(bar.join(" ")), Print("\r\n"))?;
|
[main, progress, bar.join(" ")].map(|x| format!("│ {x} │\r\n").reset().to_string());
|
||||||
crossterm::execute!(stderr(), MoveToColumn(0), MoveUp(3))?;
|
|
||||||
|
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<()> {
|
pub async fn start(queue: Arc<Player>, sender: Sender<Messages>) -> eyre::Result<()> {
|
||||||
crossterm::terminal::enable_raw_mode()?;
|
crossterm::terminal::enable_raw_mode()?;
|
||||||
|
crossterm::execute!(stderr(), Hide)?;
|
||||||
//crossterm::execute!(stderr(), EnterAlternateScreen, MoveTo(0, 0))?;
|
//crossterm::execute!(stderr(), EnterAlternateScreen, MoveTo(0, 0))?;
|
||||||
|
|
||||||
task::spawn(interface(queue.clone()));
|
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::execute!(stderr(), Clear(ClearType::FromCursorDown), Show)?;
|
||||||
crossterm::terminal::disable_raw_mode()?;
|
crossterm::terminal::disable_raw_mode()?;
|
||||||
|
|
||||||
|
@ -34,7 +34,12 @@ pub struct TrackInfo {
|
|||||||
|
|
||||||
impl TrackInfo {
|
impl TrackInfo {
|
||||||
pub fn format_name(&self) -> &'static str {
|
pub fn format_name(&self) -> &'static str {
|
||||||
self.name.split("/").nth(2).unwrap()
|
self.name
|
||||||
|
.split("/")
|
||||||
|
.nth(2)
|
||||||
|
.unwrap()
|
||||||
|
.strip_suffix(".mp3")
|
||||||
|
.unwrap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user