feat: print error message
This commit is contained in:
parent
9da3b25be2
commit
8f16e5ba6e
1 changed files with 75 additions and 81 deletions
156
cli/src/main.rs
156
cli/src/main.rs
|
|
@ -1,7 +1,12 @@
|
||||||
#![doc = include_str!("../README.md")]
|
#![doc = include_str!("../README.md")]
|
||||||
#![warn(clippy::todo, clippy::dbg_macro)]
|
#![warn(clippy::todo, clippy::dbg_macro)]
|
||||||
|
|
||||||
use std::{path::PathBuf, str::FromStr, time::Duration};
|
use std::{
|
||||||
|
path::PathBuf,
|
||||||
|
str::FromStr,
|
||||||
|
sync::{atomic::AtomicUsize, Arc},
|
||||||
|
time::Duration,
|
||||||
|
};
|
||||||
|
|
||||||
use clap::{Parser, Subcommand, ValueEnum};
|
use clap::{Parser, Subcommand, ValueEnum};
|
||||||
use futures::stream::{self, StreamExt};
|
use futures::stream::{self, StreamExt};
|
||||||
|
|
@ -177,6 +182,7 @@ enum Commands {
|
||||||
#[clap(long)]
|
#[clap(long)]
|
||||||
music: Option<MusicSearchCategory>,
|
music: Option<MusicSearchCategory>,
|
||||||
},
|
},
|
||||||
|
/// Get a YouTube visitor data cookie
|
||||||
Vdata,
|
Vdata,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -437,7 +443,7 @@ async fn download_videos(
|
||||||
parallel: usize,
|
parallel: usize,
|
||||||
client_type: Option<PlayerType>,
|
client_type: Option<PlayerType>,
|
||||||
multi: MultiProgress,
|
multi: MultiProgress,
|
||||||
) {
|
) -> anyhow::Result<()> {
|
||||||
// Indicatif setup
|
// Indicatif setup
|
||||||
let main = multi.add(ProgressBar::new(
|
let main = multi.add(ProgressBar::new(
|
||||||
videos.len().try_into().unwrap_or_default(),
|
videos.len().try_into().unwrap_or_default(),
|
||||||
|
|
@ -451,11 +457,14 @@ async fn download_videos(
|
||||||
);
|
);
|
||||||
main.tick();
|
main.tick();
|
||||||
|
|
||||||
|
let n_failed = Arc::new(AtomicUsize::default());
|
||||||
|
|
||||||
stream::iter(videos)
|
stream::iter(videos)
|
||||||
.for_each_concurrent(parallel, |video| {
|
.for_each_concurrent(parallel, |video| {
|
||||||
let dl = dl.clone();
|
let dl = dl.clone();
|
||||||
let main = main.clone();
|
let main = main.clone();
|
||||||
let id = video.id().to_owned();
|
let id = video.id().to_owned();
|
||||||
|
let n_failed = n_failed.clone();
|
||||||
|
|
||||||
let mut q = target.apply(dl.video(video));
|
let mut q = target.apply(dl.video(video));
|
||||||
if let Some(client_type) = client_type {
|
if let Some(client_type) = client_type {
|
||||||
|
|
@ -466,12 +475,20 @@ async fn download_videos(
|
||||||
if let Err(e) = q.download().await {
|
if let Err(e) = q.download().await {
|
||||||
if !matches!(e, DownloadError::Exists(_)) {
|
if !matches!(e, DownloadError::Exists(_)) {
|
||||||
tracing::error!("[{id}]: {e}");
|
tracing::error!("[{id}]: {e}");
|
||||||
|
n_failed.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
main.inc(1);
|
||||||
}
|
}
|
||||||
main.inc(1);
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
|
let n_failed = n_failed.load(std::sync::atomic::Ordering::Relaxed);
|
||||||
|
if n_failed > 0 {
|
||||||
|
anyhow::bail!("{n_failed} downloads failed");
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Stderr writer that suspends the progress bars before printing logs
|
/// Stderr writer that suspends the progress bars before printing logs
|
||||||
|
|
@ -498,6 +515,14 @@ impl std::io::Write for ProgWriter {
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
|
if let Err(e) = run().await {
|
||||||
|
println!("{}", "Error:".red().bold());
|
||||||
|
println!("{}", e);
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn run() -> anyhow::Result<()> {
|
||||||
let cli = Cli::parse();
|
let cli = Cli::parse();
|
||||||
let multi = MultiProgress::new();
|
let multi = MultiProgress::new();
|
||||||
|
|
||||||
|
|
@ -527,7 +552,7 @@ async fn main() {
|
||||||
if let Some(country) = cli.country {
|
if let Some(country) = cli.country {
|
||||||
rp = rp.country(Country::from_str(&country.to_ascii_uppercase()).expect("invalid country"));
|
rp = rp.country(Country::from_str(&country.to_ascii_uppercase()).expect("invalid country"));
|
||||||
}
|
}
|
||||||
let rp = rp.build().unwrap();
|
let rp = rp.build()?;
|
||||||
|
|
||||||
match cli.command {
|
match cli.command {
|
||||||
Commands::Download {
|
Commands::Download {
|
||||||
|
|
@ -541,7 +566,7 @@ async fn main() {
|
||||||
client_type,
|
client_type,
|
||||||
pot,
|
pot,
|
||||||
} => {
|
} => {
|
||||||
let url_target = rp.query().resolve_string(&id, false).await.unwrap();
|
let url_target = rp.query().resolve_string(&id, false).await?;
|
||||||
|
|
||||||
let mut filter = StreamFilter::new();
|
let mut filter = StreamFilter::new();
|
||||||
if let Some(res) = resolution {
|
if let Some(res) = resolution {
|
||||||
|
|
@ -570,12 +595,8 @@ async fn main() {
|
||||||
}
|
}
|
||||||
UrlTarget::Channel { id } => {
|
UrlTarget::Channel { id } => {
|
||||||
target.assert_dir();
|
target.assert_dir();
|
||||||
let mut channel = rp.query().channel_videos(id).await.unwrap();
|
let mut channel = rp.query().channel_videos(id).await?;
|
||||||
channel
|
channel.content.extend_limit(&rp.query(), limit).await?;
|
||||||
.content
|
|
||||||
.extend_limit(&rp.query(), limit)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
let videos = channel
|
let videos = channel
|
||||||
.content
|
.content
|
||||||
.items
|
.items
|
||||||
|
|
@ -583,17 +604,13 @@ async fn main() {
|
||||||
.take(limit)
|
.take(limit)
|
||||||
.map(|v| DownloadVideo::from_entity(&v))
|
.map(|v| DownloadVideo::from_entity(&v))
|
||||||
.collect();
|
.collect();
|
||||||
download_videos(&dl, videos, &target, parallel, client_type, multi).await;
|
download_videos(&dl, videos, &target, parallel, client_type, multi).await?;
|
||||||
}
|
}
|
||||||
UrlTarget::Playlist { id } => {
|
UrlTarget::Playlist { id } => {
|
||||||
target.assert_dir();
|
target.assert_dir();
|
||||||
let videos = if music {
|
let videos = if music {
|
||||||
let mut playlist = rp.query().music_playlist(id).await.unwrap();
|
let mut playlist = rp.query().music_playlist(id).await?;
|
||||||
playlist
|
playlist.tracks.extend_limit(&rp.query(), limit).await?;
|
||||||
.tracks
|
|
||||||
.extend_limit(&rp.query(), limit)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
playlist
|
playlist
|
||||||
.tracks
|
.tracks
|
||||||
.items
|
.items
|
||||||
|
|
@ -602,12 +619,8 @@ async fn main() {
|
||||||
.map(|v| DownloadVideo::from_track(&v))
|
.map(|v| DownloadVideo::from_track(&v))
|
||||||
.collect()
|
.collect()
|
||||||
} else {
|
} else {
|
||||||
let mut playlist = rp.query().playlist(id).await.unwrap();
|
let mut playlist = rp.query().playlist(id).await?;
|
||||||
playlist
|
playlist.videos.extend_limit(&rp.query(), limit).await?;
|
||||||
.videos
|
|
||||||
.extend_limit(&rp.query(), limit)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
playlist
|
playlist
|
||||||
.videos
|
.videos
|
||||||
.items
|
.items
|
||||||
|
|
@ -616,18 +629,18 @@ async fn main() {
|
||||||
.map(|v| DownloadVideo::from_entity(&v))
|
.map(|v| DownloadVideo::from_entity(&v))
|
||||||
.collect()
|
.collect()
|
||||||
};
|
};
|
||||||
download_videos(&dl, videos, &target, parallel, client_type, multi).await;
|
download_videos(&dl, videos, &target, parallel, client_type, multi).await?;
|
||||||
}
|
}
|
||||||
UrlTarget::Album { id } => {
|
UrlTarget::Album { id } => {
|
||||||
target.assert_dir();
|
target.assert_dir();
|
||||||
let album = rp.query().music_album(id).await.unwrap();
|
let album = rp.query().music_album(id).await?;
|
||||||
let videos = album
|
let videos = album
|
||||||
.tracks
|
.tracks
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.take(limit)
|
.take(limit)
|
||||||
.map(|v| DownloadVideo::from_track(&v))
|
.map(|v| DownloadVideo::from_track(&v))
|
||||||
.collect();
|
.collect();
|
||||||
download_videos(&dl, videos, &target, parallel, client_type, multi).await;
|
download_videos(&dl, videos, &target, parallel, client_type, multi).await?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -644,15 +657,15 @@ async fn main() {
|
||||||
player,
|
player,
|
||||||
client_type,
|
client_type,
|
||||||
} => {
|
} => {
|
||||||
let target = rp.query().resolve_string(&id, false).await.unwrap();
|
let target = rp.query().resolve_string(&id, false).await?;
|
||||||
|
|
||||||
match target {
|
match target {
|
||||||
UrlTarget::Video { id, .. } => {
|
UrlTarget::Video { id, .. } => {
|
||||||
if lyrics {
|
if lyrics {
|
||||||
let details = rp.query().music_details(&id).await.unwrap();
|
let details = rp.query().music_details(&id).await?;
|
||||||
match details.lyrics_id {
|
match details.lyrics_id {
|
||||||
Some(lyrics_id) => {
|
Some(lyrics_id) => {
|
||||||
let lyrics = rp.query().music_lyrics(lyrics_id).await.unwrap();
|
let lyrics = rp.query().music_lyrics(lyrics_id).await?;
|
||||||
if txt {
|
if txt {
|
||||||
println!("{}\n\n{}", lyrics.body, lyrics.footer);
|
println!("{}\n\n{}", lyrics.body, lyrics.footer);
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -662,7 +675,7 @@ async fn main() {
|
||||||
None => eprintln!("no lyrics found"),
|
None => eprintln!("no lyrics found"),
|
||||||
}
|
}
|
||||||
} else if music {
|
} else if music {
|
||||||
let details = rp.query().music_details(&id).await.unwrap();
|
let details = rp.query().music_details(&id).await?;
|
||||||
if txt {
|
if txt {
|
||||||
if details.track.is_video {
|
if details.track.is_video {
|
||||||
println!("[MV]");
|
println!("[MV]");
|
||||||
|
|
@ -696,26 +709,20 @@ async fn main() {
|
||||||
rp.query().player_from_client(&id, client_type.into()).await
|
rp.query().player_from_client(&id, client_type.into()).await
|
||||||
} else {
|
} else {
|
||||||
rp.query().player(&id).await
|
rp.query().player(&id).await
|
||||||
}
|
}?;
|
||||||
.unwrap();
|
|
||||||
print_data(&player, format, pretty);
|
print_data(&player, format, pretty);
|
||||||
} else {
|
} else {
|
||||||
let mut details = rp.query().video_details(&id).await.unwrap();
|
let mut details = rp.query().video_details(&id).await?;
|
||||||
|
|
||||||
match comments {
|
match comments {
|
||||||
Some(CommentsOrder::Top) => {
|
Some(CommentsOrder::Top) => {
|
||||||
details
|
details.top_comments.extend_limit(rp.query(), limit).await?;
|
||||||
.top_comments
|
|
||||||
.extend_limit(rp.query(), limit)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
}
|
}
|
||||||
Some(CommentsOrder::Latest) => {
|
Some(CommentsOrder::Latest) => {
|
||||||
details
|
details
|
||||||
.latest_comments
|
.latest_comments
|
||||||
.extend_limit(rp.query(), limit)
|
.extend_limit(rp.query(), limit)
|
||||||
.await
|
.await?;
|
||||||
.unwrap();
|
|
||||||
}
|
}
|
||||||
None => {}
|
None => {}
|
||||||
}
|
}
|
||||||
|
|
@ -793,7 +800,7 @@ async fn main() {
|
||||||
}
|
}
|
||||||
UrlTarget::Channel { id } => {
|
UrlTarget::Channel { id } => {
|
||||||
if music {
|
if music {
|
||||||
let artist = rp.query().music_artist(&id, true).await.unwrap();
|
let artist = rp.query().music_artist(&id, true).await?;
|
||||||
if txt {
|
if txt {
|
||||||
anstream::println!(
|
anstream::println!(
|
||||||
"{}\n{} [{}]",
|
"{}\n{} [{}]",
|
||||||
|
|
@ -853,13 +860,9 @@ async fn main() {
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
};
|
};
|
||||||
let mut channel =
|
let mut channel =
|
||||||
rp.query().channel_videos_tab(&id, video_tab).await.unwrap();
|
rp.query().channel_videos_tab(&id, video_tab).await?;
|
||||||
|
|
||||||
channel
|
channel.content.extend_limit(rp.query(), limit).await?;
|
||||||
.content
|
|
||||||
.extend_limit(rp.query(), limit)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
if txt {
|
if txt {
|
||||||
anstream::print!(
|
anstream::print!(
|
||||||
|
|
@ -881,7 +884,7 @@ async fn main() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ChannelTab::Playlists => {
|
ChannelTab::Playlists => {
|
||||||
let channel = rp.query().channel_playlists(&id).await.unwrap();
|
let channel = rp.query().channel_playlists(&id).await?;
|
||||||
|
|
||||||
if txt {
|
if txt {
|
||||||
anstream::println!(
|
anstream::println!(
|
||||||
|
|
@ -901,7 +904,7 @@ async fn main() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ChannelTab::Info => {
|
ChannelTab::Info => {
|
||||||
let info = rp.query().channel_info(&id).await.unwrap();
|
let info = rp.query().channel_info(&id).await?;
|
||||||
|
|
||||||
if txt {
|
if txt {
|
||||||
anstream::println!(
|
anstream::println!(
|
||||||
|
|
@ -937,12 +940,8 @@ async fn main() {
|
||||||
}
|
}
|
||||||
UrlTarget::Playlist { id } => {
|
UrlTarget::Playlist { id } => {
|
||||||
if music {
|
if music {
|
||||||
let mut playlist = rp.query().music_playlist(&id).await.unwrap();
|
let mut playlist = rp.query().music_playlist(&id).await?;
|
||||||
playlist
|
playlist.tracks.extend_limit(rp.query(), limit).await?;
|
||||||
.tracks
|
|
||||||
.extend_limit(rp.query(), limit)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
if txt {
|
if txt {
|
||||||
anstream::println!(
|
anstream::println!(
|
||||||
"{}\n{} [{}]\n{} {}",
|
"{}\n{} [{}]\n{} {}",
|
||||||
|
|
@ -966,12 +965,8 @@ async fn main() {
|
||||||
print_data(&playlist, format, pretty);
|
print_data(&playlist, format, pretty);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let mut playlist = rp.query().playlist(&id).await.unwrap();
|
let mut playlist = rp.query().playlist(&id).await?;
|
||||||
playlist
|
playlist.videos.extend_limit(rp.query(), limit).await?;
|
||||||
.videos
|
|
||||||
.extend_limit(rp.query(), limit)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
if txt {
|
if txt {
|
||||||
anstream::println!(
|
anstream::println!(
|
||||||
"{}\n{} [{}]\n{} {}",
|
"{}\n{} [{}]\n{} {}",
|
||||||
|
|
@ -1000,7 +995,7 @@ async fn main() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
UrlTarget::Album { id } => {
|
UrlTarget::Album { id } => {
|
||||||
let album = rp.query().music_album(&id).await.unwrap();
|
let album = rp.query().music_album(&id).await?;
|
||||||
if txt {
|
if txt {
|
||||||
anstream::print!(
|
anstream::print!(
|
||||||
"{}\n{} [{}] ({:?}",
|
"{}\n{} [{}] ({:?}",
|
||||||
|
|
@ -1043,8 +1038,8 @@ async fn main() {
|
||||||
} => match music {
|
} => match music {
|
||||||
None => match channel {
|
None => match channel {
|
||||||
Some(channel) => {
|
Some(channel) => {
|
||||||
rustypipe::validate::channel_id(&channel).unwrap();
|
rustypipe::validate::channel_id(&channel)?;
|
||||||
let res = rp.query().channel_search(&channel, &query).await.unwrap();
|
let res = rp.query().channel_search(&channel, &query).await?;
|
||||||
print_data(&res, format, pretty);
|
print_data(&res, format, pretty);
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
|
|
@ -1056,9 +1051,8 @@ async fn main() {
|
||||||
let mut res = rp
|
let mut res = rp
|
||||||
.query()
|
.query()
|
||||||
.search_filter::<YouTubeItem, _>(&query, &filter)
|
.search_filter::<YouTubeItem, _>(&query, &filter)
|
||||||
.await
|
.await?;
|
||||||
.unwrap();
|
res.items.extend_limit(rp.query(), limit).await?;
|
||||||
res.items.extend_limit(rp.query(), limit).await.unwrap();
|
|
||||||
|
|
||||||
if txt {
|
if txt {
|
||||||
if let Some(corr) = res.corrected_query {
|
if let Some(corr) = res.corrected_query {
|
||||||
|
|
@ -1071,27 +1065,27 @@ async fn main() {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Some(MusicSearchCategory::All) => {
|
Some(MusicSearchCategory::All) => {
|
||||||
let res = rp.query().music_search_main(&query).await.unwrap();
|
let res = rp.query().music_search_main(&query).await?;
|
||||||
print_music_search(&res, format, pretty, txt);
|
print_music_search(&res, format, pretty, txt);
|
||||||
}
|
}
|
||||||
Some(MusicSearchCategory::Tracks) => {
|
Some(MusicSearchCategory::Tracks) => {
|
||||||
let mut res = rp.query().music_search_tracks(&query).await.unwrap();
|
let mut res = rp.query().music_search_tracks(&query).await?;
|
||||||
res.items.extend_limit(rp.query(), limit).await.unwrap();
|
res.items.extend_limit(rp.query(), limit).await?;
|
||||||
print_music_search(&res, format, pretty, txt);
|
print_music_search(&res, format, pretty, txt);
|
||||||
}
|
}
|
||||||
Some(MusicSearchCategory::Videos) => {
|
Some(MusicSearchCategory::Videos) => {
|
||||||
let mut res = rp.query().music_search_videos(&query).await.unwrap();
|
let mut res = rp.query().music_search_videos(&query).await?;
|
||||||
res.items.extend_limit(rp.query(), limit).await.unwrap();
|
res.items.extend_limit(rp.query(), limit).await?;
|
||||||
print_music_search(&res, format, pretty, txt);
|
print_music_search(&res, format, pretty, txt);
|
||||||
}
|
}
|
||||||
Some(MusicSearchCategory::Artists) => {
|
Some(MusicSearchCategory::Artists) => {
|
||||||
let mut res = rp.query().music_search_artists(&query).await.unwrap();
|
let mut res = rp.query().music_search_artists(&query).await?;
|
||||||
res.items.extend_limit(rp.query(), limit).await.unwrap();
|
res.items.extend_limit(rp.query(), limit).await?;
|
||||||
print_music_search(&res, format, pretty, txt);
|
print_music_search(&res, format, pretty, txt);
|
||||||
}
|
}
|
||||||
Some(MusicSearchCategory::Albums) => {
|
Some(MusicSearchCategory::Albums) => {
|
||||||
let mut res = rp.query().music_search_albums(&query).await.unwrap();
|
let mut res = rp.query().music_search_albums(&query).await?;
|
||||||
res.items.extend_limit(rp.query(), limit).await.unwrap();
|
res.items.extend_limit(rp.query(), limit).await?;
|
||||||
print_music_search(&res, format, pretty, txt);
|
print_music_search(&res, format, pretty, txt);
|
||||||
}
|
}
|
||||||
Some(MusicSearchCategory::PlaylistsYtm | MusicSearchCategory::PlaylistsCommunity) => {
|
Some(MusicSearchCategory::PlaylistsYtm | MusicSearchCategory::PlaylistsCommunity) => {
|
||||||
|
|
@ -1101,15 +1095,15 @@ async fn main() {
|
||||||
&query,
|
&query,
|
||||||
music == Some(MusicSearchCategory::PlaylistsCommunity),
|
music == Some(MusicSearchCategory::PlaylistsCommunity),
|
||||||
)
|
)
|
||||||
.await
|
.await?;
|
||||||
.unwrap();
|
res.items.extend_limit(rp.query(), limit).await?;
|
||||||
res.items.extend_limit(rp.query(), limit).await.unwrap();
|
|
||||||
print_music_search(&res, format, pretty, txt);
|
print_music_search(&res, format, pretty, txt);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Commands::Vdata => {
|
Commands::Vdata => {
|
||||||
let vd = rp.query().get_visitor_data().await.unwrap();
|
let vd = rp.query().get_visitor_data().await?;
|
||||||
println!("{vd}");
|
println!("{vd}");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Reference in a new issue