From a3c05f5d17e4a3d27923ee4e12275c994a2aa9f7 Mon Sep 17 00:00:00 2001 From: Mateusz Wykurz Date: Sun, 29 Sep 2024 22:07:19 -0600 Subject: [PATCH] Update the progress bar to display additional metrics and custimize output for each tool --- common/src/cmp.rs | 6 +- common/src/copy.rs | 46 ++++++++++--- common/src/lib.rs | 59 +++-------------- common/src/link.rs | 12 ++-- common/src/progress.rs | 143 ++++++++++++++++++++++++++++++++++++++--- common/src/rm.rs | 13 ++-- rcmp/src/main.rs | 1 - rcp/src/main.rs | 1 - rlink/src/main.rs | 1 - rrm/src/main.rs | 1 - 10 files changed, 199 insertions(+), 84 deletions(-) diff --git a/common/src/cmp.rs b/common/src/cmp.rs index 7536f63..62fdf7c 100644 --- a/common/src/cmp.rs +++ b/common/src/cmp.rs @@ -135,14 +135,14 @@ fn obj_type(metadata: &std::fs::Metadata) -> ObjType { #[instrument(skip(prog_track))] #[async_recursion] pub async fn cmp( - prog_track: &'static progress::TlsProgress, + prog_track: &'static progress::Progress, src: &std::path::Path, dst: &std::path::Path, log: &LogWriter, settings: &CmpSettings, ) -> Result { throttle::get_token().await; - let _prog_guard = prog_track.guard(); + let _prog_guard = prog_track.ops.guard(); event!(Level::DEBUG, "reading source metadata"); // it is impossible for src not exist other than user passing invalid path (which is an error) let src_metadata = tokio::fs::symlink_metadata(src) @@ -283,7 +283,7 @@ mod cmp_tests { use super::*; lazy_static! { - static ref PROGRESS: progress::TlsProgress = progress::TlsProgress::new(); + static ref PROGRESS: progress::Progress = progress::Progress::new(); static ref NO_PRESERVE_SETTINGS: preserve::PreserveSettings = preserve::preserve_default(); static ref DO_PRESERVE_SETTINGS: preserve::PreserveSettings = preserve::preserve_all(); } diff --git a/common/src/copy.rs b/common/src/copy.rs index 7b593f2..cc191eb 100644 --- a/common/src/copy.rs +++ b/common/src/copy.rs @@ -43,7 +43,7 @@ pub fn is_file_type_same(md1: &std::fs::Metadata, md2: &std::fs::Metadata) -> bo #[instrument(skip(prog_track))] pub async fn copy_file( - prog_track: &'static progress::TlsProgress, + prog_track: &'static progress::Progress, src: &std::path::Path, dst: &std::path::Path, settings: &CopySettings, @@ -75,6 +75,7 @@ pub async fn copy_file( ) { event!(Level::DEBUG, "file is identical, skipping"); + prog_track.files_unchanged.inc(); return Ok(CopySummary { files_unchanged: 1, ..Default::default() @@ -117,36 +118,42 @@ pub async fn copy_file( .await .with_context(|| format!("failed copying {:?} to {:?}", &src, &dst)) .map_err(|err| CopyError::new(err, copy_summary))?; + prog_track.files_copied.inc(); + prog_track.bytes_copied.add(src_metadata.len()); event!(Level::DEBUG, "setting permissions"); preserve::set_file_metadata(preserve, &src_metadata, dst) .await .map_err(|err| CopyError::new(err, copy_summary))?; - copy_summary.files_copied += 1; // we mark files as "copied" only after all metadata is set as well + // we mark files as "copied" only after all metadata is set as well + copy_summary.bytes_copied += src_metadata.len(); + copy_summary.files_copied += 1; Ok(copy_summary) } #[derive(Copy, Clone, Debug, Default)] pub struct CopySummary { - pub rm_summary: RmSummary, + pub bytes_copied: u64, pub files_copied: usize, pub symlinks_created: usize, pub directories_created: usize, pub files_unchanged: usize, pub symlinks_unchanged: usize, pub directories_unchanged: usize, + pub rm_summary: RmSummary, } impl std::ops::Add for CopySummary { type Output = Self; fn add(self, other: Self) -> Self { Self { - rm_summary: self.rm_summary + other.rm_summary, + bytes_copied: self.bytes_copied + other.bytes_copied, files_copied: self.files_copied + other.files_copied, symlinks_created: self.symlinks_created + other.symlinks_created, directories_created: self.directories_created + other.directories_created, files_unchanged: self.files_unchanged + other.files_unchanged, symlinks_unchanged: self.symlinks_unchanged + other.symlinks_unchanged, directories_unchanged: self.directories_unchanged + other.directories_unchanged, + rm_summary: self.rm_summary + other.rm_summary, } } } @@ -155,8 +162,22 @@ impl std::fmt::Display for CopySummary { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!( f, - "{}\nfiles copied: {}\nsymlinks created: {}\ndirectories created: {}\nfiles_unchanged: {}\ndirectories_unchanged: {}\n", - &self.rm_summary, self.files_copied, self.symlinks_created, self.directories_created, self.files_unchanged, self.directories_unchanged + "bytes copied: {}\n\ + files copied: {}\n\ + symlinks created: {}\n\ + directories created: {}\n\ + files unchanged: {}\n\ + symlinks unchanged: {}\n\ + directories unchanged: {}\n\ + {}", + bytesize::ByteSize(self.bytes_copied), + self.files_copied, + self.symlinks_created, + self.directories_created, + self.files_unchanged, + self.symlinks_unchanged, + self.directories_unchanged, + &self.rm_summary, ) } } @@ -164,7 +185,7 @@ impl std::fmt::Display for CopySummary { #[instrument(skip(prog_track))] #[async_recursion] pub async fn copy( - prog_track: &'static progress::TlsProgress, + prog_track: &'static progress::Progress, cwd: &std::path::Path, src: &std::path::Path, dst: &std::path::Path, @@ -173,7 +194,7 @@ pub async fn copy( mut is_fresh: bool, ) -> Result { throttle::get_token().await; - let _prog_guard = prog_track.guard(); + let _ops_guard = prog_track.ops.guard(); event!(Level::DEBUG, "reading source metadata"); let src_metadata = tokio::fs::symlink_metadata(src) .await @@ -246,6 +267,8 @@ pub async fn copy( preserve::set_symlink_metadata(preserve, &src_metadata, dst) .await .map_err(|err| CopyError::new(err, Default::default()))?; + prog_track.symlinks_removed.inc(); + prog_track.symlinks_created.inc(); return Ok(CopySummary { rm_summary: RmSummary { symlinks_removed: 1, @@ -257,6 +280,7 @@ pub async fn copy( } } event!(Level::DEBUG, "symlink already exists, skipping"); + prog_track.symlinks_unchanged.inc(); return Ok(CopySummary { symlinks_unchanged: 1, ..Default::default() @@ -311,6 +335,7 @@ pub async fn copy( }; CopyError::new(err, copy_summary) })?; + prog_track.symlinks_created.inc(); return Ok(CopySummary { rm_summary, symlinks_created: 1, @@ -347,6 +372,7 @@ pub async fn copy( .map_err(|err| CopyError::new(err, Default::default()))?; if dst_metadata.is_dir() { event!(Level::DEBUG, "'dst' is a directory, leaving it as is"); + prog_track.directories_unchanged.inc(); CopySummary { directories_unchanged: 1, ..Default::default() @@ -384,6 +410,7 @@ pub async fn copy( })?; // anythingg copied into dst may assume they don't need to check for conflicts is_fresh = true; + prog_track.directories_created.inc(); CopySummary { rm_summary, directories_created: 1, @@ -400,6 +427,7 @@ pub async fn copy( } else { // new directory created, anythingg copied into dst may assume they don't need to check for conflicts is_fresh = true; + prog_track.directories_created.inc(); CopySummary { directories_created: 1, ..Default::default() @@ -483,7 +511,7 @@ mod copy_tests { use super::*; lazy_static! { - static ref PROGRESS: progress::TlsProgress = progress::TlsProgress::new(); + static ref PROGRESS: progress::Progress = progress::Progress::new(); static ref NO_PRESERVE_SETTINGS: preserve::PreserveSettings = preserve::preserve_default(); static ref DO_PRESERVE_SETTINGS: preserve::PreserveSettings = preserve::preserve_all(); } diff --git a/common/src/lib.rs b/common/src/lib.rs index f165e87..a1d5de6 100644 --- a/common/src/lib.rs +++ b/common/src/lib.rs @@ -37,7 +37,7 @@ pub use rm::RmSettings; pub use rm::RmSummary; lazy_static! { - static ref PROGRESS: progress::TlsProgress = progress::TlsProgress::new(); + static ref PROGRESS: progress::Progress = progress::Progress::new(); } struct ProgressTracker { @@ -88,7 +88,6 @@ impl std::str::FromStr for ProgressType { #[derive(Debug)] pub struct ProgressSettings { - pub op_name: String, pub progress_type: ProgressType, pub progress_delay: Option, } @@ -96,7 +95,6 @@ pub struct ProgressSettings { fn progress_bar( lock: &std::sync::Mutex, cvar: &std::sync::Condvar, - op_name: &str, delay_opt: &Option, ) { let pbar = indicatif::ProgressBar::new_spinner(); @@ -106,23 +104,11 @@ fn progress_bar( .unwrap() .tick_strings(&["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"]), ); - let time_started = std::time::Instant::now(); - let mut last_update = time_started; + let mut prog_printer = progress::ProgressPrinter::new(&PROGRESS); let mut is_done = lock.lock().unwrap(); loop { - let progress_status = PROGRESS.get(); - let time_now = std::time::Instant::now(); - let finished = progress_status.finished; - let in_progress = progress_status.started - progress_status.finished; - let avarage_rate = finished as f64 / time_started.elapsed().as_secs_f64(); - let current_rate = - (finished - pbar.position()) as f64 / (time_now - last_update).as_secs_f64(); - pbar.set_position(finished); - pbar.set_message(format!( - "done: {} | {}: {} | average: {:.2} items/s | current: {:.2} items/s", - finished, op_name, in_progress, avarage_rate, current_rate - )); - last_update = time_now; + pbar.set_position(pbar.position() + 1); // do we need to update? + pbar.set_message(prog_printer.print().unwrap()); let result = cvar.wait_timeout(is_done, delay).unwrap(); is_done = result.0; if *is_done { @@ -135,33 +121,13 @@ fn progress_bar( fn text_updates( lock: &std::sync::Mutex, cvar: &std::sync::Condvar, - op_name: &str, delay_opt: &Option, ) { - let time_started = std::time::Instant::now(); let delay = delay_opt.unwrap_or(std::time::Duration::from_secs(10)); - let mut last_update = time_started; - let mut prev_finished = 0; + let mut prog_printer = progress::ProgressPrinter::new(&PROGRESS); let mut is_done = lock.lock().unwrap(); loop { - let progress_status = PROGRESS.get(); - let time_now = std::time::Instant::now(); - let finished = progress_status.finished; - let in_progress = progress_status.started - progress_status.finished; - let avarage_rate = finished as f64 / time_started.elapsed().as_secs_f64(); - let current_rate = - (finished - prev_finished) as f64 / (time_now - last_update).as_secs_f64(); - prev_finished = finished; - eprintln!( - "{} :: done: {} | {}: {} | average: {:.2} items/s | current: {:.2} items/s", - chrono::Local::now(), - finished, - op_name, - in_progress, - avarage_rate, - current_rate - ); - last_update = time_now; + eprintln!("{}", prog_printer.print().unwrap()); let result = cvar.wait_timeout(is_done, delay).unwrap(); is_done = result.0; if *is_done { @@ -171,12 +137,7 @@ fn text_updates( } impl ProgressTracker { - pub fn new( - progress_type: ProgressType, - op_name: &str, - delay_opt: Option, - ) -> Self { - let op_name = op_name.to_string(); + pub fn new(progress_type: ProgressType, delay_opt: Option) -> Self { let lock_cvar = std::sync::Arc::new((std::sync::Mutex::new(false), std::sync::Condvar::new())); let lock_cvar_clone = lock_cvar.clone(); @@ -188,9 +149,9 @@ impl ProgressTracker { ProgressType::TextUpdates => false, }; if interactive { - progress_bar(lock, cvar, &op_name, &delay_opt); + progress_bar(lock, cvar, &delay_opt); } else { - text_updates(lock, cvar, &op_name, &delay_opt); + text_updates(lock, cvar, &delay_opt); } }); Self { @@ -501,7 +462,7 @@ where humantime::parse_duration(&delay_str) .expect("Couldn't parse duration out of --progress-delay") }); - ProgressTracker::new(settings.progress_type, &settings.op_name, delay) + ProgressTracker::new(settings.progress_type, delay) }); runtime.block_on(func()) }; diff --git a/common/src/link.rs b/common/src/link.rs index c534334..9a9cbde 100644 --- a/common/src/link.rs +++ b/common/src/link.rs @@ -71,9 +71,9 @@ fn is_hard_link(md1: &std::fs::Metadata, md2: &std::fs::Metadata) -> bool { && md2.st_ino() == md1.st_ino() } -#[instrument] +#[instrument(skip(prog_track))] async fn hard_link_helper( - prog_track: &'static progress::TlsProgress, + prog_track: &'static progress::Progress, src: &std::path::Path, src_metadata: &std::fs::Metadata, dst: &std::path::Path, @@ -92,6 +92,7 @@ async fn hard_link_helper( .map_err(|err| LinkError::new(err, Default::default()))?; if is_hard_link(src_metadata, &dst_metadata) { event!(Level::DEBUG, "no change, leaving file as is"); + prog_track.hard_links_unchanged.inc(); return Ok(LinkSummary { hard_links_unchanged: 1, ..Default::default() @@ -120,6 +121,7 @@ async fn hard_link_helper( .map_err(|err| LinkError::new(anyhow::Error::msg(err), link_summary))?; } } + prog_track.hard_links_created.inc(); link_summary.hard_links_created = 1; Ok(link_summary) } @@ -127,7 +129,7 @@ async fn hard_link_helper( #[instrument(skip(prog_track))] #[async_recursion] pub async fn link( - prog_track: &'static progress::TlsProgress, + prog_track: &'static progress::Progress, cwd: &std::path::Path, src: &std::path::Path, dst: &std::path::Path, @@ -136,7 +138,7 @@ pub async fn link( mut is_fresh: bool, ) -> Result { throttle::get_token().await; - let _prog_guard = prog_track.guard(); + let _prog_guard = prog_track.ops.guard(); event!(Level::DEBUG, "reading source metadata"); let src_metadata = tokio::fs::symlink_metadata(src) .await @@ -528,7 +530,7 @@ mod link_tests { use super::*; lazy_static! { - static ref PROGRESS: progress::TlsProgress = progress::TlsProgress::new(); + static ref PROGRESS: progress::Progress = progress::Progress::new(); } fn common_settings(dereference: bool, overwrite: bool) -> LinkSettings { diff --git a/common/src/progress.rs b/common/src/progress.rs index 5da6495..6b0d2ab 100644 --- a/common/src/progress.rs +++ b/common/src/progress.rs @@ -13,10 +13,14 @@ impl TlsCounter { } } - pub fn inc(&self) { + pub fn add(&self, value: u64) { let mutex = self.count.get_or(|| std::sync::Mutex::new(0)); let mut guard = mutex.lock().unwrap(); - *guard += 1; + *guard += value; + } + + pub fn inc(&self) { + self.add(1); } pub fn get(&self) -> u64 { @@ -31,24 +35,23 @@ impl Default for TlsCounter { } #[derive(Debug)] -pub struct TlsProgress { +pub struct ProgressCounter { started: TlsCounter, finished: TlsCounter, - start_time: std::time::Instant, } -impl Default for TlsProgress { +impl Default for ProgressCounter { fn default() -> Self { Self::new() } } pub struct ProgressGuard<'a> { - progress: &'a TlsProgress, + progress: &'a ProgressCounter, } impl<'a> ProgressGuard<'a> { - pub fn new(progress: &'a TlsProgress) -> Self { + pub fn new(progress: &'a ProgressCounter) -> Self { progress.started.inc(); Self { progress } } @@ -65,12 +68,11 @@ pub struct Status { pub finished: u64, } -impl TlsProgress { +impl ProgressCounter { pub fn new() -> Self { Self { started: TlsCounter::new(), finished: TlsCounter::new(), - start_time: std::time::Instant::now(), } } @@ -95,12 +97,133 @@ impl TlsProgress { } status } +} + +pub struct Progress { + pub ops: ProgressCounter, + pub bytes_copied: TlsCounter, + pub hard_links_created: TlsCounter, + pub files_copied: TlsCounter, + pub symlinks_created: TlsCounter, + pub directories_created: TlsCounter, + pub files_unchanged: TlsCounter, + pub symlinks_unchanged: TlsCounter, + pub directories_unchanged: TlsCounter, + pub hard_links_unchanged: TlsCounter, + pub files_removed: TlsCounter, + pub symlinks_removed: TlsCounter, + pub directories_removed: TlsCounter, + start_time: std::time::Instant, +} + +impl Progress { + pub fn new() -> Self { + Self { + ops: Default::default(), + bytes_copied: Default::default(), + hard_links_created: Default::default(), + files_copied: Default::default(), + symlinks_created: Default::default(), + directories_created: Default::default(), + files_unchanged: Default::default(), + symlinks_unchanged: Default::default(), + directories_unchanged: Default::default(), + hard_links_unchanged: Default::default(), + files_removed: Default::default(), + symlinks_removed: Default::default(), + directories_removed: Default::default(), + start_time: std::time::Instant::now(), + } + } pub fn get_duration(&self) -> std::time::Duration { self.start_time.elapsed() } } +pub struct ProgressPrinter<'a> { + progress: &'a Progress, + last_ops: u64, + last_bytes: u64, + last_update: std::time::Instant, +} + +impl<'a> ProgressPrinter<'a> { + pub fn new(progress: &'a Progress) -> Self { + Self { + progress, + last_ops: progress.ops.get().finished, + last_bytes: progress.bytes_copied.get(), + last_update: std::time::Instant::now(), + } + } + + pub fn print(&mut self) -> anyhow::Result { + let time_now = std::time::Instant::now(); + let ops = self.progress.ops.get(); + let total_duration_secs = self.progress.get_duration().as_secs_f64(); + let curr_duration_secs = (time_now - self.last_update).as_secs_f64(); + let avarage_ops_rate = ops.finished as f64 / total_duration_secs; + let current_ops_rate = (ops.finished - self.last_ops) as f64 / curr_duration_secs; + let bytes = self.progress.bytes_copied.get(); + let avarage_bytes_rate = bytes as f64 / total_duration_secs; + let current_bytes_rate = (bytes - self.last_bytes) as f64 / curr_duration_secs; + // update self + self.last_ops = ops.finished; + self.last_bytes = bytes; + self.last_update = time_now; + // nice to have: convert to a table + Ok(format!( + "---------------------\n\ + OPS:\n\ + pending: {:>10}\n\ + average: {:>10.2} items/s\n\ + current: {:>10.2} items/s\n\ + -----------------------\n\ + COPIED:\n\ + average: {:>10}/s\n\ + current: {:>10}/s\n\ + total: {:>10}\n\ + \n\ + files: {:>10}\n\ + symlinks: {:>10}\n\ + directories: {:>10}\n\ + hard-links: {:>10}\n\ + -----------------------\n\ + UNCHANGED:\n\ + files: {:>10}\n\ + symlinks: {:>10}\n\ + directories: {:>10}\n\ + hard-links: {:>10}\n\ + -----------------------\n\ + REMOVED:\n\ + files: {:>10}\n\ + symlinks: {:>10}\n\ + directories: {:>10}", + ops.started - ops.finished, // pending + avarage_ops_rate, + current_ops_rate, + // copy + bytesize::ByteSize(avarage_bytes_rate as u64), + bytesize::ByteSize(current_bytes_rate as u64), + bytesize::ByteSize(self.progress.bytes_copied.get()), + self.progress.files_copied.get(), + self.progress.symlinks_created.get(), + self.progress.directories_created.get(), + self.progress.hard_links_created.get(), + // unchanged + self.progress.files_unchanged.get(), + self.progress.symlinks_unchanged.get(), + self.progress.directories_unchanged.get(), + self.progress.hard_links_unchanged.get(), + // remove + self.progress.files_removed.get(), + self.progress.symlinks_removed.get(), + self.progress.directories_removed.get(), + )) + } +} + #[cfg(test)] mod tests { use super::*; @@ -135,7 +258,7 @@ mod tests { #[test] fn basic_guard() -> Result<()> { - let tls_progress = TlsProgress::new(); + let tls_progress = ProgressCounter::new(); let _guard = tls_progress.guard(); Ok(()) } diff --git a/common/src/rm.rs b/common/src/rm.rs index f0aa127..51cc6a3 100644 --- a/common/src/rm.rs +++ b/common/src/rm.rs @@ -47,7 +47,9 @@ impl std::fmt::Display for RmSummary { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!( f, - "files removed: {}\nsymlinks removed: {}\ndirectories removed: {}", + "files removed: {}\n\ + symlinks removed: {}\n\ + directories removed: {}", self.files_removed, self.symlinks_removed, self.directories_removed ) } @@ -56,12 +58,12 @@ impl std::fmt::Display for RmSummary { #[instrument(skip(prog_track))] #[async_recursion] pub async fn rm( - prog_track: &'static progress::TlsProgress, + prog_track: &'static progress::Progress, path: &std::path::Path, settings: &RmSettings, ) -> Result { throttle::get_token().await; - let _prog_guard = prog_track.guard(); + let _ops_guard = prog_track.ops.guard(); event!(Level::DEBUG, "read path metadata"); let src_metadata = tokio::fs::symlink_metadata(path) .await @@ -74,11 +76,13 @@ pub async fn rm( .with_context(|| format!("failed removing {:?}", &path)) .map_err(|err| RmError::new(anyhow::Error::msg(err), Default::default()))?; if src_metadata.file_type().is_symlink() { + prog_track.symlinks_removed.inc(); return Ok(RmSummary { symlinks_removed: 1, ..Default::default() }); } + prog_track.files_removed.inc(); return Ok(RmSummary { files_removed: 1, ..Default::default() @@ -141,6 +145,7 @@ pub async fn rm( .await .with_context(|| format!("failed removing directory {:?}", &path)) .map_err(|err| RmError::new(anyhow::Error::msg(err), rm_summary))?; + prog_track.directories_removed.inc(); rm_summary.directories_removed += 1; Ok(rm_summary) } @@ -152,7 +157,7 @@ mod tests { use tracing_test::traced_test; lazy_static! { - static ref PROGRESS: progress::TlsProgress = progress::TlsProgress::new(); + static ref PROGRESS: progress::Progress = progress::Progress::new(); } #[tokio::test] diff --git a/rcmp/src/main.rs b/rcmp/src/main.rs index 9296b25..a5b80b5 100644 --- a/rcmp/src/main.rs +++ b/rcmp/src/main.rs @@ -125,7 +125,6 @@ fn main() -> Result<()> { let res = common::run( if args.progress || args.progress_type.is_some() { Some(common::ProgressSettings { - op_name: "rcmp".to_string(), progress_type: args.progress_type.unwrap_or_default(), progress_delay: args.progress_delay, }) diff --git a/rcp/src/main.rs b/rcp/src/main.rs index 21a2503..76d3971 100644 --- a/rcp/src/main.rs +++ b/rcp/src/main.rs @@ -223,7 +223,6 @@ fn main() -> Result<(), anyhow::Error> { let res = common::run( if args.progress || args.progress_type.is_some() { Some(common::ProgressSettings { - op_name: "copy".to_string(), progress_type: args.progress_type.unwrap_or_default(), progress_delay: args.progress_delay, }) diff --git a/rlink/src/main.rs b/rlink/src/main.rs index afceca8..25050e2 100644 --- a/rlink/src/main.rs +++ b/rlink/src/main.rs @@ -158,7 +158,6 @@ fn main() -> Result<()> { let res = common::run( if args.progress || args.progress_type.is_some() { Some(common::ProgressSettings { - op_name: "link".to_string(), progress_type: args.progress_type.unwrap_or_default(), progress_delay: args.progress_delay, }) diff --git a/rrm/src/main.rs b/rrm/src/main.rs index 402fb61..4f1340b 100644 --- a/rrm/src/main.rs +++ b/rrm/src/main.rs @@ -118,7 +118,6 @@ fn main() -> Result<()> { let res = common::run( if args.progress || args.progress_type.is_some() { Some(common::ProgressSettings { - op_name: "rm".to_string(), progress_type: args.progress_type.unwrap_or_default(), progress_delay: args.progress_delay, })