Clippy pedantic fixes

This commit is contained in:
Gvidas Juknevičius 2024-09-22 02:16:18 +03:00
parent 387a5eaf4a
commit f7008e882c
Signed by: MCorange
GPG Key ID: 12B1346D720B7FBB
22 changed files with 162 additions and 275 deletions

BIN
assets/icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.4 KiB

View File

@ -2,6 +2,7 @@ use camino::Utf8PathBuf;
use clap::{Parser, Subcommand};
#[allow(clippy::pedantic)]
#[derive(Debug, Parser, Default, Clone)]
pub struct CliArgs {
/// Show more info
@ -25,6 +26,7 @@ pub struct CliArgs {
}
#[allow(clippy::pedantic)]
#[derive(Debug, Subcommand, Clone)]
pub enum CliCommand {
Download,

View File

@ -11,7 +11,7 @@ use self::cli::CliArgs;
// const YTDLP_DL_URL: &'static str = "https://github.com/yt-dlp/yt-dlp/archive/refs/heads/master.zip";
// const SPOTDL_DL_URL: &'static str = "https://github.com/spotDL/spotify-downloader/archive/refs/heads/master.zip";
#[allow(clippy::pedantic)]
#[derive(Debug, Default, Clone)]
pub struct ConfigWrapper {
pub cfg: Config,
@ -19,17 +19,20 @@ pub struct ConfigWrapper {
pub isatty: bool
}
#[allow(clippy::pedantic)]
#[derive(Debug, Serialize, Deserialize, Default, Clone)]
pub struct Config {
pub ytdlp: ConfigYtdlp,
pub spotdl: ConfigSpotdl,
}
#[allow(clippy::pedantic)]
#[derive(Debug, Serialize, Deserialize, Default, Clone)]
pub struct ConfigYtdlp {
pub path: PathBuf,
}
#[allow(clippy::pedantic)]
#[derive(Debug, Serialize, Deserialize, Default, Clone)]
pub struct ConfigSpotdl {
pub path: PathBuf,
@ -37,21 +40,22 @@ pub struct ConfigSpotdl {
impl ConfigWrapper {
pub async fn parse() -> Result<Self> {
#[allow(clippy::field_reassign_with_default)]
pub fn parse() -> Result<Self> {
let mut s = Self::default();
s.cli = cli::CliArgs::parse();
crate::logger::init_logger(s.cli.debug);
s.cfg = Config::parse(&s.cli).await?;
crate::logger::init(s.cli.debug);
s.cfg = Config::parse(&s.cli)?;
s.isatty = isatty();
Ok(s)
}
}
impl Config {
pub async fn parse(cli: &CliArgs) -> Result<Self> {
pub fn parse(cli: &CliArgs) -> Result<Self> {
if !cli.config.exists() {
log::info!("Config doesnt exist");
return Self::setup_config(&cli).await;
return Self::setup_config(cli);
}
let data = std::fs::read_to_string(&cli.config)?;
@ -59,63 +63,51 @@ impl Config {
Ok(data)
}
async fn setup_config(cli: &CliArgs) -> Result<Self> {
fn setup_config(cli: &CliArgs) -> Result<Self> {
let mut s = Self::default();
let mut error = false;
match util::is_program_in_path("yt-dlp") {
Some(p) => {
s.ytdlp.path = p;
},
if let Some(p) = util::is_program_in_path("yt-dlp") {
s.ytdlp.path = p;
} else {
error = true;
log::error!("could not find yt-dlp, please install it.");
log::info!(" - With winget (Windows only) (recommended):");
log::info!(" - Most new windows versions have winget installed, if not, instructions here: https://learn.microsoft.com/en-us/windows/package-manager/winget/#install-winget");
log::info!(" - run `winget install yt-dlp`");
log::info!(" - With chocolatey (Windows only):");
log::info!(" - Make sure you have chocolatey installed - https://chocolatey.org/install");
log::info!(" - run `choco install yt-dlp` as Admin");
log::info!(" - With pip (from python) (Cross platform)");
log::info!(" - Make sure you have python installed");
log::info!(" - pip install yt-dlp");
log::info!(" - Using your distro's package manager (Unix/BSD only) (Not recommended)");
}
None => {
if let Some(p) = util::is_program_in_path("spotdl") {
s.spotdl.path = p;
} else {
let res = crate::prompt::yes_no("Spotdl is not installed but if you dont need to download music from spotify you dont need it, skip it?", None);
if res {
s.spotdl.path = PathBuf::from("UNUSED");
} else {
error = true;
log::error!("could not find yt-dlp, please install it.");
log::info!(" - With winget (Windows only) (recommended):");
log::info!(" - Most new windows versions have winget installed, if not, instructions here: https://learn.microsoft.com/en-us/windows/package-manager/winget/#install-winget");
log::info!(" - run `winget install yt-dlp`");
log::info!(" - With chocolatey (Windows only):");
log::info!(" - Make sure you have chocolatey installed - https://chocolatey.org/install");
log::info!(" - run `choco install yt-dlp` as Admin");
log::info!(" - With pip (from python) (Cross platform)");
log::info!(" - Make sure you have python installed");
log::info!(" - pip install yt-dlp");
log::info!(" - Using your distro's package manager (Unix/BSD only) (Not recommended)")
log::error!("could not find spotdl, please install it. ");
log::info!(" - With pip (from python) (Cross platform) (recommended)");
log::info!(" - Make sure you have python installed - https://www.python.org/downloads/");
log::info!(" - pip install spotdl");
}
}
match util::is_program_in_path("spotdl") {
Some(p) => {
s.spotdl.path = p;
},
None => {
let res = crate::prompt::prompt_bool("Spotdl is not installed but if you dont need to download music from spotify you dont need it, skip it?", None);
if res {
s.spotdl.path = PathBuf::from("UNUSED");
} else {
error = true;
log::error!("could not find spotdl, please install it. ");
log::info!(" - With pip (from python) (Cross platform) (recommended)");
log::info!(" - Make sure you have python installed - https://www.python.org/downloads/");
log::info!(" - pip install spotdl");
}
}
}
match util::is_program_in_path("ffmpeg") {
Some(_) => (),
None => {
error = true;
log::error!("could not find ffmpeg, please install it.");
log::info!(" - With winget (Windows only) (recommended):");
log::info!(" - Most new windows versions have winget installed, if not, instructions here: https://learn.microsoft.com/en-us/windows/package-manager/winget/#install-winget");
log::info!(" - run `winget install --id=Gyan.FFmpeg -e`");
log::info!(" - With chocolatey (Windows only):");
log::info!(" - Make sure you have chocolatey installed - https://chocolatey.org/install");
log::info!(" - run `choco install ffmpeg` as Admin");
}
if util::is_program_in_path("ffmpeg").is_none() {
error = true;
log::error!("could not find ffmpeg, please install it.");
log::info!(" - With winget (Windows only) (recommended):");
log::info!(" - Most new windows versions have winget installed, if not, instructions here: https://learn.microsoft.com/en-us/windows/package-manager/winget/#install-winget");
log::info!(" - run `winget install --id=Gyan.FFmpeg -e`");
log::info!(" - With chocolatey (Windows only):");
log::info!(" - Make sure you have chocolatey installed - https://chocolatey.org/install");
log::info!(" - run `choco install ffmpeg` as Admin");
}
if !error {

View File

@ -1,15 +1,15 @@
#[cfg(target_family="windows")]
mod constants {
pub const PATH_VAR_SEP: &'static str = ";";
pub const EXEC_EXT: &'static str = "exe";
mod _m {
pub const PATH_VAR_SEP: &str = ";";
pub const EXEC_EXT: &str = "exe";
}
#[cfg(target_family="unix")]
mod constants {
pub const PATH_VAR_SEP: &'static str = ":";
pub const EXEC_EXT: &'static str = "";
mod _m {
pub const PATH_VAR_SEP: &str = ":";
pub const EXEC_EXT: &str = "";
}
pub use constants::*;
pub use _m::*;

View File

@ -40,8 +40,8 @@ impl Downloader {
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()));
pub fn download_song_nb(&mut self, cfg: &ConfigWrapper, pname: &str, sname: &str, song: &Song, format: &Format) -> anyhow::Result<()> {
self.nb_cache.push((pname.to_string(), sname.to_string(), song.clone(), format.clone()));
self.nb_initial_song_count += 1;
self.download_all_nb_poll(cfg)?;
Ok(())
@ -76,12 +76,12 @@ impl Downloader {
pub async fn download_all(&mut self, manifest: &Manifest, cfg: &ConfigWrapper) -> anyhow::Result<usize> {
pub fn download_all(&mut self, manifest: &Manifest, cfg: &ConfigWrapper) -> anyhow::Result<usize> {
let format = manifest.get_format();
for (name, playlist) in manifest.get_playlists() {
for (song_name, song) in playlist.get_songs() {
self.download_song(cfg, song_name, song, &name, format)?;
self.download_song(cfg, song_name, song, name, format)?;
self.count += crate::process_manager::wait_for_procs_untill(10)?;
}
}
@ -90,7 +90,7 @@ impl Downloader {
}
#[allow(dead_code)]
pub fn download_playlist(&mut self, cfg: &ConfigWrapper, url: &String, pname: &String, format: &Format) -> anyhow::Result<usize> {
pub fn download_playlist(&mut self, cfg: &ConfigWrapper, url: &str, pname: &str, format: &Format) -> anyhow::Result<usize> {
self.download_playlist_nb(cfg, url, pname, format)?;
let mut count = 0;
while let Some(c) = self.download_all_nb_poll(cfg)? {
@ -99,14 +99,14 @@ impl Downloader {
Ok(count)
}
pub fn download_playlist_nb(&mut self, cfg: &ConfigWrapper, url: &String, pname: &String, format: &Format) -> anyhow::Result<HashMap<String, Song>> {
pub fn download_playlist_nb(&mut self, cfg: &ConfigWrapper, url: &str, pname: &str, format: &Format) -> anyhow::Result<HashMap<String, Song>> {
log::warn!("This automatically assumes its a youtube link as it is currently the only supported playlist source");
let mut cmd = tokio::process::Command::new(&cfg.cfg.ytdlp.path);
cmd.args([
"--flat-playlist",
"--simulate",
"-O", "%(url)s|%(title)s",
url.as_str()
url
]);
cmd
.stderr(Stdio::null())
@ -119,11 +119,11 @@ impl Downloader {
let out = futures::executor::block_on(ftr)?.stdout;
let out = String::from_utf8(out)?;
for line in out.lines() {
let mut split_text = line.split("|").collect::<Vec<&str>>();
let mut split_text = line.split('|').collect::<Vec<&str>>();
let url = split_text.swap_remove(0).to_string();
let sname = split_text.join("|");
let song = Song::from_url_str(url)?.set_type(SongType::Youtube).clone();
self.nb_cache.push((pname.clone(), sname.clone(), song.clone(), format.clone()));
self.nb_cache.push((pname.to_string(), sname.clone(), song.clone(), format.clone()));
ret.insert(sname, song.clone());
}
self.nb_initial_song_count += out.lines().count();
@ -131,7 +131,7 @@ impl Downloader {
Ok(ret)
}
pub fn download_song(&mut self, cfg: &ConfigWrapper, name: &String, song: &Song, playlist: &String, format: &Format) -> anyhow::Result<()> {
pub fn download_song(&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}");
@ -168,7 +168,7 @@ impl Downloader {
]);
cmd
}
url => {
url @ SongType::Soundcloud => {
log::error!("Unknown or unsupported hostname '{:?}'", url);
return Ok(());
}

View File

@ -1,7 +1,7 @@
use log::LevelFilter;
pub fn init_logger(debug: bool) {
pub fn init(debug: bool) {
let level = if debug {
LevelFilter::Debug
} else {
@ -11,4 +11,4 @@ pub fn init_logger(debug: bool) {
.format_timestamp(None)
.filter_level(level)
.init();
}
}

View File

@ -8,15 +8,14 @@ mod manifest;
mod logger;
mod downloader;
mod util;
mod prompt;
mod config;
mod constants;
mod process_manager;
mod ui;
mod prompt;
#[tokio::main]
async fn main() {
let Ok(cfg) = ConfigWrapper::parse().await else {
fn main() {
let Ok(cfg) = ConfigWrapper::parse() else {
return;
};
@ -29,5 +28,5 @@ async fn main() {
};
let _ = ui::cli::command_run(&cfg, &mut manifest).await;
let _ = ui::cli::command_run(&cfg, &mut manifest);
}

View File

@ -2,6 +2,7 @@
pub mod song;
pub mod playlist;
use playlist::Playlist;
use song::Song;
use std::{collections::HashMap, fmt::{Debug, Display}, path::PathBuf};
@ -10,7 +11,7 @@ use anyhow::{bail, Result};
use serde::{Deserialize, Serialize};
const DEFAULT_MANIFEST: &'static str = include_str!("../../manifest.default.json");
const DEFAULT_MANIFEST: &str = include_str!("../../manifest.default.json");
@ -51,7 +52,7 @@ impl Manifest {
self.get_playlist_mut(playlist_name)?.get_song_mut(name)
}
pub fn add_playlist(&mut self, playlist_name: String) {
self.playlists.insert(playlist_name, Default::default());
self.playlists.insert(playlist_name, Playlist::default());
}
pub fn get_playlist(&self, playlist_name: &String) -> Option<&playlist::Playlist> {
self.playlists.get(playlist_name)
@ -67,7 +68,7 @@ impl Manifest {
}
pub fn get_song_count(&self) -> usize {
let mut count = 0;
for (_, v) in &self.playlists {
for v in self.playlists.values() {
count += v.len();
}
count
@ -98,7 +99,7 @@ impl Manifest {
let mut s = Self::default();
log::debug!("Path: {p:?}");
s.path = p.clone();
s.path.clone_from(p);
s.load(Some(p))?;
Ok(s)
}

View File

@ -1,9 +1,9 @@
use std::str::FromStr;
use std::{fmt::Display, str::FromStr};
use anyhow::{bail, Result};
use serde::{Deserialize, Serialize};
#[allow(clippy::pedantic)]
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, Default)]
pub enum SongType {
#[default]
@ -12,14 +12,14 @@ pub enum SongType {
Soundcloud,
}
impl ToString for SongType {
fn to_string(&self) -> String {
let s = match self {
SongType::Youtube => "Youtube",
SongType::Spotify => "Spotify",
SongType::Soundcloud => "Soundcloud",
};
String::from(s)
impl Display for SongType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Soundcloud => write!(f, "SoundCloud")?,
Self::Spotify => write!(f, "Spotify")?,
Self::Youtube => write!(f, "YouTube")?,
}
Ok(())
}
}
@ -31,8 +31,9 @@ pub struct Song {
#[allow(dead_code)]
impl Song {
pub fn from_url_str(url: String) -> Result<Self> {
Self::from_url(url::Url::from_str(url.as_str())?)
#[allow(clippy::needless_pass_by_value)]
pub fn from_url_str<S: ToString>(url: S) -> Result<Self> {
Self::from_url(url::Url::from_str(&url.to_string())?)
}
pub fn from_url(url: url::Url) -> Result<Self> {
Ok(Self {

View File

@ -1,108 +1,7 @@
use std::{collections::HashMap, io::Write};
use std::io::Write;
//pub(crate) fn simple_prompt(p: &str) -> String {
//
// print!("{c}prompt{r}: {p} > ",
// c=anstyle::AnsiColor::Cyan.render_fg(),
// r=anstyle::Reset.render()
// );
//
// // I dont care if it fails
// let _ = std::io::stdout().flush();
//
// let mut buf = String::new();
// let _ = std::io::stdin().read_line(&mut buf);
//
// buf.trim().to_string()
//}
#[allow(dead_code)]
pub(crate) fn prompt_with_list(p: &str, options: &[&str]) -> usize {
println!("{c}prompt{r}: {p}",
c=anstyle::AnsiColor::Cyan.render_fg(),
r=anstyle::Reset.render()
);
for (i, op) in options.iter().enumerate() {
println!(" - {}: {}", i, op);
}
print!("> ");
// I dont care if it fails
let _ = std::io::stdout().flush();
let mut buf = String::new();
let _ = std::io::stdin().read_line(&mut buf);
if let Ok(num) = buf.parse::<usize>() {
if num <= options.len() {
return num;
} else {
return prompt_with_list(p, options);
}
} else {
return prompt_with_list(p, options);
}
}
// pub(crate) fn prompt_with_list_or_str(p: &str, options: &[String]) -> String {
// println!("{c}prompt{r}: {p} (select with number or input text)",
// c=anstyle::AnsiColor::Cyan.render_fg(),
// r=anstyle::Reset.render()
// );
//
// for (i, op) in options.iter().enumerate() {
// println!(" - {}: {}", i, op);
// }
//
// print!("> ");
// // I dont care if it fails
// let _ = std::io::stdout().flush();
//
// let mut buf = String::new();
// let _ = std::io::stdin().read_line(&mut buf);
//
// if let Ok(num) = buf.trim().parse::<usize>() {
// if let Some(g) = options.get(num) {
// return g.clone();
// } else {
// return prompt_with_list_or_str(p, options);
// }
// } else {
// return buf.trim().to_string();
// }
// }
#[allow(dead_code)]
pub(crate) fn prompt_with_map(p: &str, options: HashMap<&str, &str>) -> String {
println!("{c}prompt{r}: {p}",
c=anstyle::AnsiColor::Cyan.render_fg(),
r=anstyle::Reset.render()
);
let mut keys = Vec::new();
for (k, v) in &options {
println!(" - {}: {}", k, v);
keys.push(k.trim().to_lowercase())
}
print!("> ");
// I dont care if it fails
let _ = std::io::stdout().flush();
let mut buf = String::new();
let _ = std::io::stdin().read_line(&mut buf);
if !keys.contains(&buf.trim().to_lowercase()) {
return prompt_with_map(p, options);
}
buf.trim().to_string()
}
pub fn prompt_bool(p: &str, default: Option<bool>) -> bool {
pub fn yes_no(p: &str, default: Option<bool>) -> bool {
if default == Some(true) {
println!("{c}prompt{r}: {p} (Y/n)",
c=anstyle::AnsiColor::Cyan.render_fg(),
@ -132,7 +31,7 @@ pub fn prompt_bool(p: &str, default: Option<bool>) -> bool {
Some(true) => return true,
Some(false) => return false,
None => {
return prompt_bool(p, default);
return yes_no(p, default);
}
}
}
@ -142,7 +41,7 @@ pub fn prompt_bool(p: &str, default: Option<bool>) -> bool {
"n" => false,
c => {
log::error!("'{c}' is invalid, type y (yes) or n (no)");
return prompt_bool(p, default);
yes_no(p, default)
}
}
}

View File

@ -6,34 +6,34 @@ use crate::{config::ConfigWrapper, downloader::Downloader, manifest::{song::Song
pub async fn add(cfg: &ConfigWrapper, manifest: &mut Manifest, downloader: &mut Downloader, url: &String, name: &String, playlist: &String) -> anyhow::Result<()> {
pub fn song(cfg: &ConfigWrapper, manifest: &mut Manifest, downloader: &mut Downloader, url: &str, name: &String, playlist: &String) -> anyhow::Result<()> {
let mut playlists = manifest.get_playlists().keys().map(|f| f.clone()).collect::<Vec<String>>();
let mut playlists = manifest.get_playlists().keys().cloned().collect::<Vec<String>>();
playlists.sort();
if !is_supported_host(url::Url::from_str(&url)?) {
if !is_supported_host(&url::Url::from_str(url)?) {
log::error!("Invalid or unsupported host name");
return Ok(());
}
let song = Song::from_url_str(url.clone())?;
let song = Song::from_url_str(url.to_string())?;
manifest.add_song(playlist, name.clone(), song.clone());
manifest.save(None)?;
let should_download = crate::prompt::prompt_bool("Download song now?", Some(false));
let should_download = crate::prompt::yes_no("Download song now?", Some(false));
if should_download {
downloader.download_song(cfg, &name, &song, &playlist, manifest.get_format())?;
downloader.download_song(cfg, name, &song, playlist, manifest.get_format())?;
crate::process_manager::wait_for_procs_untill(0)?;
}
Ok(())
}
pub async fn add_playlist(cfg: &ConfigWrapper, manifest: &mut Manifest, downloader: &mut Downloader, url: &String, name: &String) -> anyhow::Result<()> {
pub fn playlist(cfg: &ConfigWrapper, manifest: &mut Manifest, downloader: &mut Downloader, url: &str, name: &String) -> anyhow::Result<()> {
let songs = downloader.download_playlist_nb(cfg, url, name, manifest.get_format())?;
if manifest.get_playlist(name).is_some() {

View File

@ -4,14 +4,14 @@ use crate::{config::{cli::CliCommand, ConfigWrapper}, downloader::Downloader, ma
pub async fn command_run(cfg: &ConfigWrapper, manifest: &mut Manifest) -> anyhow::Result<()> {
pub fn command_run(cfg: &ConfigWrapper, manifest: &mut Manifest) -> anyhow::Result<()> {
log::info!("Is in term: {}", cfg.isatty);
//std::fs::write("./isatty", format!("{}\n", cfg.isatty))?;
let mut downloader = Downloader::new();
match (&cfg.cli.command, cfg.isatty) {
(None | Some(CliCommand::Download), true) => {
match downloader.download_all(manifest, &cfg).await {
match downloader.download_all(manifest, cfg) {
Ok(count) => log::info!("Downloaded {count} songs"),
Err(e) => {
log::error!("Failed to download songs: {e}");
@ -23,12 +23,12 @@ pub async fn command_run(cfg: &ConfigWrapper, manifest: &mut Manifest) -> anyhow
match c {
CliCommand::Download => unreachable!(),
CliCommand::AddPlaylist { url, name } => {
if let Err(e) = add::add_playlist(cfg, manifest, &mut downloader, url, name).await {
if let Err(e) = add::playlist(cfg, manifest, &mut downloader, url, name) {
log::error!("Failed to run 'add-playlist' commmand: {e}");
}
}
CliCommand::Add { url, name, playlist } => {
if let Err(e) = add::add(cfg, manifest, &mut downloader, url, name, playlist).await {
if let Err(e) = add::song(cfg, manifest, &mut downloader, url, name, playlist) {
log::error!("Failed to run 'add' command: {e}");
}
}

View File

@ -11,7 +11,7 @@ impl /* ComponentUi for */ ContextMenu {
let w = gui.windows.get_window::<GuiSongEditor>(WindowIndex::SongEdit);
w.set_active_song(pname, sname, song.get_url_str());
gui.windows.open(WindowIndex::SongEdit, true);
ui.close_menu()
ui.close_menu();
}
if ui.button("Download").clicked() {
@ -19,7 +19,7 @@ impl /* ComponentUi for */ ContextMenu {
log::error!("{e}");
gui.throw_error(format!("Failed to download song {sname}: {e}"));
}
ui.close_menu()
ui.close_menu();
}
if ui.button("Open Source").clicked() {
@ -27,18 +27,18 @@ impl /* ComponentUi for */ ContextMenu {
log::error!("{e}");
gui.throw_error(format!("Failed to open song source: {e}"));
}
ui.close_menu()
ui.close_menu();
}
if ui.button("Play").clicked() {
let p = crate::util::get_song_path(pname, sname, gui.manifest.get_format());
if !p.exists() {
gui.throw_error(format!("Song does not exist on disk"));
gui.throw_error("Song does not exist on disk".to_string());
} else if let Err(e) = open::that(p) {
log::error!("{e}");
gui.throw_error(format!("Failed to play song: {e}"));
}
ui.close_menu()
ui.close_menu();
}
if ui.button("Delete from disk").clicked() {
let p = crate::util::get_song_path(pname, sname, gui.manifest.get_format());
@ -51,7 +51,7 @@ impl /* ComponentUi for */ ContextMenu {
}
if ui.button(RichText::new("Delete").color(Color32::RED)).clicked() {
gui.throw_error("TODO");
ui.close_menu()
ui.close_menu();
}
}
}

View File

@ -3,8 +3,9 @@ use crate::ui::gui::{windows::WindowIndex, Gui};
use super::Component;
#[allow(clippy::pedantic)]
pub struct NavBar;
#[warn(clippy::pedantic)]
impl Component for NavBar {
fn ui(gui: &mut Gui, ctx: &egui::Context) {

View File

@ -1,4 +1,4 @@
use egui::{Color32, RichText};
use egui::Color32;
use egui_extras::{Column, TableBuilder};
use crate::manifest::song::SongType;
@ -60,7 +60,7 @@ impl ComponentUi for SongList {
let mut songs = Vec::new();
for (pname, p) in playlists {
for (sname, s) in p {
songs.push((pname.clone(), sname, s))
songs.push((pname.clone(), sname, s));
}
}
songs
@ -91,10 +91,8 @@ impl ComponentUi for SongList {
if !s.get_url_str().contains(&filter_clean) {
continue;
}
} else if !filter_clean.is_empty() {
if !sname.to_lowercase().contains(&filter_clean) {
continue;
}
} else if !filter_clean.is_empty() && !sname.to_lowercase().contains(&filter_clean) {
continue;
}
body.row(18.0, |mut row| {
@ -121,7 +119,7 @@ impl ComponentUi for SongList {
row.response()
.context_menu(|ui| ContextMenu::ui(gui, ui, &pname, &sname, &s));
})
});
}
});
});

View File

@ -31,12 +31,10 @@ impl Gui {
let native_options = eframe::NativeOptions {
viewport: egui::ViewportBuilder::default()
.with_inner_size([400.0, 300.0])
.with_min_inner_size([300.0, 220.0]),
// .with_icon(
// // NOTE: Adding an icon is optional
// eframe::icon_data::from_png_bytes(&include_bytes!("../assets/icon-256.png")[..])
// .expect("Failed to load icon"),
// ),
.with_min_inner_size([300.0, 220.0])
.with_icon(
eframe::icon_data::from_png_bytes(&include_bytes!("../../../assets/icon.png")[..])?,
),
..Default::default()
};
@ -50,9 +48,11 @@ impl Gui {
Ok(())
}
pub fn throw_error(&mut self, text: impl ToString) {
#[allow(clippy::pedantic)]
pub fn throw_error<S: ToString>(&mut self, text: S) {
let w = self.windows.get_window::<windows::error::GuiError>(WindowIndex::Error);
w.set_error_message(text);
w.set_error_message(&text);
self.windows.open(WindowIndex::Error, true);
}
}
@ -66,7 +66,7 @@ impl eframe::App for Gui {
downloader: self.downloader.clone(),
manifest: self.manifest.clone(),
};
self.windows.ui(&mut state, ctx).unwrap();
self.windows.ui(&mut state, ctx);
self.cfg = state.cfg;
self.downloader = state.downloader;
self.manifest = state.manifest;

View File

@ -3,6 +3,7 @@ use egui::{Color32, Label, RichText};
use super::{State, Window};
#[allow(clippy::pedantic)]
#[derive(Debug, Default)]
pub struct GuiError {
text: String,
@ -26,7 +27,7 @@ impl Window for GuiError {
}
impl GuiError {
pub fn set_error_message<S: ToString>(&mut self, text: S) {
pub fn set_error_message<S: ToString>(&mut self, text: &S) {
self.text = text.to_string();
}
}

View File

@ -1,7 +1,7 @@
use super::{State, Window};
#[allow(clippy::pedantic)]
#[derive(Debug, Default)]
pub struct GuiImportPlaylist {
ed_name: String,
@ -41,7 +41,7 @@ impl Window for GuiImportPlaylist {
log::error!("Playlist {name} already exists");
}
let songs = state.downloader.download_playlist_nb(&state.cfg, &url, &name, &state.manifest.get_format()).unwrap();
let songs = state.downloader.download_playlist_nb(&state.cfg, &url, &name, state.manifest.get_format()).unwrap();
state.manifest.add_playlist(name.clone());
let playlist = state.manifest.get_playlist_mut(&name).expect("Unreachable");

View File

@ -45,17 +45,17 @@ impl WindowManager {
}
#[allow(dead_code)]
pub fn is_open(&self, id: &WindowIndex) -> bool {
*self.opened.get(id).unwrap()
pub fn is_open(&self, id: WindowIndex) -> bool {
*self.opened.get(&id).unwrap()
}
pub fn open(&mut self, id: WindowIndex, open: bool) {
self.opened.insert(id, open);
}
pub fn ui(&mut self, state: &mut State, ctx: &egui::Context) -> anyhow::Result<()> {
pub fn ui(&mut self, state: &mut State, ctx: &egui::Context) {
for (id, window) in &mut self.windows {
if !self.opened.contains_key(&id) {
if !self.opened.contains_key(id) {
self.opened.insert(*id, false);
}
let open = self.opened.get_mut(id).unwrap();
@ -63,8 +63,6 @@ impl WindowManager {
log::error!("Window {id:?} errored: {e}");
}
}
Ok(())
}
pub fn get_window<T: Window + 'static>(&mut self, id: WindowIndex) -> &mut Box<T> {

View File

@ -1,6 +1,5 @@
use anyhow::{Result, bail};
use anyhow::bail;
use egui::Color32;
use crate::ui::gui::Gui;
use super::{State, Window};
@ -44,7 +43,7 @@ impl Window for GuiSongEditor {
ui.horizontal(|ui| {
ui.label("Type: ");
ui.label(&song.get_type().to_string());
ui.label(song.get_type().to_string());
});
ui.horizontal(|ui| {
@ -67,7 +66,7 @@ impl Window for GuiSongEditor {
bail!("Failed to get song (2)");
};
*song.get_url_str_mut() = self.ed_url.clone();
song.get_url_str_mut().clone_from(&self.ed_url);
}
let Some(playlist) = state.manifest.get_playlist_mut(&playlist) else {
@ -86,10 +85,10 @@ impl Window for GuiSongEditor {
}
impl GuiSongEditor {
pub fn set_active_song(&mut self, pname: &String, sname: &String, url: &String) {
self.song.0 = pname.clone();
self.song.1 = sname.clone();
self.ed_name = sname.clone();
self.ed_url = url.clone();
pub fn set_active_song(&mut self, pname: &str, sname: &str, url: &str) {
self.song.0 = pname.to_string();
self.song.1 = sname.to_string();
self.ed_name = sname.to_string();
self.ed_url = url.to_string();
}
}

View File

@ -3,10 +3,10 @@ use super::{State, Window};
#[derive(Debug, Default)]
pub struct GuiNewSong {
ed_type: SongType,
ed_name: String,
ed_playlist: Option<String>,
ed_url: String,
typ: SongType,
name: String,
playlist: Option<String>,
url: String,
}
impl Window for GuiNewSong {
@ -18,33 +18,33 @@ impl Window for GuiNewSong {
ui.horizontal(|ui| {
ui.label("Type: ");
egui::ComboBox::from_id_source("new_song_window_type")
.selected_text(format!("{:?}", self.ed_type))
.selected_text(format!("{:?}", self.typ))
.show_ui(ui, |ui| {
ui.selectable_value(&mut self.ed_type, SongType::Youtube, "Youtube");
ui.selectable_value(&mut self.ed_type, SongType::Spotify, "Spotify");
ui.selectable_value(&mut self.ed_type, SongType::Soundcloud, "Soundcloud");
ui.selectable_value(&mut self.typ, SongType::Youtube, "Youtube");
ui.selectable_value(&mut self.typ, SongType::Spotify, "Spotify");
ui.selectable_value(&mut self.typ, SongType::Soundcloud, "Soundcloud");
}
);
});
ui.horizontal(|ui| {
ui.label("Name: ");
ui.text_edit_singleline(&mut self.ed_name);
ui.text_edit_singleline(&mut self.name);
});
ui.horizontal(|ui| {
ui.label("Playlist: ");
egui::ComboBox::from_id_source("new_song_window_playlist")
.selected_text(format!("{}", self.ed_playlist.clone().unwrap_or("".to_string())))
.selected_text(self.playlist.clone().unwrap_or_default())
.show_ui(ui, |ui| {
for p in state.manifest.get_playlists().keys() {
ui.selectable_value(&mut self.ed_playlist, Option::Some(p.clone()), p.as_str());
ui.selectable_value(&mut self.playlist, Option::Some(p.clone()), p.as_str());
}
}
);
});
ui.horizontal(|ui| {
ui.label("Url: ");
ui.text_edit_singleline(&mut self.ed_url);
ui.text_edit_singleline(&mut self.url);
});
if ui.button("Save").clicked() {
@ -53,13 +53,13 @@ impl Window for GuiNewSong {
});
if save {
let Some(playlist) = state.manifest.get_playlist_mut(&self.ed_playlist.clone().unwrap()) else {
let Some(playlist) = state.manifest.get_playlist_mut(&self.playlist.clone().unwrap()) else {
panic!("couldnt find playlist from a preset playlist list????????????");
};
playlist.add_song(
self.ed_name.clone(),
Song::from_url_str(self.ed_url.clone()).unwrap().set_type(self.ed_type.clone()).clone()
self.name.clone(),
Song::from_url_str(self.url.clone()).unwrap().set_type(self.typ.clone()).clone()
);

View File

@ -2,16 +2,12 @@ use std::{any::Any, path::PathBuf};
use crate::{constants, manifest::Format};
pub(crate) fn is_supported_host(url: url::Url) -> bool {
pub(crate) fn is_supported_host(url: &url::Url) -> bool {
let host = url.host_str();
if host.is_none() {
return false;
}
match host.unwrap() {
"youtube.com" | "youtu.be" |
"open.spotify.com" => true,
_ => false
}
matches!(host.unwrap(), "youtube.com" | "youtu.be" | "open.spotify.com")
}
pub(crate) fn is_program_in_path(program: &str) -> Option<PathBuf> {
@ -66,7 +62,7 @@ pub fn get_song_path/*<P: TryInto<PathBuf>>*/(/*basepath: Option<P>,*/ pname: &S
path = std::env::current_dir().unwrap_or(PathBuf::new());
}
} else {*/
let mut path = std::env::current_dir().unwrap_or(PathBuf::new());
let mut path = std::env::current_dir().unwrap_or_default();
//}
// TODO: Get this from cfg
path.push("out");