add to playlist feature
This commit is contained in:
parent
8269766147
commit
59eed99c85
33
src/app.rs
33
src/app.rs
|
|
@ -22,6 +22,10 @@ pub struct App {
|
|||
pub inputmode: InputMode,
|
||||
pub search_input: String,
|
||||
pub cursor_position: usize,
|
||||
|
||||
// add to playlists
|
||||
pub playlist_popup: bool,
|
||||
pub append_list: ContentList<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
|
|
@ -38,6 +42,7 @@ impl App {
|
|||
let mut pl_list = ContentList::new();
|
||||
pl_list.list = Self::get_playlist(&mut conn.conn)?;
|
||||
|
||||
let append_list = Self::get_append_list(&mut conn.conn)?;
|
||||
Self::get_queue(&mut conn, &mut queue_list.list);
|
||||
|
||||
let browser = FileBrowser::new();
|
||||
|
|
@ -51,6 +56,8 @@ impl App {
|
|||
inputmode: InputMode::Normal,
|
||||
search_input: String::new(),
|
||||
cursor_position: 0,
|
||||
playlist_popup: false,
|
||||
append_list,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -91,6 +98,16 @@ impl App {
|
|||
Ok(list)
|
||||
}
|
||||
|
||||
pub fn get_append_list(conn: &mut Client) -> AppResult<ContentList<String>> {
|
||||
let mut list = ContentList::new();
|
||||
list.list.push("Current Playlist".to_string());
|
||||
for item in Self::get_playlist(conn)? {
|
||||
list.list.push(item.to_string());
|
||||
}
|
||||
|
||||
Ok(list)
|
||||
}
|
||||
|
||||
pub fn update_playlist(&mut self) -> AppResult<()> {
|
||||
Self::get_playlist(&mut self.conn.conn)?;
|
||||
Ok(())
|
||||
|
|
@ -105,21 +122,9 @@ impl App {
|
|||
}
|
||||
|
||||
pub fn search_song(&mut self) -> AppResult<()> {
|
||||
let list = self
|
||||
.conn
|
||||
.songs_filenames
|
||||
.iter()
|
||||
.map(|f| f.as_str())
|
||||
.collect::<Vec<&str>>();
|
||||
let (filename, _) =
|
||||
rust_fuzzy_search::fuzzy_search_sorted(self.search_input.as_str(), &list)
|
||||
.get(0)
|
||||
.unwrap()
|
||||
.clone();
|
||||
|
||||
let song = self.conn.get_song_with_only_filename(filename);
|
||||
let filename = self.conn.get_full_path(&self.search_input)?;
|
||||
let song = self.conn.get_song_with_only_filename(&filename);
|
||||
self.conn.push(&song)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,8 @@ use simple_dmenu::dmenu;
|
|||
use std::process::Command;
|
||||
use std::time::Duration;
|
||||
|
||||
use crate::app::AppResult;
|
||||
|
||||
pub type Result<T> = core::result::Result<T, Error>;
|
||||
pub type Error = Box<dyn std::error::Error>;
|
||||
|
||||
|
|
@ -143,6 +145,12 @@ impl Connection {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// Add given song to playlist
|
||||
pub fn add_to_playlist(&mut self, playlist: &str, song: &Song) -> Result<()> {
|
||||
self.conn.pl_push(playlist, song)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Given a filename, get instance of Song with only filename
|
||||
pub fn get_song_with_only_filename(&self, filename: &str) -> Song {
|
||||
Song {
|
||||
|
|
@ -158,6 +166,21 @@ impl Connection {
|
|||
}
|
||||
}
|
||||
|
||||
/// Given a song name from a directory, it returns the full path of the song in the database
|
||||
pub fn get_full_path(&self, short_path: &str) -> AppResult<String> {
|
||||
let list = self
|
||||
.songs_filenames
|
||||
.iter()
|
||||
.map(|f| f.as_str())
|
||||
.collect::<Vec<&str>>();
|
||||
let (filename, _) = rust_fuzzy_search::fuzzy_search_sorted(&short_path, &list)
|
||||
.get(0)
|
||||
.unwrap()
|
||||
.clone();
|
||||
|
||||
Ok(filename.to_string())
|
||||
}
|
||||
|
||||
/// Print status to stdout
|
||||
pub fn status(&mut self) {
|
||||
let current_song = self.conn.currentsong();
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ use crate::{
|
|||
};
|
||||
use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
|
||||
use rust_fuzzy_search::{self, fuzzy_search_sorted};
|
||||
use simple_dmenu::dmenu;
|
||||
|
||||
pub fn handle_key_events(key_event: KeyEvent, app: &mut App) -> AppResult<()> {
|
||||
if app.inputmode == InputMode::Editing {
|
||||
|
|
@ -68,6 +67,52 @@ pub fn handle_key_events(key_event: KeyEvent, app: &mut App) -> AppResult<()> {
|
|||
app.move_cursor_right();
|
||||
}
|
||||
|
||||
_ => {}
|
||||
}
|
||||
} else if app.playlist_popup {
|
||||
match key_event.code {
|
||||
KeyCode::Char('q') | KeyCode::Esc => {
|
||||
app.playlist_popup = false;
|
||||
}
|
||||
|
||||
KeyCode::Char('j') | KeyCode::Down => app.append_list.next(),
|
||||
KeyCode::Char('k') | KeyCode::Up => app.append_list.prev(),
|
||||
|
||||
KeyCode::Enter => {
|
||||
let pl_index = app.append_list.index;
|
||||
let pl_name = app.append_list.list.get(pl_index).unwrap();
|
||||
|
||||
let s_index: usize;
|
||||
let mut short_path: String = String::new();
|
||||
match app.selected_tab {
|
||||
SelectedTab::Queue => {
|
||||
s_index = app.queue_list.index;
|
||||
short_path = app.queue_list.list.get(s_index).unwrap().to_string();
|
||||
}
|
||||
|
||||
SelectedTab::DirectoryBrowser => {
|
||||
let (t, f) = app.browser.filetree.get(app.browser.selected).unwrap();
|
||||
if t == "file" {
|
||||
short_path = f.to_string();
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
let full_path = app.conn.get_full_path(&short_path)?;
|
||||
let song = app.conn.get_song_with_only_filename(&full_path);
|
||||
|
||||
if pl_name == "Current Playlist" {
|
||||
app.conn.conn.push(&song)?;
|
||||
app.update_queue();
|
||||
} else {
|
||||
app.conn.add_to_playlist(pl_name, &song)?;
|
||||
}
|
||||
|
||||
// hide the playlist popup
|
||||
app.playlist_popup = false;
|
||||
app.append_list.index = 0;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
} else {
|
||||
|
|
@ -160,32 +205,34 @@ pub fn handle_key_events(key_event: KeyEvent, app: &mut App) -> AppResult<()> {
|
|||
|
||||
// add to queue
|
||||
KeyCode::Char('a') => {
|
||||
let list = app
|
||||
.conn
|
||||
.songs_filenames
|
||||
.iter()
|
||||
.map(|f| f.as_str())
|
||||
.collect::<Vec<&str>>();
|
||||
// let list = app
|
||||
// .conn
|
||||
// .songs_filenames
|
||||
// .iter()
|
||||
// .map(|f| f.as_str())
|
||||
// .collect::<Vec<&str>>();
|
||||
//
|
||||
// let files: Vec<String> = app
|
||||
// .browser
|
||||
// .filetree
|
||||
// .clone()
|
||||
// .into_iter()
|
||||
// .map(|(_, f)| f)
|
||||
// .collect();
|
||||
//
|
||||
// let (filename, _) = rust_fuzzy_search::fuzzy_search_sorted(
|
||||
// &files.get(app.browser.selected).unwrap(),
|
||||
// &list,
|
||||
// )
|
||||
// .get(0)
|
||||
// .unwrap()
|
||||
// .clone();
|
||||
//
|
||||
// let song = app.conn.get_song_with_only_filename(filename);
|
||||
//
|
||||
// app.conn.conn.push(&song)?;
|
||||
|
||||
let files: Vec<String> = app
|
||||
.browser
|
||||
.filetree
|
||||
.clone()
|
||||
.into_iter()
|
||||
.map(|(_, f)| f)
|
||||
.collect();
|
||||
|
||||
let (filename, _) = rust_fuzzy_search::fuzzy_search_sorted(
|
||||
&files.get(app.browser.selected).unwrap(),
|
||||
&list,
|
||||
)
|
||||
.get(0)
|
||||
.unwrap()
|
||||
.clone();
|
||||
|
||||
let song = app.conn.get_song_with_only_filename(filename);
|
||||
|
||||
app.conn.conn.push(&song)?;
|
||||
app.playlist_popup = true;
|
||||
}
|
||||
|
||||
KeyCode::Right => {
|
||||
|
|
|
|||
48
src/ui.rs
48
src/ui.rs
|
|
@ -21,11 +21,6 @@ impl InputMode {
|
|||
|
||||
/// Renders the user interface widgets
|
||||
pub fn render(app: &mut App, frame: &mut Frame) {
|
||||
// This is where you add new widgets.
|
||||
// See the following resources:
|
||||
// - https://docs.rs/ratatui/latest/ratatui/widgets/index.html
|
||||
// - https://github.com/ratatui-org/ratatui/tree/master/examples
|
||||
|
||||
// Layout
|
||||
let layout = Layout::default()
|
||||
.direction(Direction::Vertical)
|
||||
|
|
@ -46,6 +41,10 @@ pub fn render(app: &mut App, frame: &mut Frame) {
|
|||
draw_search_bar(frame, app, layout[1]);
|
||||
}
|
||||
}
|
||||
|
||||
if app.playlist_popup {
|
||||
draw_add_to_playlist(frame, app, layout[0]);
|
||||
}
|
||||
}
|
||||
|
||||
/// Draws the file tree browser
|
||||
|
|
@ -238,3 +237,42 @@ fn draw_progress_bar(frame: &mut Frame, app: &mut App, size: Rect) {
|
|||
|
||||
frame.render_widget(progress_bar, size);
|
||||
}
|
||||
|
||||
fn draw_add_to_playlist(frame: &mut Frame, app: &mut App, area: Rect) {
|
||||
let area = centered_rect(40, 50, area);
|
||||
let mut state = ListState::default();
|
||||
let title = Block::default()
|
||||
.title(Title::from("Add Selected Item to: "))
|
||||
.title(Title::from("<Esc> to Cancel".green().bold()).alignment(Alignment::Right));
|
||||
let list = List::new(app.append_list.list.clone())
|
||||
.block(title.borders(Borders::ALL))
|
||||
.highlight_style(
|
||||
Style::new()
|
||||
.fg(Color::Cyan)
|
||||
.bg(Color::Black)
|
||||
.add_modifier(Modifier::BOLD)
|
||||
.add_modifier(Modifier::REVERSED),
|
||||
)
|
||||
.highlight_symbol(">>")
|
||||
.repeat_highlight_symbol(true);
|
||||
|
||||
state.select(Some(app.append_list.index));
|
||||
frame.render_widget(Clear, area); //this clears out the background
|
||||
frame.render_stateful_widget(list, area, &mut state);
|
||||
}
|
||||
|
||||
fn centered_rect(percent_x: u16, percent_y: u16, r: Rect) -> Rect {
|
||||
let popup_layout = Layout::vertical([
|
||||
Constraint::Percentage((100 - percent_y) / 2),
|
||||
Constraint::Percentage(percent_y),
|
||||
Constraint::Percentage((100 - percent_y) / 2),
|
||||
])
|
||||
.split(r);
|
||||
|
||||
Layout::horizontal([
|
||||
Constraint::Percentage((100 - percent_x) / 2),
|
||||
Constraint::Percentage(percent_x),
|
||||
Constraint::Percentage((100 - percent_x) / 2),
|
||||
])
|
||||
.split(popup_layout[1])[1]
|
||||
}
|
||||
|
|
|
|||
Reference in New Issue