alot of edge cases removed, ui improvments
This commit is contained in:
parent
59e8e8cbe6
commit
04e5d2ad28
|
|
@ -225,6 +225,12 @@ version = "0.5.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||
|
||||
[[package]]
|
||||
name = "humantime"
|
||||
version = "2.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
|
||||
|
||||
[[package]]
|
||||
name = "indoc"
|
||||
version = "2.0.5"
|
||||
|
|
@ -386,6 +392,7 @@ version = "0.1.0"
|
|||
dependencies = [
|
||||
"clap",
|
||||
"crossterm",
|
||||
"humantime",
|
||||
"mpd",
|
||||
"ratatui",
|
||||
"rust-fuzzy-search",
|
||||
|
|
|
|||
|
|
@ -13,3 +13,4 @@ simple-dmenu = "0.1.0"
|
|||
ratatui = "0.26.2"
|
||||
crossterm = "0.27.0"
|
||||
rust-fuzzy-search = "0.1.1"
|
||||
humantime = "2.1.0"
|
||||
|
|
|
|||
15
README.md
15
README.md
|
|
@ -1,7 +1,7 @@
|
|||
A MPD client in Rust
|
||||
|
||||
### Keys
|
||||
- `q` OR 'Ctr+C' to quit
|
||||
- `q` OR `Ctr+C` to quit
|
||||
- `p` to toggle pause
|
||||
- `+` to increase volume
|
||||
- `-` to decrease volume
|
||||
|
|
@ -16,9 +16,18 @@ A MPD client in Rust
|
|||
- `3` to go to playlists view
|
||||
- `Enter` to add song/playlist to current playlist
|
||||
- `a` to append the song to current playing queue
|
||||
- `f` to go forward
|
||||
- `b` to go backwords
|
||||
- `f` to go forwards
|
||||
- `b` to go backwards
|
||||
- `>` to play next song from queue
|
||||
- `<` to play previous song from queue
|
||||
- `U` to update the MPD database
|
||||
- `r` to toggle repeat
|
||||
- `z` to toggle random
|
||||
|
||||
### TODO
|
||||
- [x] fix performance issues
|
||||
- [ ] improvements on queue control
|
||||
- [ ] add to playlists, playlists view
|
||||
- [ ] search for songs
|
||||
- [ ] metadata based tree view
|
||||
- [ ] Humantime format
|
||||
|
|
|
|||
21
src/app.rs
21
src/app.rs
|
|
@ -14,7 +14,6 @@ pub struct App {
|
|||
/// check if app is running
|
||||
pub running: bool,
|
||||
pub conn: Connection,
|
||||
pub song_list: ContentList<String>,
|
||||
pub queue_list: ContentList<String>,
|
||||
pub pl_list: ContentList<String>,
|
||||
pub selected_tab: SelectedTab,
|
||||
|
|
@ -28,16 +27,6 @@ pub enum SelectedTab {
|
|||
Playlists,
|
||||
}
|
||||
|
||||
impl SelectedTab {
|
||||
fn as_usize(&self) {
|
||||
match self {
|
||||
SelectedTab::Queue => 0,
|
||||
SelectedTab::Playlists => 1,
|
||||
SelectedTab::DirectoryBrowser => 2,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
impl App {
|
||||
pub fn builder(addrs: &str) -> AppResult<Self> {
|
||||
let mut conn = Connection::new(addrs).unwrap();
|
||||
|
|
@ -47,14 +36,10 @@ impl App {
|
|||
|
||||
Self::get_queue(&mut conn, &mut queue_list.list);
|
||||
|
||||
let mut song_list = ContentList::new();
|
||||
song_list.list = conn.songs_filenames.clone();
|
||||
|
||||
let browser = FileBrowser::new();
|
||||
Ok(Self {
|
||||
running: true,
|
||||
conn,
|
||||
song_list,
|
||||
queue_list,
|
||||
pl_list,
|
||||
selected_tab: SelectedTab::DirectoryBrowser,
|
||||
|
|
@ -63,11 +48,9 @@ impl App {
|
|||
}
|
||||
|
||||
pub fn tick(&mut self) {
|
||||
self.conn.update_state();
|
||||
self.conn.update_progress();
|
||||
self.conn.update_volume();
|
||||
self.conn.update_status();
|
||||
self.update_queue();
|
||||
self.browser.update_directory(&mut self.conn).unwrap();
|
||||
// self.browser.update_directory(&mut self.conn).unwrap();
|
||||
}
|
||||
|
||||
pub fn quit(&mut self) {
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
use mpd::Query;
|
||||
|
||||
use crate::{app::AppResult, connection::Connection};
|
||||
|
||||
#[derive(Debug)]
|
||||
|
|
@ -59,32 +61,45 @@ impl FileBrowser {
|
|||
if t == "directory" {
|
||||
if path != "." {
|
||||
self.prev_path = self.path.clone();
|
||||
self.path = path.to_string();
|
||||
self.path = self.prev_path.clone() + "/" + path;
|
||||
self.update_directory(conn)?;
|
||||
self.prev_selected = self.selected;
|
||||
self.selected = 0;
|
||||
// println!("self.path: {}", self.path);
|
||||
// println!("self.prev_pat: {}", self.prev_path);
|
||||
}
|
||||
} else {
|
||||
let list = conn
|
||||
.songs_filenames
|
||||
.iter()
|
||||
.map(|f| f.as_str())
|
||||
.collect::<Vec<&str>>();
|
||||
let (filename, _) = rust_fuzzy_search::fuzzy_search_sorted(&path, &list)
|
||||
.get(0)
|
||||
.unwrap()
|
||||
.clone();
|
||||
// let list = conn
|
||||
// .songs_filenames
|
||||
// .iter()
|
||||
// .map(|f| f.as_str())
|
||||
// .collect::<Vec<&str>>();
|
||||
// let (filename, _) = rust_fuzzy_search::fuzzy_search_sorted(&path, &list)
|
||||
// .get(0)
|
||||
// .unwrap()
|
||||
// .clone();
|
||||
|
||||
for filename in conn.songs_filenames.clone().iter() {
|
||||
if filename.contains(path) {
|
||||
let song = conn.get_song_with_only_filename(filename);
|
||||
conn.push(&song)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn handle_go_back(&mut self, conn: &mut Connection) -> AppResult<()> {
|
||||
self.path = self.prev_path.clone();
|
||||
self.selected = self.prev_selected;
|
||||
if self.prev_path != "." {
|
||||
let r = self.path.rfind("/").unwrap();
|
||||
self.path = self.path.as_str()[..r].to_string();
|
||||
self.update_directory(conn)?;
|
||||
} else {
|
||||
self.path = self.prev_path.clone();
|
||||
self.update_directory(conn)?;
|
||||
}
|
||||
|
||||
self.selected = self.prev_selected;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,10 +6,10 @@ use clap::{Parser, Subcommand};
|
|||
pub struct Args {
|
||||
/// No TUI
|
||||
#[arg(short= 'n', default_value="false")]
|
||||
pub no_tui: bool,
|
||||
pub tui: bool,
|
||||
|
||||
#[command(subcommand)]
|
||||
pub command: Command,
|
||||
pub command: Option<Command>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Subcommand)]
|
||||
|
|
|
|||
|
|
@ -15,6 +15,8 @@ pub struct Connection {
|
|||
pub elapsed: Duration,
|
||||
pub total_duration: Duration,
|
||||
pub volume: u8,
|
||||
pub repeat: bool,
|
||||
pub random: bool,
|
||||
}
|
||||
|
||||
impl Connection {
|
||||
|
|
@ -27,8 +29,11 @@ impl Connection {
|
|||
.into_iter()
|
||||
.map(|x| x.file)
|
||||
.collect();
|
||||
let (elapsed, total) = conn.status().unwrap().time.unwrap_or_default();
|
||||
let volume: u8 = conn.status().unwrap_or_default().volume as u8;
|
||||
let status = conn.status().unwrap();
|
||||
let (elapsed, total) = status.time.unwrap_or_default();
|
||||
let volume: u8 = status.volume as u8;
|
||||
let repeat = status.repeat;
|
||||
let random = status.random;
|
||||
|
||||
Ok(Self {
|
||||
conn,
|
||||
|
|
@ -37,6 +42,8 @@ impl Connection {
|
|||
elapsed,
|
||||
total_duration: total,
|
||||
volume,
|
||||
repeat,
|
||||
random,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -64,25 +71,33 @@ impl Connection {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn update_state(&mut self) -> String {
|
||||
match self.conn.status().unwrap().state {
|
||||
/// Update status
|
||||
pub fn update_status(&mut self) {
|
||||
let status = self.conn.status().unwrap();
|
||||
|
||||
// Playback State
|
||||
match status.state {
|
||||
State::Stop => self.state = "Stopped".to_string(),
|
||||
State::Play => self.state = "Playing".to_string(),
|
||||
State::Pause => self.state = "Paused".to_string(),
|
||||
}
|
||||
self.state.clone()
|
||||
}
|
||||
|
||||
pub fn update_progress(&mut self) {
|
||||
let (elapsed, total) = self.conn.status().unwrap().time.unwrap_or_default();
|
||||
// Progress
|
||||
let (elapsed, total) = status.time.unwrap_or_default();
|
||||
self.elapsed = elapsed;
|
||||
self.total_duration = total;
|
||||
|
||||
// Volume
|
||||
self.volume = status.volume as u8;
|
||||
|
||||
// Repeat mode
|
||||
self.repeat = status.repeat;
|
||||
|
||||
// Random mode
|
||||
self.random = status.random;
|
||||
}
|
||||
|
||||
pub fn update_volume(&mut self) {
|
||||
self.volume = self.conn.status().unwrap_or_default().volume as u8;
|
||||
}
|
||||
|
||||
/// Get progress ratio of current playing song
|
||||
pub fn get_progress_ratio(&self) -> f64 {
|
||||
let total = self.total_duration.as_secs_f64();
|
||||
if total == 0.0 {
|
||||
|
|
@ -143,11 +158,6 @@ impl Connection {
|
|||
}
|
||||
}
|
||||
|
||||
/// get current playing song
|
||||
pub fn get_current_song(&mut self) -> Option<String> {
|
||||
self.conn.currentsong().unwrap().unwrap_or_default().title
|
||||
}
|
||||
|
||||
/// Print status to stdout
|
||||
pub fn status(&mut self) {
|
||||
let current_song = self.conn.currentsong();
|
||||
|
|
@ -170,7 +180,7 @@ impl Connection {
|
|||
let song = self.conn.currentsong()?.unwrap_or_default();
|
||||
if let Some(s) = song.title {
|
||||
if let Some(a) = song.artist {
|
||||
return Ok(Some(format!("\"{}\" By {}", a, s)));
|
||||
return Ok(Some(format!("\"{}\" By {}", s, a)));
|
||||
} else {
|
||||
return Ok(Some(s));
|
||||
}
|
||||
|
|
@ -190,7 +200,26 @@ impl Connection {
|
|||
self.conn.toggle_pause().unwrap();
|
||||
}
|
||||
|
||||
/// Toggle Repeat mode
|
||||
pub fn toggle_repeat(&mut self) {
|
||||
if self.conn.status().unwrap().repeat {
|
||||
self.conn.repeat(false).unwrap();
|
||||
} else {
|
||||
self.conn.repeat(true).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
/// Toggle random mode
|
||||
pub fn toggle_random(&mut self) {
|
||||
if self.conn.status().unwrap().random {
|
||||
self.conn.random(false).unwrap();
|
||||
} else {
|
||||
self.conn.random(true).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
// Volume controls
|
||||
/// Increase Volume
|
||||
pub fn inc_volume(&mut self, v: i8) {
|
||||
let cur = self.conn.status().unwrap().volume;
|
||||
if cur + v <= 100 {
|
||||
|
|
@ -198,6 +227,7 @@ impl Connection {
|
|||
}
|
||||
}
|
||||
|
||||
/// Decrease volume
|
||||
pub fn dec_volume(&mut self, v: i8) {
|
||||
let cur = self.conn.status().unwrap().volume;
|
||||
if cur - v >= 0 {
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ pub fn handle_key_events(key_event: KeyEvent, app: &mut App) -> AppResult<()> {
|
|||
app.quit();
|
||||
} else {
|
||||
app.conn.conn.clear()?;
|
||||
app.conn.update_status();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -47,6 +48,7 @@ pub fn handle_key_events(key_event: KeyEvent, app: &mut App) -> AppResult<()> {
|
|||
.push_playlist(app.pl_list.list.get(app.pl_list.index).unwrap())?;
|
||||
}
|
||||
}
|
||||
app.conn.update_status();
|
||||
}
|
||||
|
||||
KeyCode::Char('h') => match app.selected_tab {
|
||||
|
|
@ -68,6 +70,18 @@ pub fn handle_key_events(key_event: KeyEvent, app: &mut App) -> AppResult<()> {
|
|||
app.conn.pause();
|
||||
}
|
||||
|
||||
// Toggle rpeat
|
||||
KeyCode::Char('r') => {
|
||||
app.conn.toggle_repeat();
|
||||
app.conn.update_status();
|
||||
}
|
||||
|
||||
// Toggle random
|
||||
KeyCode::Char('z') => {
|
||||
app.conn.toggle_random();
|
||||
app.conn.update_status();
|
||||
}
|
||||
|
||||
// Dmenu prompt
|
||||
KeyCode::Char('D') => {
|
||||
app.conn.play_dmenu()?;
|
||||
|
|
@ -75,9 +89,23 @@ pub fn handle_key_events(key_event: KeyEvent, app: &mut App) -> AppResult<()> {
|
|||
|
||||
// add to queue
|
||||
KeyCode::Char('a') => {
|
||||
let song = app.conn.get_song_with_only_filename(
|
||||
app.conn.songs_filenames.get(app.song_list.index).unwrap(),
|
||||
);
|
||||
// let song = app.conn.get_song_with_only_filename(
|
||||
// app.conn.songs_filenames.get(app.song_list.index).unwrap(),
|
||||
// );
|
||||
|
||||
let list = app
|
||||
.conn
|
||||
.songs_filenames
|
||||
.iter()
|
||||
.map(|f| f.as_str())
|
||||
.collect::<Vec<&str>>();
|
||||
let (filename, _) = rust_fuzzy_search::fuzzy_search_sorted(&app.browser.path, &list)
|
||||
.get(0)
|
||||
.unwrap()
|
||||
.clone();
|
||||
|
||||
let song = app.conn.get_song_with_only_filename(filename);
|
||||
|
||||
app.conn.conn.push(&song)?;
|
||||
}
|
||||
|
||||
|
|
@ -127,16 +155,22 @@ pub fn handle_key_events(key_event: KeyEvent, app: &mut App) -> AppResult<()> {
|
|||
// Volume controls
|
||||
KeyCode::Char('=') => {
|
||||
app.conn.inc_volume(2);
|
||||
app.conn.update_volume();
|
||||
app.conn.update_status();
|
||||
}
|
||||
|
||||
KeyCode::Char('-') => {
|
||||
app.conn.dec_volume(2);
|
||||
app.conn.update_volume();
|
||||
app.conn.update_status();
|
||||
}
|
||||
|
||||
// Delete highlighted song from the queue
|
||||
KeyCode::Char('d') => {
|
||||
if app.queue_list.index >= app.queue_list.list.len() - 1 {
|
||||
if app.queue_list.index != 0 {
|
||||
app.queue_list.index -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
app.conn.conn.delete(app.queue_list.index as u32)?;
|
||||
app.update_queue();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,6 +13,9 @@ pub mod tui;
|
|||
/// Content list
|
||||
pub mod list;
|
||||
|
||||
/// Song
|
||||
pub mod song;
|
||||
|
||||
/// File Browser
|
||||
pub mod browser;
|
||||
|
||||
|
|
|
|||
|
|
@ -18,17 +18,23 @@ impl<T> ContentList<T> {
|
|||
// 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
|
||||
pub fn prev(&mut self) {
|
||||
if self.index == 0 {
|
||||
self.index = self.list.len() - 1;
|
||||
let len = self.list.len();
|
||||
if len != 0 {
|
||||
self.index = len - 1;
|
||||
}
|
||||
} else {
|
||||
self.index -= 1;
|
||||
}
|
||||
|
|
|
|||
52
src/main.rs
52
src/main.rs
|
|
@ -9,6 +9,7 @@ use rmptui::connection::Connection;
|
|||
use rmptui::event::Event;
|
||||
use rmptui::event::EventHandler;
|
||||
use rmptui::handler;
|
||||
use rmptui::song::RSong;
|
||||
use rmptui::tui;
|
||||
use std::io;
|
||||
|
||||
|
|
@ -23,41 +24,52 @@ pub type Result<T> = core::result::Result<T, Error>;
|
|||
pub type Error = Box<dyn std::error::Error>;
|
||||
|
||||
fn main() -> AppResult<()> {
|
||||
// let args = Args::parse();
|
||||
let args = Args::parse();
|
||||
let mut app = App::builder("127.0.0.1:6600")?;
|
||||
|
||||
// if args.no_tui {
|
||||
// handle_tui()?;
|
||||
// } else {
|
||||
// match args.command {
|
||||
// Command::Volume { vol } => {
|
||||
// conn.set_volume(vol);
|
||||
// }
|
||||
// Command::Dmenu => conn.play_dmenu().unwrap(),
|
||||
// Command::Fzf => conn.play_fzf().unwrap(),
|
||||
// Command::Status => conn.status(),
|
||||
// Command::Pause => conn.pause(),
|
||||
// Command::Toggle => conn.toggle_pause(),
|
||||
// };
|
||||
// }
|
||||
if !args.tui {
|
||||
handle_tui(&mut app)?;
|
||||
} else {
|
||||
match args.command {
|
||||
Some(Command::Dmenu) => app.conn.play_dmenu()?,
|
||||
Some(Command::Fzf) => app.conn.play_fzf().unwrap(),
|
||||
Some(Command::Status) => app.conn.status(),
|
||||
Some(Command::Pause) => app.conn.pause(),
|
||||
Some(Command::Toggle) => app.conn.toggle_pause(),
|
||||
_ => {
|
||||
let mut vec: Vec<RSong> = Vec::new();
|
||||
for filename in app.conn.songs_filenames {
|
||||
let song = RSong::new(&mut app.conn.conn, filename);
|
||||
vec.push(song);
|
||||
}
|
||||
println!("{:#?}", vec);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn handle_tui(app: &mut App) -> AppResult<()> {
|
||||
let backend = CrosstermBackend::new(io::stderr());
|
||||
let terminal = Terminal::new(backend)?;
|
||||
let mut app = App::builder("127.0.0.1:6600")?;
|
||||
let events = EventHandler::new(250);
|
||||
let events = EventHandler::new(1000);
|
||||
|
||||
let mut tui = tui::Tui::new(terminal, events);
|
||||
tui.init()?;
|
||||
|
||||
// update the directory
|
||||
app.browser.update_directory(&mut app.conn).unwrap();
|
||||
|
||||
while app.running {
|
||||
tui.draw(&mut app)?;
|
||||
tui.draw(app)?;
|
||||
match tui.events.next()? {
|
||||
Event::Tick => app.tick(),
|
||||
Event::Key(key_event) => handler::handle_key_events(key_event, &mut app)?,
|
||||
Event::Key(key_event) => handler::handle_key_events(key_event, app)?,
|
||||
Event::Mouse(_) => {}
|
||||
Event::Resize(_, _) => {}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,67 @@
|
|||
use mpd::{Client, Song};
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Clone )]
|
||||
pub struct RSong {
|
||||
pub file: String,
|
||||
pub artist: Option<String>,
|
||||
pub title: Option<String>,
|
||||
pub duration: Option<u32>,
|
||||
pub last_mod: Option<String>,
|
||||
pub name: Option<String>,
|
||||
pub place: Option<String>,
|
||||
pub range: Option<String>,
|
||||
pub tags: Vec<(String, String)>,
|
||||
}
|
||||
|
||||
impl RSong {
|
||||
pub fn new(c: &mut Client, filename: String) -> Self {
|
||||
let mut s = RSong {
|
||||
file: filename.clone(),
|
||||
artist: None,
|
||||
title: None,
|
||||
duration: None,
|
||||
last_mod: None,
|
||||
name: None,
|
||||
place: None,
|
||||
range: None,
|
||||
tags: vec![],
|
||||
};
|
||||
|
||||
// Dummy song
|
||||
|
||||
let song = Song {
|
||||
file: filename.clone(),
|
||||
artist: None,
|
||||
title: None,
|
||||
duration: None,
|
||||
last_mod: None,
|
||||
name: None,
|
||||
place: None,
|
||||
range: None,
|
||||
tags: vec![("".to_string(), "".to_string())],
|
||||
};
|
||||
|
||||
for (k, v) in (c.readcomments(song).unwrap()).flatten() {
|
||||
if k.to_lowercase().contains("artist") {
|
||||
s.artist = Some(v);
|
||||
} else if k.to_lowercase().contains("title") {
|
||||
s.title = Some(v);
|
||||
} else if k.to_lowercase().contains("duration") {
|
||||
s.duration = Some(v.parse::<u32>().unwrap());
|
||||
} else if k.to_lowercase().contains("lastmod") {
|
||||
s.last_mod = Some(v);
|
||||
} else if k.to_lowercase().contains("name") {
|
||||
s.name = Some(v);
|
||||
} else if k.to_lowercase().contains("place") {
|
||||
s.place = Some(v);
|
||||
} else if k.to_lowercase().contains("range") {
|
||||
s.range = Some(v);
|
||||
} else {
|
||||
s.tags.push((k, v));
|
||||
}
|
||||
}
|
||||
|
||||
s
|
||||
}
|
||||
}
|
||||
85
src/ui.rs
85
src/ui.rs
|
|
@ -14,7 +14,7 @@ pub fn render(app: &mut App, frame: &mut Frame) {
|
|||
// Layout
|
||||
let layout = Layout::default()
|
||||
.direction(Direction::Vertical)
|
||||
.constraints(vec![Constraint::Percentage(93), Constraint::Percentage(7)])
|
||||
.constraints(vec![Constraint::Percentage(93), Constraint::Min(3)])
|
||||
.split(frame.size());
|
||||
|
||||
match app.selected_tab {
|
||||
|
|
@ -52,7 +52,12 @@ fn draw_directory_browser(frame: &mut Frame, app: &mut App, size: Rect) {
|
|||
)
|
||||
.borders(Borders::ALL),
|
||||
)
|
||||
.highlight_style(Style::new().add_modifier(Modifier::REVERSED))
|
||||
.highlight_style(
|
||||
Style::new()
|
||||
.fg(Color::Cyan)
|
||||
.bg(Color::Black)
|
||||
.add_modifier(Modifier::REVERSED),
|
||||
)
|
||||
.highlight_symbol(">>")
|
||||
.repeat_highlight_symbol(true)
|
||||
.scroll_padding(20);
|
||||
|
|
@ -72,7 +77,12 @@ fn draw_queue(frame: &mut Frame, app: &mut App, size: Rect) {
|
|||
);
|
||||
let list = List::new(app.queue_list.list.clone())
|
||||
.block(title.borders(Borders::ALL))
|
||||
.highlight_style(Style::new().add_modifier(Modifier::REVERSED))
|
||||
.highlight_style(
|
||||
Style::new()
|
||||
.fg(Color::Cyan)
|
||||
.bg(Color::Black)
|
||||
.add_modifier(Modifier::REVERSED),
|
||||
)
|
||||
.highlight_symbol(">>")
|
||||
.repeat_highlight_symbol(true);
|
||||
|
||||
|
|
@ -93,7 +103,12 @@ fn draw_playlists(frame: &mut Frame, app: &mut App, size: Rect) {
|
|||
|
||||
let list = List::new(app.pl_list.list.clone())
|
||||
.block(title.borders(Borders::ALL))
|
||||
.highlight_style(Style::new().add_modifier(Modifier::REVERSED))
|
||||
.highlight_style(
|
||||
Style::new()
|
||||
.fg(Color::Cyan)
|
||||
.bg(Color::Black)
|
||||
.add_modifier(Modifier::REVERSED),
|
||||
)
|
||||
.highlight_symbol(">>")
|
||||
.repeat_highlight_symbol(true);
|
||||
|
||||
|
|
@ -103,39 +118,65 @@ fn draw_playlists(frame: &mut Frame, app: &mut App, size: Rect) {
|
|||
|
||||
/// Draws Progress Bar
|
||||
fn draw_progress_bar(frame: &mut Frame, app: &mut App, size: Rect) {
|
||||
// Get the current playing song
|
||||
let song = app
|
||||
.conn
|
||||
.now_playing()
|
||||
.unwrap()
|
||||
.unwrap_or_else(|| "No Title Found".to_string());
|
||||
|
||||
let state = &app.conn.state;
|
||||
// let (elapsed, total) = app.conn.conn.status().unwrap().time.unwrap_or_default();
|
||||
// Get the current playing state
|
||||
let mut state: String = String::new();
|
||||
if !app.queue_list.list.is_empty() {
|
||||
state = app.conn.state.clone();
|
||||
state.push(':');
|
||||
}
|
||||
|
||||
let title = Block::default()
|
||||
.title(Title::from(format!("{}: ", state).red().bold()))
|
||||
.title(Title::from(song.green().bold()))
|
||||
.title(
|
||||
Title::from(
|
||||
// Get the current modes
|
||||
let mut modes_bottom: String = String::new();
|
||||
// we do this to check if at least one mode is enabled so we can push "[]"
|
||||
if app.conn.repeat | app.conn.random {
|
||||
modes_bottom.push('r');
|
||||
}
|
||||
|
||||
if !modes_bottom.is_empty() {
|
||||
modes_bottom.clear();
|
||||
modes_bottom.push('[');
|
||||
if app.conn.repeat {
|
||||
modes_bottom.push('r');
|
||||
}
|
||||
if app.conn.random {
|
||||
modes_bottom.push('z');
|
||||
}
|
||||
modes_bottom.push(']');
|
||||
};
|
||||
|
||||
|
||||
// get the duration
|
||||
let duration = if app.conn.total_duration.as_secs() != 0 {
|
||||
format!(
|
||||
"{}/{}",
|
||||
app.conn.elapsed.as_secs(),
|
||||
app.conn.total_duration.as_secs()
|
||||
)
|
||||
.cyan()
|
||||
.bold(),
|
||||
)
|
||||
.alignment(Alignment::Right),
|
||||
"[{}/{}]",
|
||||
humantime::format_duration(app.conn.elapsed),
|
||||
humantime::format_duration(app.conn.total_duration)
|
||||
)
|
||||
} else {
|
||||
"".to_string()
|
||||
};
|
||||
|
||||
// Define the title
|
||||
let title = Block::default()
|
||||
.title(Title::from(format!("{}", state).red().bold()))
|
||||
.title(Title::from(song.green().bold()))
|
||||
.title(Title::from(duration.cyan().bold()).alignment(Alignment::Right))
|
||||
.title(Title::from(format!("{}", modes_bottom)).position(block::Position::Bottom))
|
||||
.borders(Borders::ALL);
|
||||
|
||||
// .title(Title::from(app.conn.conn.status().unwrap_or_default().volume.to_string().yellow())).title_alignment(Alignment::Right);
|
||||
let progress_bar = LineGauge::default()
|
||||
.block(title.borders(Borders::ALL))
|
||||
.gauge_style(
|
||||
Style::default()
|
||||
.fg(Color::LightBlue)
|
||||
.bg(Color::Gray)
|
||||
.fg(Color::Blue)
|
||||
.bg(Color::Black)
|
||||
.add_modifier(Modifier::BOLD),
|
||||
)
|
||||
.line_set(symbols::line::THICK)
|
||||
|
|
|
|||
Reference in New Issue