diff --git a/src/app.rs b/src/app.rs index d57c0b6..cb7b520 100755 --- a/src/app.rs +++ b/src/app.rs @@ -1,5 +1,6 @@ -use crate::connection::Connection; +use crate::connection::{self, Connection}; use crate::list::ContentList; +use mpd::Client; use std::collections::VecDeque; // Application result type @@ -12,20 +13,30 @@ pub struct App { pub running: bool, pub conn: Connection, pub play_deque: VecDeque, - pub list: ContentList, + pub song_list: ContentList, + pub queue_list: ContentList, + pub pl_list: ContentList, } impl App { - pub fn new(addrs: &str) -> Self { + pub fn builder(addrs: &str) -> AppResult { let mut conn = Connection::new(addrs).unwrap(); let mut vec: VecDeque = VecDeque::new(); + let mut pl_list = ContentList::new(); + pl_list.list = Self::get_playlist(&mut conn.conn)?; Self::get_queue(&mut conn, &mut vec); - Self { + + let mut song_list = ContentList::new(); + song_list.list = conn.songs_filenames.clone(); + + Ok(Self { running: true, conn, play_deque: vec, - list: ContentList::new(), - } + song_list, + queue_list: ContentList::new(), + pl_list, + }) } pub fn tick(&self) {} @@ -52,10 +63,15 @@ impl App { self.play_deque.clear(); Self::get_queue(&mut self.conn, &mut self.play_deque); } -} -fn to_vecdeque(filenames: &Vec) -> VecDeque { - let mut v: VecDeque = VecDeque::new(); - v = filenames.iter().map(|x| x.to_string()).collect(); - v + pub fn get_playlist(conn: &mut Client) -> AppResult> { + let list: Vec = conn.playlists()?.iter().map(|p| p.clone().name).collect(); + + Ok(list) + } + + pub fn update_playlist(&mut self) -> AppResult<()> { + Self::get_playlist(&mut self.conn.conn)?; + Ok(()) + } } diff --git a/src/connection.rs b/src/connection.rs index f2499e6..4f98beb 100755 --- a/src/connection.rs +++ b/src/connection.rs @@ -10,6 +10,7 @@ pub type Error = Box; pub struct Connection { pub conn: Client, pub songs_filenames: Vec, + // pub state: String, } impl Connection { @@ -25,6 +26,7 @@ impl Connection { Ok(Self { conn, songs_filenames, + // state: "Stopped".to_string(), }) } @@ -50,14 +52,22 @@ impl Connection { Ok(()) } + // pub fn update_state(&mut self) { + // match self.conn.status().unwrap().state { + // State::Stop => self.state = "Stopped".to_string(), + // State::Play => self.state = "Playing".to_string(), + // State::Pause => self.state = "Paused".to_string(), + // } + // } + pub fn push(&mut self, song: &Song) -> Result<()> { if self.conn.queue().unwrap().is_empty() { self.conn.push(song).unwrap(); self.conn.play().unwrap(); } else { - self.conn.push(song).unwrap(); - if self.conn.status().unwrap().state == State::Stop { - self.conn.play().unwrap(); + self.conn.push(song)?; + if self.conn.status()?.state == State::Stop { + self.conn.play()?; } self.conn.next().unwrap(); } @@ -65,6 +75,19 @@ impl Connection { Ok(()) } + pub fn push_playlist(&mut self, playlist: &str) -> Result<()> { + let songs: Vec = self.conn.playlist(playlist)?; + + for song in songs { + if self.songs_filenames.contains(&song.file) { + let song = self.get_song_with_only_filename(&song.file); + self.conn.push(&song)?; + self.conn.play()?; + } + } + Ok(()) + } + pub fn get_song_with_only_filename(&self, filename: &str) -> Song { Song { file: filename.to_string(), @@ -81,7 +104,6 @@ impl Connection { pub fn get_current_song(&mut self) -> Option { self.conn.currentsong().unwrap().unwrap_or_default().title - } pub fn status(&mut self) { let current_song = self.conn.currentsong(); @@ -99,6 +121,19 @@ impl Connection { ); } + pub fn now_playing(&mut self) -> Option { + let song = self.conn.currentsong().unwrap().unwrap_or_default(); + if let Some(s) = song.title { + if let Some(a) = song.artist { + Some(format!("{} - {}", s, a)) + } else { + Some(s) + } + } else { + None + } + } + // Playback controls pub fn pause(&mut self) { self.conn.pause(true).unwrap(); diff --git a/src/handler.rs b/src/handler.rs index 32b4b6f..91b5f86 100755 --- a/src/handler.rs +++ b/src/handler.rs @@ -11,17 +11,17 @@ pub fn handle_key_events(key_event: KeyEvent, app: &mut App) -> AppResult<()> { } KeyCode::Char('j') => { - app.list.next(); + app.song_list.next(); } KeyCode::Char('k') => { - app.list.prev(); + app.song_list.prev(); } KeyCode::Enter | KeyCode::Char('l') => { - let song = app.conn.get_song_with_only_filename(app.conn.songs_filenames.get(app.list.index).unwrap()); - app.conn.push(&song).unwrap(); - app.update_queue(); + let song = app.conn.get_song_with_only_filename(app.conn.songs_filenames.get(app.song_list.index).unwrap()); + app.conn.push(&song)?; + // app.update_queue(); } // Playback controls @@ -35,10 +35,42 @@ pub fn handle_key_events(key_event: KeyEvent, app: &mut App) -> AppResult<()> { app.conn.pause(); } + // Clearn Queue KeyCode::Char('x') => { app.conn.conn.clear()?; - app.update_queue(); + // app.update_queue(); + } + + KeyCode::Char('d') => { + app.conn.play_dmenu()?; + } + + KeyCode::Down=> { + app.pl_list.next(); + } + + KeyCode::Up=> { + app.pl_list.prev(); + } + + + KeyCode::Right => { + app.conn.push_playlist(app.pl_list.list.get(app.pl_list.index).unwrap())?; + } + + KeyCode::Char('f')=> { + // let place = app.conn.conn.status().unwrap().duration; + let (pos, _) = app.conn.conn.status().unwrap().time.unwrap(); + let pos: i64 = (pos.as_secs() + 2).try_into().unwrap(); + app.conn.conn.seek(2, pos )?; + } + + KeyCode::Char('b')=> { + // let place = app.conn.conn.status().unwrap().duration; + let (pos, _) = app.conn.conn.status().unwrap().time.unwrap(); + let pos: i64 = (pos.as_secs() - 2).try_into().unwrap(); + app.conn.conn.seek(2, pos )?; } _ => {} } diff --git a/src/list.rs b/src/list.rs index 669d1af..74790a1 100755 --- a/src/list.rs +++ b/src/list.rs @@ -1,11 +1,13 @@ #[derive(Debug)] -pub struct ContentList { +pub struct ContentList { + pub list: Vec, pub index: usize } -impl ContentList { +impl ContentList { pub fn new() -> Self { ContentList { + list: Vec::new(), index: 0 } } diff --git a/src/main.rs b/src/main.rs index 3fbfc14..4d849f1 100755 --- a/src/main.rs +++ b/src/main.rs @@ -42,7 +42,7 @@ fn main() -> AppResult<()> { let backend = CrosstermBackend::new(io::stderr()); let terminal = Terminal::new(backend)?; - let mut app = App::new("127.0.0.1:6600"); + let mut app = App::builder("127.0.0.1:6600")?; let events = EventHandler::new(250); let mut tui = tui::Tui::new(terminal, events); diff --git a/src/ui.rs b/src/ui.rs index 3f2eec8..b605747 100755 --- a/src/ui.rs +++ b/src/ui.rs @@ -1,4 +1,4 @@ -use crate::{app::App, connection::Connection}; +use crate::app::App; use ratatui::{prelude::*, widgets::*}; /// Renders the user interface widgets @@ -9,7 +9,7 @@ pub fn render(app: &mut App, frame: &mut Frame) { // - https://github.com/ratatui-org/ratatui/tree/master/examples // List of songs - let mut state = ListState::default(); + let mut song_state = ListState::default(); let size = Rect::new(100, 0, frame.size().width, frame.size().height - 3); let list = List::new(app.conn.songs_filenames.clone()) .block(Block::default().title("Song List").borders(Borders::ALL)) @@ -17,21 +17,49 @@ pub fn render(app: &mut App, frame: &mut Frame) { .highlight_symbol(">>") .repeat_highlight_symbol(true); - state.select(Some(app.list.index)); - frame.render_stateful_widget(list, size, &mut state); + song_state.select(Some(app.song_list.index)); + frame.render_stateful_widget(list, size, &mut song_state); // Play Queue + let mut queue_state = ListState::default(); let size = Rect::new(0, 0, 100, frame.size().height - 25); let list = List::new(app.play_deque.clone()) .block(Block::default().title("Play Queue").borders(Borders::ALL)) .highlight_style(Style::new().add_modifier(Modifier::REVERSED)) .highlight_symbol(">>") .repeat_highlight_symbol(true); - frame.render_widget(list, size); + + app.update_queue(); + frame.render_stateful_widget(list, size, &mut queue_state); // Status - let size = Rect::new(0, frame.size().height - 3, frame.size().width, 3); - let status = Paragraph::new(app.conn.conn.status().unwrap().volume.to_string()) - .block(Block::default().title("Status").borders(Borders::ALL)); - frame.render_widget(status, size); + // let size = Rect::new(0, frame.size().height - 3, frame.size().width, 3); + // let song = app + // .conn + // .now_playing() + // .unwrap_or_else(|| "No Title Found".to_string()); + // + // let (elapsed, total) = app.conn.conn.status().unwrap().time.unwrap(); + // + // let mut lines = vec![]; + // lines.push(Line::from(vec![ + // Span::styled("Current: ", Style::default().fg(Color::Red)), + // Span::styled(song, Style::default().fg(Color::Yellow)), + // Span::styled(format!("[{}/{}]", elapsed.as_secs(), total.as_secs()), Style::default().fg(Color::Yellow)), + // ])); + // let status = Paragraph::new(Text::from(lines)) + // .block(Block::default().title("Status").borders(Borders::ALL)); + // frame.render_widget(status, size); + + // Playlists + let mut state = ListState::default(); + let size = Rect::new(0, 25, 100, frame.size().height - 25 - 3); + let list = List::new(app.pl_list.list.clone()) + .block(Block::default().title("Playlists").borders(Borders::ALL)) + .highlight_style(Style::new().add_modifier(Modifier::REVERSED)) + .highlight_symbol(">>") + .repeat_highlight_symbol(true); + + state.select(Some(app.pl_list.index)); + frame.render_stateful_widget(list, size, &mut state); }