diff --git a/build.rs b/build.rs index f1a659a..883837e 100644 --- a/build.rs +++ b/build.rs @@ -1,4 +1,4 @@ -use rustc_version::{version_meta, Channel}; +use rustc_version::{Channel, version_meta}; fn main() { // Set cfg flags depending on release channel @@ -8,5 +8,5 @@ fn main() { Channel::Nightly => "CHANNEL_NIGHTLY", Channel::Dev => "CHANNEL_DEV", }; - println!("cargo:rustc-cfg={}", channel) -} \ No newline at end of file + println!("cargo:rustc-cfg={channel}"); +} diff --git a/cli/Cargo.toml b/cli/Cargo.toml index 143c90b..da3e49f 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -25,9 +25,10 @@ clap = { version = "3.0.0-rc.7", features = ["derive", "wrap_help"] } fern = { version = "0.6.0", features = ["colored"] } log = "0.4.14" mime = "0.3.16" -rustube = { path = "..", version = "0.6", features = ["download", "std"] } +rustube = { path = "..", version = "0.6", features = ["download", "std", "callback"] } tokio = { version = "1.12.0", features = ["rt-multi-thread"] } serde = "1.0.130" strum = { version = "0.22.0", features = ["derive"] } serde_json = "1.0.68" serde_yaml = "0.8.21" +pbr = "1.0.4" diff --git a/cli/src/args/logging.rs b/cli/src/args/logging.rs index 76153b6..016d785 100644 --- a/cli/src/args/logging.rs +++ b/cli/src/args/logging.rs @@ -14,6 +14,10 @@ pub struct LoggingArgs { #[clap(long, short, parse(from_occurrences))] verbose: u8, + /// Show a progress bar + #[clap(long, short, conflicts_with = "verbose")] + progress: bool, + /// When to log coloredd #[clap(long, default_value = "always", possible_values = & ["always", "never"], value_name = "WHEN")] color: ColorUsage, @@ -25,7 +29,7 @@ pub struct LoggingArgs { impl LoggingArgs { pub fn init_logger(&self) { - if self.quiet { return; } + if self.quiet || self.progress { return; } let formatter = self.log_msg_formatter(); @@ -38,6 +42,19 @@ impl LoggingArgs { .expect("The global logger was already initialized"); } + pub fn init_progress_bar(&self, total: u64) -> pbr::ProgressBar> { + let writer = match self.progress { + true => Box::new(std::io::stderr()) as _, + false => Box::new(std::io::sink()) as _, + }; + let mut pb = pbr::ProgressBar::on(writer, total); + pb.set_units(pbr::Units::Bytes); + pb.format("[=> ]"); + pb.add(0); + + pb + } + fn log_msg_formatter(&self) -> fn(FormatCallback, &Arguments, &Record) { #[inline(always)] fn format_msg( diff --git a/cli/src/args/stream_filter.rs b/cli/src/args/stream_filter.rs index 4d3620d..f071bac 100644 --- a/cli/src/args/stream_filter.rs +++ b/cli/src/args/stream_filter.rs @@ -121,6 +121,6 @@ impl StreamFilter { } fn parse_json serde::Deserialize<'de>>(s: &str) -> anyhow::Result { - let args = format!("\"{}\"", s); + let args = format!("\"{s}\""); Ok(serde_json::from_str(&args)?) } diff --git a/cli/src/main.rs b/cli/src/main.rs index 53a781a..4e5b8e9 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -6,6 +6,7 @@ use clap::Parser; use args::DownloadArgs; use args::StreamFilter; use rustube::{Error, Id, IdBuf, Stream, Video, VideoFetcher, VideoInfo}; +use rustube::Callback; use crate::args::{CheckArgs, Command, FetchArgs}; use crate::video_serializer::VideoSerializer; @@ -48,8 +49,11 @@ async fn check(args: CheckArgs) -> Result<()> { let (video_info, streams) = get_streams(id, &args.stream_filter).await?; let video_serializer = VideoSerializer::new(video_info, streams, args.output.output_level); - let output = args.output.output_format.serialize_output(&video_serializer)?; - println!("{}", output); + let output = args + .output + .output_format + .serialize_output(&video_serializer)?; + println!("{output}"); Ok(()) } @@ -59,22 +63,27 @@ async fn download(args: DownloadArgs) -> Result<()> { let id = args.identifier.id()?; let (video_info, stream) = get_stream(id.as_owned(), args.stream_filter).await?; - let download_path = download_path( - args.filename, - stream.mime.subtype().as_str(), - args.dir, - id, - ); + let download_path = download_path(args.filename, stream.mime.subtype().as_str(), args.dir, id); + + let mut pb = args.logging.init_progress_bar(stream.content_length().await?); + let callback = Callback::new() + .connect_on_progress_closure(|cargs| { + // update progress bar + pb.add(cargs.current_chunk.saturating_sub(cargs.current_chunk) as u64); + }); - stream.download_to(download_path).await?; + stream + .download_to_with_callback(&download_path, callback) + .await?; + pb.finish_println(&format!("Finished downloading video to {download_path:?}\n")); let video_serializer = VideoSerializer::new( video_info, std::iter::once(stream), args.output.output_level, ); - let output = args.output.output_format.serialize_output(&video_serializer)?; - println!("{}", output); + let output = args.output.output_format.serialize_output(&video_serializer).unwrap(); + println!("{output}"); Ok(()) } @@ -83,20 +92,15 @@ async fn fetch(args: FetchArgs) -> Result<()> { args.logging.init_logger(); let id = args.identifier.id()?; - let video_info = rustube::VideoFetcher::from_id(id)? - .fetch_info() - .await?; + let video_info = rustube::VideoFetcher::from_id(id)?.fetch_info().await?; let output = args.output.output_format.serialize_output(&video_info)?; - println!("{}", output); + println!("{output}"); Ok(()) } -async fn get_stream( - id: IdBuf, - stream_filter: StreamFilter, -) -> Result<(VideoInfo, Stream)> { +async fn get_stream(id: IdBuf, stream_filter: StreamFilter) -> Result<(VideoInfo, Stream)> { let (video_info, streams) = get_streams(id, &stream_filter).await?; let stream = streams @@ -111,9 +115,7 @@ async fn get_streams( id: IdBuf, stream_filter: &'_ StreamFilter, ) -> Result<(VideoInfo, impl Iterator + '_)> { - let (video_info, streams) = get_video(id) - .await? - .into_parts(); + let (video_info, streams) = get_video(id).await?.into_parts(); let streams = streams .into_iter() @@ -131,9 +133,14 @@ async fn get_video(id: IdBuf) -> Result