aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rwxr-xr-xsrc/app.rs2
-rwxr-xr-xsrc/browser.rs58
-rwxr-xr-xsrc/connection.rs29
-rwxr-xr-xsrc/handler.rs2
-rwxr-xr-xsrc/main.rs12
-rwxr-xr-xsrc/ui.rs118
6 files changed, 179 insertions, 42 deletions
diff --git a/src/app.rs b/src/app.rs
index f67ca4e..c2ce06c 100755
--- a/src/app.rs
+++ b/src/app.rs
@@ -170,7 +170,7 @@ impl App {
self.selected_tab = match self.selected_tab {
SelectedTab::Queue => SelectedTab::DirectoryBrowser,
SelectedTab::DirectoryBrowser => SelectedTab::Playlists,
- SelectedTab::Playlists => SelectedTab::Queue,
+ SelectedTab::Playlists => SelectedTab::DirectoryBrowser,
};
}
diff --git a/src/browser.rs b/src/browser.rs
index 6af4f59..73f9cdd 100755
--- a/src/browser.rs
+++ b/src/browser.rs
@@ -1,3 +1,8 @@
+use std::ffi::OsStr;
+use std::path::Path;
+
+use mpd::Song;
+
use crate::{app::AppResult, connection::Connection};
#[derive(Debug)]
@@ -7,6 +12,24 @@ pub struct FileBrowser {
pub prev_selected: usize,
pub path: String,
pub prev_path: String,
+ pub songs: Vec<Song>,
+}
+
+// https://stackoverflow.com/questions/72392835/check-if-a-file-is-of-a-given-type
+pub trait FileExtension {
+ fn has_extension<S: AsRef<str>>(&self, extensions: &[S]) -> bool;
+}
+
+impl<P: AsRef<Path>> FileExtension for P {
+ fn has_extension<S: AsRef<str>>(&self, extensions: &[S]) -> bool {
+ if let Some(ref extension) = self.as_ref().extension().and_then(OsStr::to_str) {
+ return extensions
+ .iter()
+ .any(|x| x.as_ref().eq_ignore_ascii_case(extension));
+ }
+
+ false
+ }
}
impl FileBrowser {
@@ -17,6 +40,7 @@ impl FileBrowser {
prev_selected: 0,
path: ".".to_string(),
prev_path: ".".to_string(),
+ songs: vec![],
}
}
@@ -26,9 +50,41 @@ impl FileBrowser {
.conn
.listfiles(self.path.as_str())?
.into_iter()
- .filter(|(f, _)| f == "directory" || f == "file")
+ .filter(|(f, l)| {
+ f == "directory"
+ || f == "file" && Path::new(l).has_extension(&["mp3", "ogg", "flac", "m4a", "wav", "aac" ,"opus", "ape", "wma", "mpc", "aiff", "dff", "mp2", "mka"])
+ })
.collect::<Vec<(String, String)>>();
+ self.songs.clear();
+ for (t, song) in self.filetree.iter() {
+ if t == "file" {
+ let v = conn
+ .conn
+ .lsinfo(Song {
+ file: conn
+ .get_full_path(song)
+ .unwrap_or_else(|| "Not a song".to_string()),
+ ..Default::default()
+ })
+ .unwrap_or_else(|_| {
+ vec![Song {
+ file: "Not a song".to_string(),
+ ..Default::default()
+ }]
+ });
+
+ self.songs.push(v.get(0).unwrap().clone());
+ } else {
+ let v = Song {
+ file: "".to_string(),
+ ..Default::default()
+ };
+
+ self.songs.push(v);
+ }
+ }
+
Ok(())
}
diff --git a/src/connection.rs b/src/connection.rs
index 7327bb9..5cba89d 100755
--- a/src/connection.rs
+++ b/src/connection.rs
@@ -27,14 +27,7 @@ impl Connection {
let empty_song = Song {
file: "No Song playing or in Queue".to_string(),
- artist: None,
- title: None,
- duration: None,
- last_mod: None,
- name: None,
- place: None,
- range: None,
- tags: vec![("".to_string(), "".to_string())],
+ ..Default::default()
};
let songs_filenames: Vec<String> = conn
@@ -70,7 +63,6 @@ impl Connection {
/// Fzf prompt for selecting song
pub fn play_fzf(&mut self) -> Result<()> {
is_installed("fzf").map_err(|ex| ex)?;
-
let ss = &self.songs_filenames;
let fzf_choice = rust_fzf::select(ss.clone(), Vec::new()).unwrap();
let index = get_choice_index(&self.songs_filenames, fzf_choice.get(0).unwrap());
@@ -174,29 +166,12 @@ impl Connection {
pub fn get_song_with_only_filename(&self, filename: &str) -> Song {
Song {
file: filename.to_string(),
- artist: None,
- title: None,
- duration: None,
- last_mod: None,
- name: None,
- place: None,
- range: None,
- tags: vec![("".to_string(), "".to_string())],
+ ..Default::default()
}
}
/// 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) -> Option<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();
-
for (i, f) in self.songs_filenames.iter().enumerate() {
if f.contains(short_path) {
return Some(self.songs_filenames.get(i).unwrap().to_string());
diff --git a/src/handler.rs b/src/handler.rs
index c9df412..6d8c32c 100755
--- a/src/handler.rs
+++ b/src/handler.rs
@@ -62,6 +62,7 @@ pub fn handle_key_events(key_event: KeyEvent, app: &mut App) -> AppResult<()> {
}
}
}
+
}
match key_event.code {
@@ -199,6 +200,7 @@ pub fn handle_key_events(key_event: KeyEvent, app: &mut App) -> AppResult<()> {
app.conn
.load_playlist(app.pl_list.list.get(app.pl_list.index).unwrap())?;
}
+
}
app.conn.update_status();
}
diff --git a/src/main.rs b/src/main.rs
index 9b436a5..467af46 100755
--- a/src/main.rs
+++ b/src/main.rs
@@ -26,7 +26,7 @@ pub type Error = Box<dyn std::error::Error>;
fn main() -> AppResult<()> {
let args = Args::parse();
let env_host = env::var("MPD_HOST").unwrap_or_else(|_| "127.0.0.1".to_string());
- let env_port = env::var("MPD_PORT").unwrap_or_else(|_| "6600".to_string());
+ let env_port = env::var("MPD_PORT").unwrap_or_else(|_| "6600".to_string());
let mut app = App::builder(format!("{}:{}", env_host, env_port).as_str())?;
if !args.tui {
@@ -38,15 +38,7 @@ fn main() -> AppResult<()> {
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(())
diff --git a/src/ui.rs b/src/ui.rs
index d23e9ae..93dcd53 100755
--- a/src/ui.rs
+++ b/src/ui.rs
@@ -1,3 +1,5 @@
+use std::time::Duration;
+
use crate::app::{App, SelectedTab};
use ratatui::{
prelude::*,
@@ -30,7 +32,8 @@ pub fn render(app: &mut App, frame: &mut Frame) {
match app.selected_tab {
SelectedTab::Queue => draw_queue(frame, app, layout[0]),
SelectedTab::Playlists => draw_playlists(frame, app, layout[0]),
- SelectedTab::DirectoryBrowser => draw_directory_browser(frame, app, layout[0]),
+ SelectedTab::DirectoryBrowser => draw_song_browser(frame, app, layout[0]),
+ // SelectedTab::SongBrowser => draw_song_browser(frame, app, layout[0]),
}
match app.inputmode {
@@ -63,6 +66,7 @@ fn draw_directory_browser(frame: &mut Frame, app: &mut App, size: Rect) {
if status {
list.push(ListItem::new(s.clone().magenta().bold()));
} else {
+ // list.push(ListItem::new(s.clone()));
list.push(ListItem::new(s.clone()));
}
} else {
@@ -75,7 +79,7 @@ fn draw_directory_browser(frame: &mut Frame, app: &mut App, size: Rect) {
let list = List::new(list)
.block(
Block::default()
- .title(format!("File Browser: {}", app.browser.path.clone()).bold())
+ .title(format!("Directory Browser: {}", app.browser.path.clone()).bold())
.title(
Title::from(format!("Total Songs: {}", total_songs).bold().green())
.alignment(Alignment::Center),
@@ -101,12 +105,120 @@ fn draw_directory_browser(frame: &mut Frame, app: &mut App, size: Rect) {
frame.render_stateful_widget(list, size, &mut song_state);
}
+/// Draws the song browser
+fn draw_song_browser(frame: &mut Frame, app: &mut App, size: Rect) {
+ let total_songs = app.conn.conn.stats().unwrap().songs.to_string();
+
+ let rows = app.browser.filetree.iter().enumerate().map(|(i, (t, s))| {
+ if t == "file" {
+ let song = app.browser.songs.get(i).unwrap().clone();
+
+ // metadata
+ let title = song.clone().title.unwrap_or_else(|| song.clone().file);
+ let artist = song.clone().artist.unwrap_or_default().cyan();
+ let album = song
+ .tags
+ .iter()
+ .filter(|(x, _)| x == "Album")
+ .map(|(_, l)| l.clone())
+ .collect::<Vec<String>>()
+ .join("");
+
+ let track = song
+ .tags
+ .iter()
+ .filter(|(x, _)| x == "Track")
+ .map(|(_, l)| l.clone())
+ .collect::<Vec<String>>()
+ .join("");
+
+ let time = humantime::format_duration(
+ song.clone().duration.unwrap_or_else(|| Duration::new(0, 0)),
+ );
+
+ let mut status: bool = false;
+ for sn in app.queue_list.list.iter() {
+ if sn.contains(s) {
+ status = true;
+ }
+ }
+
+ if status {
+ let row = Row::new(vec![
+ Cell::from(artist),
+ Cell::from(track.green()),
+ Cell::from(title),
+ Cell::from(album),
+ Cell::from(time.to_string().red()),
+ ]);
+ row.magenta().bold()
+ } else {
+ let row = Row::new(vec![
+ Cell::from(artist),
+ Cell::from(track.green()),
+ Cell::from(title),
+ Cell::from(album),
+ Cell::from(time.to_string().red()),
+ ]);
+ row
+ }
+ } else {
+ let row = Row::new(vec![Cell::from(format!("[{}]", *s))]);
+ row
+ }
+ });
+
+ let mut state = TableState::new();
+ let header = ["Artist", "Track", "Title", "Album", "Time"]
+ .into_iter()
+ .map(Cell::from)
+ .collect::<Row>()
+ .bold()
+ .height(1);
+ let table = Table::new(
+ rows,
+ [
+ Constraint::Percentage(33),
+ Constraint::Percentage(3),
+ Constraint::Percentage(30),
+ Constraint::Percentage(30),
+ Constraint::Percentage(3),
+ ],
+ )
+ .block(
+ Block::default()
+ .title(format!("Song Browser: {}", app.browser.path.clone()).bold())
+ .title(
+ Title::from(format!("Total Songs: {}", total_songs).bold().green())
+ .alignment(Alignment::Center),
+ )
+ .title(
+ Title::from(format!("Volume: {}%", app.conn.volume).bold().green())
+ .alignment(Alignment::Right),
+ )
+ .borders(Borders::ALL),
+ )
+ .highlight_style(
+ Style::default()
+ .add_modifier(Modifier::REVERSED)
+ .fg(Color::Cyan)
+ .bg(Color::Black),
+ )
+ .highlight_symbol(">>")
+ .header(header);
+
+ state.select(Some(app.browser.selected));
+ frame.render_stateful_widget(table, size, &mut state);
+}
+
/// draws playing queue
fn draw_queue(frame: &mut Frame, app: &mut App, size: Rect) {
let mut queue_state = ListState::default();
let title = Block::default()
.title(Title::from("Play Queue".green().bold()))
- .title(Title::from(format!("({} items)", app.queue_list.list.len()).bold()))
+ .title(Title::from(
+ format!("({} items)", app.queue_list.list.len()).bold(),
+ ))
.title(
Title::from(format!("Volume: {}%", app.conn.volume).bold().green())
.alignment(Alignment::Right),