Added highlighting, right click menu, other fixes
This commit is contained in:
@@ -2,15 +2,16 @@ mod nav_bar;
|
||||
mod song_edit_window;
|
||||
|
||||
|
||||
use egui::{Color32, Label, Sense};
|
||||
use std::collections::HashSet;
|
||||
|
||||
use egui::{Button, Color32, Label, RichText, Sense};
|
||||
use egui_extras::{Column, TableBuilder};
|
||||
use song_edit_window::{GuiError, GuiImportPlaylist, GuiNewSong};
|
||||
|
||||
use crate::{config::{Config, ConfigWrapper}, downloader::Downloader, manifest::{song::SongType, Manifest}};
|
||||
use crate::{config::{Config, ConfigWrapper}, downloader::Downloader, manifest::{song::{Song, SongType}, Manifest}};
|
||||
|
||||
use self::song_edit_window::GuiSongEditor;
|
||||
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct Gui {
|
||||
manifest: Manifest,
|
||||
@@ -20,14 +21,14 @@ pub struct Gui {
|
||||
error_w: GuiError,
|
||||
filter: String,
|
||||
downloader: Downloader,
|
||||
cfg: ConfigWrapper
|
||||
cfg: ConfigWrapper,
|
||||
downloading: bool,
|
||||
}
|
||||
|
||||
impl Gui {
|
||||
fn new(_: &eframe::CreationContext<'_>, manifest: Manifest, downloader: Downloader, cfg: ConfigWrapper) -> Self {
|
||||
Self {
|
||||
manifest,
|
||||
filter: String::new(),
|
||||
downloader,
|
||||
cfg,
|
||||
..Default::default()
|
||||
@@ -93,7 +94,7 @@ impl eframe::App for Gui {
|
||||
|
||||
ui.vertical(|ui| {
|
||||
ui.horizontal(|ui| {
|
||||
ui.colored_label(Color32::BLUE, "Filter: ");
|
||||
ui.colored_label(Color32::from_hex("#4444aa").unwrap(), "Filter: ");
|
||||
ui.text_edit_singleline(&mut self.filter);
|
||||
});
|
||||
});
|
||||
@@ -104,7 +105,7 @@ impl eframe::App for Gui {
|
||||
.striped(true)
|
||||
.cell_layout(egui::Layout::left_to_right(egui::Align::Center))
|
||||
.resizable(true)
|
||||
.column(Column::auto())
|
||||
//.column(Column::auto())
|
||||
.column(Column::auto())
|
||||
//.column(
|
||||
// Column::remainder()
|
||||
@@ -132,7 +133,7 @@ impl eframe::App for Gui {
|
||||
};
|
||||
|
||||
table.header(20.0, |mut header| {
|
||||
header.col(|_|{});
|
||||
// header.col(|_|{});
|
||||
header.col(|ui| {
|
||||
ui.strong("Playlist");
|
||||
});
|
||||
@@ -161,31 +162,83 @@ impl eframe::App for Gui {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
body.row(18.0, |mut row| {
|
||||
|
||||
row.col(|ui| {
|
||||
if ui.add(Label::new("[edit]").sense(Sense::click())).clicked() {
|
||||
self.song_edit_w.song = (
|
||||
ui.label(pname.clone())
|
||||
.context_menu(|ui| context_menu(self, ui, &pname, &sname, &s));
|
||||
});
|
||||
row.col(|ui| {
|
||||
let color =
|
||||
match s.get_type() {
|
||||
SongType::Youtube => Color32::from_hex("#FF0000").unwrap(),
|
||||
SongType::Spotify => Color32::from_hex("#1db954").unwrap(),
|
||||
SongType::Soundcloud => Color32::from_hex("#F26F23").unwrap()
|
||||
};
|
||||
|
||||
ui.colored_label(color, s.get_type().to_string())
|
||||
.context_menu(|ui| context_menu(self, ui, &pname, &sname, &s));
|
||||
});
|
||||
row.col(|ui| {
|
||||
ui.hyperlink_to(sname.clone(), s.get_url_str())
|
||||
.context_menu(|ui| context_menu(self, ui, &pname, &sname, &s));
|
||||
});
|
||||
|
||||
row.response()
|
||||
.context_menu(|ui| context_menu(self, ui, &pname, &sname, &s));
|
||||
|
||||
fn context_menu(this: &mut Gui, ui: &mut egui::Ui, pname: &String, sname: &String, song: &Song) {
|
||||
if ui.button("Edit").clicked() {
|
||||
this.song_edit_w.song = (
|
||||
pname.clone(),
|
||||
sname.clone(),
|
||||
);
|
||||
log::debug!("Label pressed");
|
||||
self.song_edit_w.is_open = true;
|
||||
self.song_edit_w.ed_name = sname.clone();
|
||||
self.song_edit_w.ed_url = s.get_url_str().clone();
|
||||
this.song_edit_w.is_open = true;
|
||||
this.song_edit_w.ed_name = sname.clone();
|
||||
this.song_edit_w.ed_url = song.get_url_str().clone();
|
||||
ui.close_menu()
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
row.col(|ui| {
|
||||
ui.label(pname.clone());
|
||||
});
|
||||
row.col(|ui| {
|
||||
ui.label(s.get_type().to_string());
|
||||
});
|
||||
row.col(|ui| {
|
||||
ui.hyperlink_to(sname.clone(), s.get_url_str());
|
||||
});
|
||||
if ui.button("Download").clicked() {
|
||||
if let Err(e) = this.downloader.download_song_nb(&this.cfg, pname, sname, song, this.manifest.get_format()) {
|
||||
log::error!("{e}");
|
||||
this.throw_error(format!("Failed to download song {sname}: {e}"));
|
||||
}
|
||||
ui.close_menu()
|
||||
}
|
||||
|
||||
if ui.button("Open Source").clicked() {
|
||||
if let Err(e) = open::that(song.get_url_str()) {
|
||||
log::error!("{e}");
|
||||
this.throw_error(format!("Failed to open song source: {e}"));
|
||||
}
|
||||
ui.close_menu()
|
||||
}
|
||||
if ui.button("Play").clicked() {
|
||||
let p = crate::util::get_song_path(pname, sname, this.manifest.get_format());
|
||||
|
||||
if !p.exists() {
|
||||
this.throw_error(format!("Song does not exist on disk"));
|
||||
} else if let Err(e) = open::that(p) {
|
||||
log::error!("{e}");
|
||||
this.throw_error(format!("Failed to play song: {e}"));
|
||||
}
|
||||
ui.close_menu()
|
||||
}
|
||||
if ui.button("Delete from disk").clicked() {
|
||||
let p = crate::util::get_song_path(pname, sname, this.manifest.get_format());
|
||||
if p.exists() {
|
||||
if let Err(e) = std::fs::remove_file(p) {
|
||||
this.throw_error(format!("Failed to delete file: {e}"));
|
||||
}
|
||||
}
|
||||
ui.close_menu();
|
||||
}
|
||||
if ui.button(RichText::new("Delete").color(Color32::RED)).clicked() {
|
||||
this.throw_error("TODO");
|
||||
ui.close_menu()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
@@ -46,7 +46,14 @@ impl Gui {
|
||||
ui.with_layout(egui::Layout::bottom_up(egui::Align::RIGHT), |ui| {
|
||||
ui.horizontal(|ui| {
|
||||
if self.downloader.get_songs_left_nb() > 0 {
|
||||
self.downloading = true;
|
||||
ui.label(format!("Downloading: {}/{}", self.downloader.get_songs_left_nb(), self.downloader.get_initial_song_count_nb()));
|
||||
} else if self.downloading {
|
||||
let _ = notify_rust::Notification::new()
|
||||
.summary("Done downloading")
|
||||
.body("Your music has been downloaded")
|
||||
.show();
|
||||
self.downloading = false;
|
||||
}
|
||||
let _ = self.downloader.download_all_nb_poll(&self.cfg);
|
||||
egui::widgets::global_dark_light_mode_buttons(ui);
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
|
||||
use egui::{Color32, RichText, TextBuffer};
|
||||
use std::sync::Arc;
|
||||
|
||||
use egui::{text::{LayoutJob, TextWrapping}, Color32, Label, RichText, Style, TextBuffer, TextFormat, TextStyle};
|
||||
|
||||
|
||||
use crate::manifest::{playlist::Playlist, song::{Song, SongType}};
|
||||
@@ -97,6 +99,7 @@ impl Gui {
|
||||
|
||||
playlist.remove_song(&song_name);
|
||||
playlist.add_song(self.song_edit_w.ed_name.clone(), song);
|
||||
self.song_edit_w.is_open = false;
|
||||
let _ = self.manifest.save(None);
|
||||
}
|
||||
}
|
||||
@@ -125,7 +128,7 @@ impl Gui {
|
||||
ui.horizontal(|ui| {
|
||||
ui.label("Playlist: ");
|
||||
egui::ComboBox::from_id_source("new_song_window_playlist")
|
||||
.selected_text(format!("{}", self.new_song_w.ed_playlist.clone().unwrap()))
|
||||
.selected_text(format!("{}", self.new_song_w.ed_playlist.clone().unwrap_or("".to_string())))
|
||||
.show_ui(ui, |ui| {
|
||||
for p in self.manifest.get_playlists().keys() {
|
||||
ui.selectable_value(&mut self.new_song_w.ed_playlist, Option::Some(p.clone()), p.as_str());
|
||||
@@ -212,8 +215,7 @@ impl Gui {
|
||||
ui.vertical(|ui| {
|
||||
ui.label(RichText::new("Error:").size(30.0).color(Color32::RED));
|
||||
ui.horizontal(|ui| {
|
||||
ui.label("Mcmg had an error:");
|
||||
ui.label(self.error_w.text.clone());
|
||||
ui.add(Label::new(self.error_w.text.clone()).wrap(true));
|
||||
})
|
||||
})
|
||||
});
|
||||
|
||||
@@ -37,7 +37,14 @@ impl Downloader {
|
||||
}
|
||||
|
||||
pub fn get_songs_left_nb(&self) -> usize {
|
||||
self.nb_cache.len()
|
||||
self.nb_cache.len() + crate::process_manager::proc_count()
|
||||
}
|
||||
|
||||
pub fn download_song_nb(&mut self, cfg: &ConfigWrapper, pname: &String, sname: &String, song: &Song, format: &Format) -> anyhow::Result<()> {
|
||||
self.nb_cache.push((pname.clone(), sname.clone(), song.clone(), format.clone()));
|
||||
self.nb_initial_song_count += 1;
|
||||
self.download_all_nb_poll(cfg)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn download_all_nb(&mut self, manifest: &Manifest, cfg: &ConfigWrapper) -> anyhow::Result<Option<usize>> {
|
||||
@@ -57,7 +64,7 @@ impl Downloader {
|
||||
self.download_song(cfg, &sname, &song, &pname, &format)?;
|
||||
}
|
||||
}
|
||||
if self.nb_cache.is_empty() {
|
||||
if self.get_songs_left_nb() == 0 {
|
||||
self.nb_initial_song_count = 0;
|
||||
}
|
||||
if crate::process_manager::proc_count() == 0 && self.nb_cache.is_empty() {
|
||||
@@ -81,7 +88,8 @@ impl Downloader {
|
||||
self.count += crate::process_manager::wait_for_procs_untill(0)?;
|
||||
Ok(self.count)
|
||||
}
|
||||
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn download_playlist(&mut self, cfg: &ConfigWrapper, url: &String, pname: &String, format: &Format) -> anyhow::Result<usize> {
|
||||
self.download_playlist_nb(cfg, url, pname, format)?;
|
||||
let mut count = 0;
|
||||
@@ -126,7 +134,7 @@ impl Downloader {
|
||||
pub fn download_song(&mut self, cfg: &ConfigWrapper, name: &String, song: &Song, playlist: &String, format: &Format) -> anyhow::Result<()> {
|
||||
let dl_dir = format!("{}/{playlist}", cfg.cli.output);
|
||||
let dl_file = format!("{dl_dir}/{}.{}", name, &format);
|
||||
|
||||
log::debug!("Checking: {dl_file}");
|
||||
if PathBuf::from(&dl_file).exists() {
|
||||
log::debug!("File {dl_file} exists, skipping");
|
||||
return Ok(())
|
||||
|
||||
@@ -64,6 +64,7 @@ impl Song {
|
||||
|
||||
|
||||
|
||||
|
||||
impl TryFrom<url::Url> for SongType {
|
||||
type Error = anyhow::Error;
|
||||
|
||||
|
||||
30
src/util.rs
30
src/util.rs
@@ -1,6 +1,6 @@
|
||||
use std::path::PathBuf;
|
||||
use std::{any::Any, path::PathBuf};
|
||||
|
||||
use crate::constants;
|
||||
use crate::{constants, manifest::Format};
|
||||
|
||||
pub(crate) fn is_supported_host(url: url::Url) -> bool {
|
||||
let host = url.host_str();
|
||||
@@ -52,6 +52,11 @@ pub(crate) fn isatty() -> bool {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub fn as_any_mut<T: Any>(val: &mut T) -> &mut dyn Any {
|
||||
val as &mut dyn Any
|
||||
}
|
||||
|
||||
// pub async fn dl_to_file(url: &str, p: PathBuf) -> anyhow::Result<()> {
|
||||
// log::info!("Downloading {} -> {:?}", url, p);
|
||||
// let ytdlp_req = reqwest::get(url).await?.bytes().await?;
|
||||
@@ -60,4 +65,23 @@ pub(crate) fn isatty() -> bool {
|
||||
// fd.write(&ytdlp_req)?;
|
||||
// log::debug!("Finished writing {:?}", p);
|
||||
// Ok(())
|
||||
// }
|
||||
// }
|
||||
|
||||
pub fn get_song_path/*<P: TryInto<PathBuf>>*/(/*basepath: Option<P>,*/ pname: &String, sname: &String, format: &Format) -> PathBuf {
|
||||
// let mut path: PathBuf;
|
||||
/*if let Some(bp) = basepath {
|
||||
if let Ok(bp) = bp.try_into() {
|
||||
path = bp;
|
||||
} else {
|
||||
path = std::env::current_dir().unwrap_or(PathBuf::new());
|
||||
}
|
||||
} else {*/
|
||||
let mut path = std::env::current_dir().unwrap_or(PathBuf::new());
|
||||
//}
|
||||
// TODO: Get this from cfg
|
||||
path.push("out");
|
||||
path.push(pname);
|
||||
path.push(sname);
|
||||
path.set_extension(format.to_string());
|
||||
path
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user