Krolyxon
Commit 59eed99

rmptui59eed99

add to playlist feature

Author
krolxon
Date
2024-04-26 23:10 +0530
Parent
8269766
Commit
59eed99c85957c193dba53e7311f960a6b8e916c

+159 −46

4 files changed

  1. M src/app.rs +19 −14
  2. M src/connection.rs +23 −0
  3. M src/handler.rs +74 −27
  4. M src/ui.rs +43 −5
M src/app.rs View
@@ -22,6 +22,10 @@ pub struct App {
2222 pub inputmode: InputMode,
2323 pub search_input: String,
2424 pub cursor_position: usize,
25+
26+ // add to playlists
27+ pub playlist_popup: bool,
28+ pub append_list: ContentList<String>,
2529 }
2630
2731 #[derive(Debug, PartialEq, Clone)]
@@ -38,6 +42,7 @@ impl App {
3842 let mut pl_list = ContentList::new();
3943 pl_list.list = Self::get_playlist(&mut conn.conn)?;
4044
45+ let append_list = Self::get_append_list(&mut conn.conn)?;
4146 Self::get_queue(&mut conn, &mut queue_list.list);
4247
4348 let browser = FileBrowser::new();
@@ -51,6 +56,8 @@ impl App {
5156 inputmode: InputMode::Normal,
5257 search_input: String::new(),
5358 cursor_position: 0,
59+ playlist_popup: false,
60+ append_list,
5461 })
5562 }
5663
@@ -91,6 +98,16 @@ impl App {
9198 Ok(list)
9299 }
93100
101+ pub fn get_append_list(conn: &mut Client) -> AppResult<ContentList<String>> {
102+ let mut list = ContentList::new();
103+ list.list.push("Current Playlist".to_string());
104+ for item in Self::get_playlist(conn)? {
105+ list.list.push(item.to_string());
106+ }
107+
108+ Ok(list)
109+ }
110+
94111 pub fn update_playlist(&mut self) -> AppResult<()> {
95112 Self::get_playlist(&mut self.conn.conn)?;
96113 Ok(())
@@ -105,21 +122,9 @@ impl App {
105122 }
106123
107124 pub fn search_song(&mut self) -> AppResult<()> {
108− let list = self
109− .conn
110− .songs_filenames
111− .iter()
112− .map(|f| f.as_str())
113− .collect::<Vec<&str>>();
114− let (filename, _) =
115− rust_fuzzy_search::fuzzy_search_sorted(self.search_input.as_str(), &list)
116− .get(0)
117− .unwrap()
118− .clone();
119−
120− let song = self.conn.get_song_with_only_filename(filename);
125+ let filename = self.conn.get_full_path(&self.search_input)?;
126+ let song = self.conn.get_song_with_only_filename(&filename);
121127 self.conn.push(&song)?;
122−
123128 Ok(())
124129 }
125130
M src/connection.rs View
@@ -4,6 +4,8 @@ use simple_dmenu::dmenu;
44 use std::process::Command;
55 use std::time::Duration;
66
7+use crate::app::AppResult;
8+
79 pub type Result<T> = core::result::Result<T, Error>;
810 pub type Error = Box<dyn std::error::Error>;
911
@@ -143,6 +145,12 @@ impl Connection {
143145 Ok(())
144146 }
145147
148+ /// Add given song to playlist
149+ pub fn add_to_playlist(&mut self, playlist: &str, song: &Song) -> Result<()> {
150+ self.conn.pl_push(playlist, song)?;
151+ Ok(())
152+ }
153+
146154 /// Given a filename, get instance of Song with only filename
147155 pub fn get_song_with_only_filename(&self, filename: &str) -> Song {
148156 Song {
@@ -158,6 +166,21 @@ impl Connection {
158166 }
159167 }
160168
169+ /// Given a song name from a directory, it returns the full path of the song in the database
170+ pub fn get_full_path(&self, short_path: &str) -> AppResult<String> {
171+ let list = self
172+ .songs_filenames
173+ .iter()
174+ .map(|f| f.as_str())
175+ .collect::<Vec<&str>>();
176+ let (filename, _) = rust_fuzzy_search::fuzzy_search_sorted(&short_path, &list)
177+ .get(0)
178+ .unwrap()
179+ .clone();
180+
181+ Ok(filename.to_string())
182+ }
183+
161184 /// Print status to stdout
162185 pub fn status(&mut self) {
163186 let current_song = self.conn.currentsong();
M src/handler.rs View
@@ -6,7 +6,6 @@ use crate::{
66 };
77 use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
88 use rust_fuzzy_search::{self, fuzzy_search_sorted};
9−use simple_dmenu::dmenu;
109
1110 pub fn handle_key_events(key_event: KeyEvent, app: &mut App) -> AppResult<()> {
1211 if app.inputmode == InputMode::Editing {
@@ -68,6 +67,52 @@ pub fn handle_key_events(key_event: KeyEvent, app: &mut App) -> AppResult<()> {
6867 app.move_cursor_right();
6968 }
7069
70+ _ => {}
71+ }
72+ } else if app.playlist_popup {
73+ match key_event.code {
74+ KeyCode::Char('q') | KeyCode::Esc => {
75+ app.playlist_popup = false;
76+ }
77+
78+ KeyCode::Char('j') | KeyCode::Down => app.append_list.next(),
79+ KeyCode::Char('k') | KeyCode::Up => app.append_list.prev(),
80+
81+ KeyCode::Enter => {
82+ let pl_index = app.append_list.index;
83+ let pl_name = app.append_list.list.get(pl_index).unwrap();
84+
85+ let s_index: usize;
86+ let mut short_path: String = String::new();
87+ match app.selected_tab {
88+ SelectedTab::Queue => {
89+ s_index = app.queue_list.index;
90+ short_path = app.queue_list.list.get(s_index).unwrap().to_string();
91+ }
92+
93+ SelectedTab::DirectoryBrowser => {
94+ let (t, f) = app.browser.filetree.get(app.browser.selected).unwrap();
95+ if t == "file" {
96+ short_path = f.to_string();
97+ }
98+ }
99+ _ => {}
100+ }
101+
102+ let full_path = app.conn.get_full_path(&short_path)?;
103+ let song = app.conn.get_song_with_only_filename(&full_path);
104+
105+ if pl_name == "Current Playlist" {
106+ app.conn.conn.push(&song)?;
107+ app.update_queue();
108+ } else {
109+ app.conn.add_to_playlist(pl_name, &song)?;
110+ }
111+
112+ // hide the playlist popup
113+ app.playlist_popup = false;
114+ app.append_list.index = 0;
115+ }
71116 _ => {}
72117 }
73118 } else {
@@ -160,32 +205,34 @@ pub fn handle_key_events(key_event: KeyEvent, app: &mut App) -> AppResult<()> {
160205
161206 // add to queue
162207 KeyCode::Char('a') => {
163− let list = app
164− .conn
165− .songs_filenames
166− .iter()
167− .map(|f| f.as_str())
168− .collect::<Vec<&str>>();
169−
170− let files: Vec<String> = app
171− .browser
172− .filetree
173− .clone()
174− .into_iter()
175− .map(|(_, f)| f)
176− .collect();
177−
178− let (filename, _) = rust_fuzzy_search::fuzzy_search_sorted(
179− &files.get(app.browser.selected).unwrap(),
180− &list,
181− )
182− .get(0)
183− .unwrap()
184− .clone();
185−
186− let song = app.conn.get_song_with_only_filename(filename);
187−
188− app.conn.conn.push(&song)?;
208+ // let list = app
209+ // .conn
210+ // .songs_filenames
211+ // .iter()
212+ // .map(|f| f.as_str())
213+ // .collect::<Vec<&str>>();
214+ //
215+ // let files: Vec<String> = app
216+ // .browser
217+ // .filetree
218+ // .clone()
219+ // .into_iter()
220+ // .map(|(_, f)| f)
221+ // .collect();
222+ //
223+ // let (filename, _) = rust_fuzzy_search::fuzzy_search_sorted(
224+ // &files.get(app.browser.selected).unwrap(),
225+ // &list,
226+ // )
227+ // .get(0)
228+ // .unwrap()
229+ // .clone();
230+ //
231+ // let song = app.conn.get_song_with_only_filename(filename);
232+ //
233+ // app.conn.conn.push(&song)?;
234+
235+ app.playlist_popup = true;
189236 }
190237
191238 KeyCode::Right => {
M src/ui.rs View
@@ -21,11 +21,6 @@ impl InputMode {
2121
2222 /// Renders the user interface widgets
2323 pub fn render(app: &mut App, frame: &mut Frame) {
24− // This is where you add new widgets.
25− // See the following resources:
26− // - https://docs.rs/ratatui/latest/ratatui/widgets/index.html
27− // - https://github.com/ratatui-org/ratatui/tree/master/examples
28−
2924 // Layout
3025 let layout = Layout::default()
3126 .direction(Direction::Vertical)
@@ -46,6 +41,10 @@ pub fn render(app: &mut App, frame: &mut Frame) {
4641 draw_search_bar(frame, app, layout[1]);
4742 }
4843 }
44+
45+ if app.playlist_popup {
46+ draw_add_to_playlist(frame, app, layout[0]);
47+ }
4948 }
5049
5150 /// Draws the file tree browser
@@ -238,3 +237,42 @@ fn draw_progress_bar(frame: &mut Frame, app: &mut App, size: Rect) {
238237
239238 frame.render_widget(progress_bar, size);
240239 }
240+
241+fn draw_add_to_playlist(frame: &mut Frame, app: &mut App, area: Rect) {
242+ let area = centered_rect(40, 50, area);
243+ let mut state = ListState::default();
244+ let title = Block::default()
245+ .title(Title::from("Add Selected Item to: "))
246+ .title(Title::from("<Esc> to Cancel".green().bold()).alignment(Alignment::Right));
247+ let list = List::new(app.append_list.list.clone())
248+ .block(title.borders(Borders::ALL))
249+ .highlight_style(
250+ Style::new()
251+ .fg(Color::Cyan)
252+ .bg(Color::Black)
253+ .add_modifier(Modifier::BOLD)
254+ .add_modifier(Modifier::REVERSED),
255+ )
256+ .highlight_symbol(">>")
257+ .repeat_highlight_symbol(true);
258+
259+ state.select(Some(app.append_list.index));
260+ frame.render_widget(Clear, area); //this clears out the background
261+ frame.render_stateful_widget(list, area, &mut state);
262+}
263+
264+fn centered_rect(percent_x: u16, percent_y: u16, r: Rect) -> Rect {
265+ let popup_layout = Layout::vertical([
266+ Constraint::Percentage((100 - percent_y) / 2),
267+ Constraint::Percentage(percent_y),
268+ Constraint::Percentage((100 - percent_y) / 2),
269+ ])
270+ .split(r);
271+
272+ Layout::horizontal([
273+ Constraint::Percentage((100 - percent_x) / 2),
274+ Constraint::Percentage(percent_x),
275+ Constraint::Percentage((100 - percent_x) / 2),
276+ ])
277+ .split(popup_layout[1])[1]
278+}