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 std::str::FromStr;
|
||||||
|
|
||||||
|
use anyhow::bail;
|
||||||
|
|
||||||
use crate::{config::ConfigWrapper, downloader::Downloader, manifest::{song::Song, Manifest}, util::is_supported_host};
|
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<()> {
|
pub async fn add(cfg: &ConfigWrapper, manifest: &mut Manifest, downloader: &mut Downloader, url: &String, name: &String, playlist: &String) -> anyhow::Result<()> {
|
||||||
|
|
||||||
log::debug!("Playlist: {playlist:?}");
|
|
||||||
log::debug!("url: {url:?}");
|
|
||||||
log::debug!("name: {name:?}");
|
|
||||||
|
|
||||||
let mut playlists = manifest.get_playlists().keys().map(|f| f.clone()).collect::<Vec<String>>();
|
let mut playlists = manifest.get_playlists().keys().map(|f| f.clone()).collect::<Vec<String>>();
|
||||||
|
|
||||||
playlists.sort();
|
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)?) {
|
if !is_supported_host(url::Url::from_str(&url)?) {
|
||||||
log::error!("Invalid or unsupported host name");
|
log::error!("Invalid or unsupported host name");
|
||||||
return Ok(());
|
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)?;
|
let song = Song::from_url_str(url.clone())?;
|
||||||
manifest.add_song(playlist.clone(), name.clone(), song.clone());
|
manifest.add_song(playlist, name.clone(), song.clone());
|
||||||
manifest.save(None)?;
|
manifest.save(None)?;
|
||||||
|
|
||||||
let should_download = crate::prompt::prompt_bool("Download song now?", Some(false));
|
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(())
|
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 (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;
|
return;
|
||||||
};
|
};
|
||||||
let song = song.clone();
|
let song = song.clone();
|
||||||
|
@ -65,14 +65,14 @@ impl Gui {
|
||||||
|
|
||||||
if save {
|
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;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
*song.get_url_str_mut() = self.song_editor.ed_url.clone();
|
*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;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -126,7 +126,7 @@ impl Gui {
|
||||||
});
|
});
|
||||||
|
|
||||||
if save {
|
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????????????");
|
panic!("couldnt find playlist from a preset playlist list????????????");
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -137,7 +137,6 @@ impl Gui {
|
||||||
|
|
||||||
|
|
||||||
let _ = self.manifest.save(None);
|
let _ = self.manifest.save(None);
|
||||||
save = false;
|
|
||||||
self.song_editor.is_new_open = 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), _) => {
|
(Some(c), _) => {
|
||||||
match c {
|
match c {
|
||||||
CliCommand::Download => unreachable!(),
|
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 } => {
|
CliCommand::Add { url, name, playlist } => {
|
||||||
if let Err(e) = add::add(cfg, manifest, &mut downloader, url, name, playlist).await {
|
if let Err(e) = add::add(cfg, manifest, &mut downloader, url, name, playlist).await {
|
||||||
log::error!("Failed to run 'add' command: {e}");
|
log::error!("Failed to run 'add' command: {e}");
|
||||||
|
|
|
@ -30,11 +30,17 @@ pub enum CliCommand {
|
||||||
Download,
|
Download,
|
||||||
Add {
|
Add {
|
||||||
#[arg(long, short)]
|
#[arg(long, short)]
|
||||||
url: Option<String>,
|
url: String,
|
||||||
#[arg(long, short)]
|
#[arg(long, short)]
|
||||||
name: Option<String>,
|
name: String,
|
||||||
#[arg(long, short)]
|
#[arg(long, short)]
|
||||||
playlist: Option<String>
|
playlist: String
|
||||||
|
},
|
||||||
|
AddPlaylist {
|
||||||
|
#[arg(long, short)]
|
||||||
|
url: String,
|
||||||
|
#[arg(long, short)]
|
||||||
|
name: String
|
||||||
},
|
},
|
||||||
Gui
|
Gui
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,7 +40,7 @@ impl Downloader {
|
||||||
self.nb_cache.len()
|
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 (pname, playlist) in manifest.get_playlists() {
|
||||||
for (sname, song) in playlist.get_songs() {
|
for (sname, song) in playlist.get_songs() {
|
||||||
self.nb_cache.push((pname.clone(), sname.clone(), song.clone(), manifest.get_format().clone()));
|
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)
|
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 !crate::process_manager::is_proc_queue_full(10) {
|
||||||
if let Some((pname, sname, song, format)) = self.nb_cache.pop() {
|
if let Some((pname, sname, song, format)) = self.nb_cache.pop() {
|
||||||
self.download_song(cfg, &sname, &song, &pname, &format)?;
|
self.download_song(cfg, &sname, &song, &pname, &format)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if self.nb_cache.is_empty() {
|
||||||
Ok(crate::process_manager::purge_done_procs())
|
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)
|
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<()> {
|
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_dir = format!("{}/{playlist}", cfg.cli.output);
|
||||||
let dl_file = format!("{dl_dir}/{}.{}", name, &format);
|
let dl_file = format!("{dl_dir}/{}.{}", name, &format);
|
||||||
|
|
|
@ -43,23 +43,23 @@ impl Manifest {
|
||||||
pub fn get_format(&self) -> &Format {
|
pub fn get_format(&self) -> &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)
|
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)
|
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)
|
self.get_playlist_mut(playlist_name)?.get_song_mut(name)
|
||||||
}
|
}
|
||||||
pub fn add_playlist(&mut self, playlist_name: String) {
|
pub fn add_playlist(&mut self, playlist_name: String) {
|
||||||
self.playlists.insert(playlist_name, Default::default());
|
self.playlists.insert(playlist_name, Default::default());
|
||||||
}
|
}
|
||||||
pub fn get_playlist(&self, playlist_name: String) -> Option<&playlist::Playlist> {
|
pub fn get_playlist(&self, playlist_name: &String) -> Option<&playlist::Playlist> {
|
||||||
self.playlists.get(&playlist_name)
|
self.playlists.get(playlist_name)
|
||||||
}
|
}
|
||||||
pub fn get_playlist_mut(&mut self, playlist_name: String) -> Option<&mut playlist::Playlist> {
|
pub fn get_playlist_mut(&mut self, playlist_name: &String) -> Option<&mut playlist::Playlist> {
|
||||||
self.playlists.get_mut(&playlist_name)
|
self.playlists.get_mut(playlist_name)
|
||||||
}
|
}
|
||||||
pub fn get_playlists(&self) -> &HashMap<String, playlist::Playlist> {
|
pub fn get_playlists(&self) -> &HashMap<String, playlist::Playlist> {
|
||||||
&self.playlists
|
&self.playlists
|
||||||
|
|
|
@ -38,6 +38,9 @@ pub fn add_proc(mut cmd: Command, msg: String) -> anyhow::Result<()> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn proc_count() -> usize {
|
||||||
|
PROCESSES.lock().unwrap().read().unwrap().len()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn is_proc_queue_full(max: usize) -> bool {
|
pub fn is_proc_queue_full(max: usize) -> bool {
|
||||||
let proc_cnt = PROCESSES.lock().unwrap().read().unwrap().len();
|
let proc_cnt = PROCESSES.lock().unwrap().read().unwrap().len();
|
||||||
|
|
Loading…
Reference in New Issue
Block a user