Added playlist downloading to cli
This commit is contained in:
		
							parent
							
								
									52a55d8be2
								
							
						
					
					
						commit
						29c7e452b0
					
				
							
								
								
									
										1126
									
								
								manifest.json
									
									
									
									
									
								
							
							
						
						
									
										1126
									
								
								manifest.json
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| 
						 | 
				
			
			@ -1,42 +1,26 @@
 | 
			
		|||
use std::str::FromStr;
 | 
			
		||||
 | 
			
		||||
use anyhow::bail;
 | 
			
		||||
 | 
			
		||||
use crate::{config::ConfigWrapper, downloader::Downloader, manifest::{song::Song, Manifest}, util::is_supported_host};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
pub async fn add(cfg: &ConfigWrapper, manifest: &mut Manifest, downloader: &mut Downloader, url: &Option<String>, name: &Option<String>, playlist: &Option<String>) -> anyhow::Result<()> {
 | 
			
		||||
    
 | 
			
		||||
    log::debug!("Playlist: {playlist:?}");
 | 
			
		||||
    log::debug!("url: {url:?}");
 | 
			
		||||
    log::debug!("name: {name:?}");
 | 
			
		||||
pub async fn add(cfg: &ConfigWrapper, manifest: &mut Manifest, downloader: &mut Downloader, url: &String, name: &String, playlist: &String) -> anyhow::Result<()> {
 | 
			
		||||
    
 | 
			
		||||
    let mut playlists = manifest.get_playlists().keys().map(|f| f.clone()).collect::<Vec<String>>();
 | 
			
		||||
 | 
			
		||||
    playlists.sort();
 | 
			
		||||
 | 
			
		||||
    let playlist = playlist.clone().unwrap_or_else( || {
 | 
			
		||||
        let g = crate::prompt::prompt_with_list_or_str("Enter song playlist", &playlists);
 | 
			
		||||
        log::info!("Playlist: {g}");
 | 
			
		||||
        g
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    let url = url.clone().unwrap_or_else( ||
 | 
			
		||||
        crate::prompt::simple_prompt("Enter song youtube url, make sure its not a playlist, (yt only for now)")
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    if !is_supported_host(url::Url::from_str(&url)?) {
 | 
			
		||||
        log::error!("Invalid or unsupported host name");
 | 
			
		||||
        return Ok(());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    let name = name.clone().unwrap_or_else( ||
 | 
			
		||||
        crate::prompt::simple_prompt("Enter song name with like this: {Author} - {Song name}")
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    let song = Song::from_url_str(url)?;
 | 
			
		||||
    manifest.add_song(playlist.clone(), name.clone(), song.clone());
 | 
			
		||||
    let song = Song::from_url_str(url.clone())?;
 | 
			
		||||
    manifest.add_song(playlist, name.clone(), song.clone());
 | 
			
		||||
    manifest.save(None)?;
 | 
			
		||||
 | 
			
		||||
    let should_download = crate::prompt::prompt_bool("Download song now?", Some(false));
 | 
			
		||||
| 
						 | 
				
			
			@ -48,3 +32,24 @@ pub async fn add(cfg: &ConfigWrapper, manifest: &mut Manifest, downloader: &mut
 | 
			
		|||
 | 
			
		||||
    Ok(())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub async fn add_playlist(cfg: &ConfigWrapper, manifest: &mut Manifest, downloader: &mut Downloader, url: &String, name: &String) -> anyhow::Result<()> {
 | 
			
		||||
    let songs = downloader.download_playlist_nb(cfg, url, name, manifest.get_format())?;
 | 
			
		||||
 | 
			
		||||
    if manifest.get_playlist(name).is_some() {
 | 
			
		||||
        log::error!("Playlist {name} already exists");
 | 
			
		||||
        bail!("")
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    manifest.add_playlist(name.clone());
 | 
			
		||||
 | 
			
		||||
    let playlist = manifest.get_playlist_mut(name).expect("Unreachable");
 | 
			
		||||
    
 | 
			
		||||
    for (sname, song) in songs {
 | 
			
		||||
        playlist.add_song(sname, song);
 | 
			
		||||
    }
 | 
			
		||||
    manifest.save(None)?;
 | 
			
		||||
 | 
			
		||||
    while downloader.download_all_nb_poll(cfg)?.is_some() {};
 | 
			
		||||
    Ok(())
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -24,7 +24,7 @@ impl Gui {
 | 
			
		|||
        
 | 
			
		||||
        let (playlist, song_name) = self.song_editor.song.clone();
 | 
			
		||||
 | 
			
		||||
        let Some(song) = self.manifest.get_song(playlist.clone(), &song_name) else {
 | 
			
		||||
        let Some(song) = self.manifest.get_song(&playlist, &song_name) else {
 | 
			
		||||
            return;
 | 
			
		||||
        };
 | 
			
		||||
        let song = song.clone();
 | 
			
		||||
| 
						 | 
				
			
			@ -65,14 +65,14 @@ impl Gui {
 | 
			
		|||
 | 
			
		||||
        if save {
 | 
			
		||||
            {
 | 
			
		||||
                let Some(song) = self.manifest.get_song_mut(playlist.clone(), &song_name) else {
 | 
			
		||||
                let Some(song) = self.manifest.get_song_mut(&playlist, &song_name) else {
 | 
			
		||||
                    return;
 | 
			
		||||
                };
 | 
			
		||||
                
 | 
			
		||||
                *song.get_url_str_mut() = self.song_editor.ed_url.clone();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            let Some(playlist) = self.manifest.get_playlist_mut(playlist.clone()) else {
 | 
			
		||||
            let Some(playlist) = self.manifest.get_playlist_mut(&playlist) else {
 | 
			
		||||
                return;
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -126,7 +126,7 @@ impl Gui {
 | 
			
		|||
        });
 | 
			
		||||
 | 
			
		||||
        if save {
 | 
			
		||||
            let Some(playlist) = self.manifest.get_playlist_mut(self.song_editor.ed_playlist.clone().unwrap())  else {
 | 
			
		||||
            let Some(playlist) = self.manifest.get_playlist_mut(&self.song_editor.ed_playlist.clone().unwrap())  else {
 | 
			
		||||
                panic!("couldnt find playlist from a preset playlist list????????????");
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -137,7 +137,6 @@ impl Gui {
 | 
			
		|||
 | 
			
		||||
 | 
			
		||||
            let _ = self.manifest.save(None);
 | 
			
		||||
            save = false;
 | 
			
		||||
            self.song_editor.is_new_open = false;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -23,6 +23,11 @@ pub async fn command_run(cfg: &ConfigWrapper, manifest: &mut Manifest) -> anyhow
 | 
			
		|||
        (Some(c), _) => {
 | 
			
		||||
            match c {
 | 
			
		||||
                CliCommand::Download => unreachable!(),
 | 
			
		||||
                CliCommand::AddPlaylist { url, name } => {
 | 
			
		||||
                    if let Err(e) = add::add_playlist(cfg, manifest, &mut downloader, url, name).await {
 | 
			
		||||
                        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 {
 | 
			
		||||
                        log::error!("Failed to run 'add' command: {e}");
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -30,11 +30,17 @@ pub enum CliCommand {
 | 
			
		|||
    Download,
 | 
			
		||||
    Add {
 | 
			
		||||
        #[arg(long, short)]
 | 
			
		||||
        url: Option<String>,
 | 
			
		||||
        url: String,
 | 
			
		||||
        #[arg(long, short)]
 | 
			
		||||
        name: Option<String>,
 | 
			
		||||
        name: String,
 | 
			
		||||
        #[arg(long, short)]
 | 
			
		||||
        playlist: Option<String>
 | 
			
		||||
        playlist: String
 | 
			
		||||
    },
 | 
			
		||||
    AddPlaylist {
 | 
			
		||||
        #[arg(long, short)]
 | 
			
		||||
        url: String,
 | 
			
		||||
        #[arg(long, short)]
 | 
			
		||||
        name: String
 | 
			
		||||
    },
 | 
			
		||||
    Gui
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -40,7 +40,7 @@ impl Downloader {
 | 
			
		|||
        self.nb_cache.len()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn download_all_nb(&mut self, manifest: &Manifest, cfg: &ConfigWrapper) -> anyhow::Result<usize> {
 | 
			
		||||
    pub fn download_all_nb(&mut self, manifest: &Manifest, cfg: &ConfigWrapper) -> anyhow::Result<Option<usize>> {
 | 
			
		||||
        for (pname, playlist) in manifest.get_playlists() {
 | 
			
		||||
            for (sname, song) in playlist.get_songs() {
 | 
			
		||||
                self.nb_cache.push((pname.clone(), sname.clone(), song.clone(), manifest.get_format().clone()));
 | 
			
		||||
| 
						 | 
				
			
			@ -51,14 +51,20 @@ impl Downloader {
 | 
			
		|||
        self.download_all_nb_poll(cfg)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn download_all_nb_poll(&mut self, cfg: &ConfigWrapper) -> anyhow::Result<usize> {
 | 
			
		||||
    pub fn download_all_nb_poll(&mut self, cfg: &ConfigWrapper) -> anyhow::Result<Option<usize>> {
 | 
			
		||||
        if !crate::process_manager::is_proc_queue_full(10)  {
 | 
			
		||||
            if let Some((pname, sname, song, format)) = self.nb_cache.pop() {
 | 
			
		||||
                self.download_song(cfg, &sname, &song, &pname, &format)?;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        Ok(crate::process_manager::purge_done_procs())
 | 
			
		||||
        if self.nb_cache.is_empty() {
 | 
			
		||||
            self.nb_initial_song_count = 0;
 | 
			
		||||
        }
 | 
			
		||||
        if crate::process_manager::proc_count() == 0 && self.nb_cache.is_empty() {
 | 
			
		||||
            Ok(None)
 | 
			
		||||
        } else {
 | 
			
		||||
            Ok(Some(crate::process_manager::purge_done_procs()))
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -76,6 +82,47 @@ impl Downloader {
 | 
			
		|||
        Ok(self.count)
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    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;
 | 
			
		||||
        while let Some(c) = self.download_all_nb_poll(cfg)? {
 | 
			
		||||
            count += c;
 | 
			
		||||
        }
 | 
			
		||||
        Ok(count)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn download_playlist_nb(&mut self, cfg: &ConfigWrapper, url: &String, pname: &String, 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()
 | 
			
		||||
        ]);
 | 
			
		||||
        cmd
 | 
			
		||||
            .stderr(Stdio::null())
 | 
			
		||||
            .stdout(Stdio::piped());
 | 
			
		||||
 | 
			
		||||
        let ftr = cmd.output();
 | 
			
		||||
        
 | 
			
		||||
        let mut ret = HashMap::new();
 | 
			
		||||
 | 
			
		||||
        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 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()));
 | 
			
		||||
            ret.insert(sname, song.clone());
 | 
			
		||||
        }
 | 
			
		||||
        self.nb_initial_song_count += out.lines().count();
 | 
			
		||||
        self.download_all_nb_poll(cfg)?;
 | 
			
		||||
        Ok(ret)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -43,23 +43,23 @@ impl Manifest {
 | 
			
		|||
    pub fn get_format(&self) -> &Format {
 | 
			
		||||
        &self.format
 | 
			
		||||
    }
 | 
			
		||||
    pub fn add_song(&mut self, playlist_name: String, name: SongName, song: Song) -> Option<Song> {
 | 
			
		||||
    pub fn add_song(&mut self, playlist_name: &String, name: SongName, song: Song) -> Option<Song> {
 | 
			
		||||
        self.get_playlist_mut(playlist_name)?.add_song(name, song)
 | 
			
		||||
    }
 | 
			
		||||
    pub fn get_song(&self, playlist_name: String, name: &String) -> Option<&Song> {
 | 
			
		||||
    pub fn get_song(&self, playlist_name: &String, name: &String) -> Option<&Song> {
 | 
			
		||||
        self.get_playlist(playlist_name)?.get_song(name)
 | 
			
		||||
    }
 | 
			
		||||
    pub fn get_song_mut(&mut self, playlist_name: String, name: &String) -> Option<&mut Song> {
 | 
			
		||||
    pub fn get_song_mut(&mut self, playlist_name: &String, name: &String) -> Option<&mut Song> {
 | 
			
		||||
        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());
 | 
			
		||||
    }
 | 
			
		||||
    pub fn get_playlist(&self, playlist_name: String) -> Option<&playlist::Playlist> {
 | 
			
		||||
        self.playlists.get(&playlist_name)
 | 
			
		||||
    pub fn get_playlist(&self, playlist_name: &String) -> Option<&playlist::Playlist> {
 | 
			
		||||
        self.playlists.get(playlist_name)
 | 
			
		||||
    }
 | 
			
		||||
    pub fn get_playlist_mut(&mut self, playlist_name: String) -> Option<&mut playlist::Playlist> {
 | 
			
		||||
        self.playlists.get_mut(&playlist_name)
 | 
			
		||||
    pub fn get_playlist_mut(&mut self, playlist_name: &String) -> Option<&mut playlist::Playlist> {
 | 
			
		||||
        self.playlists.get_mut(playlist_name)
 | 
			
		||||
    }
 | 
			
		||||
    pub fn get_playlists(&self) -> &HashMap<String, playlist::Playlist> {
 | 
			
		||||
        &self.playlists
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -38,6 +38,9 @@ pub fn add_proc(mut cmd: Command, msg: String) -> anyhow::Result<()> {
 | 
			
		|||
    Ok(())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn proc_count() -> usize {
 | 
			
		||||
    PROCESSES.lock().unwrap().read().unwrap().len()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn is_proc_queue_full(max: usize) -> bool {
 | 
			
		||||
    let proc_cnt = PROCESSES.lock().unwrap().read().unwrap().len();
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue
	
	Block a user