Clippy pedantic fixes
This commit is contained in:
		
							parent
							
								
									387a5eaf4a
								
							
						
					
					
						commit
						f7008e882c
					
				
							
								
								
									
										
											BIN
										
									
								
								assets/icon.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								assets/icon.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 8.4 KiB  | 
| 
						 | 
				
			
			@ -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,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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 {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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::*;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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(());
 | 
			
		||||
            }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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 {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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)
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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 {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										109
									
								
								src/prompt.rs
									
									
									
									
									
								
							
							
						
						
									
										109
									
								
								src/prompt.rs
									
									
									
									
									
								
							| 
						 | 
				
			
			@ -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)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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() {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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}");
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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));
 | 
			
		||||
 | 
			
		||||
                    })
 | 
			
		||||
                    });
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
        });
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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");
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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> {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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()
 | 
			
		||||
            );
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										10
									
								
								src/util.rs
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								src/util.rs
									
									
									
									
									
								
							| 
						 | 
				
			
			@ -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");
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue
	
	Block a user