Show Download Progress with rustube + indicatif

I am trying to download a video using rustube and showing the progress using indicatif , but when i try i get :

error[E0382]: use of moved value: `progress_bar`
  --> src\assets.rs:60:58
   |
56 | ...et progress_bar = ProgressBar::new(stream.content_length().awai...
   |       ------------ move occurs because `progress_bar` has type `ProgressBar`, which does not implement the `Copy` trait...
59 | ...   .connect_on_progress_closure_slow(move |arg| progress_bar.in...
   |                                         ---------- ------------ variable moved due to use in closure
   |                                         |
   |                                         value moved into 
closure here
60 | ...allback.connect_on_complete_closure(|_| progress_bar.finis...
   |                                        ^^^^^^^^ ------------ use occurs due to use in closure
   |                                        |
   |                                        value used here after move

For more information about this error, try `rustc --explain E0382`.

This is the current function that i am using

async fn check_download_vid_with_id(id : &str) -> Result<(),AssetsError> {
    let _dir = format!("{VIDEOS_DIR}/{id}.mp4");
    let path = Path::new(&_dir);
        
    match path.exists()  {
        true => Ok(()),
        false => {
            let _id = Id::from_str(id).unwrap();
            let _video = Video::from_id(_id.into_owned()).await.unwrap();
            
            match _video.best_video() {
                None => Err(AssetsError::VideoStreamUnavailable),
                Some(stream) => {
                    let progress_bar = ProgressBar::new(stream.content_length().await.unwrap());

                    let mut callback = Callback::new()
                        .connect_on_progress_closure_slow(move |arg| progress_bar.inc(arg.current_chunk as u64));
                    callback.connect_on_complete_closure(|_| progress_bar.finish() );
                    stream.download_to_dir(VIDEOS_DIR).await.map(|_| Ok(()))?
                    
                }
            }
        }
    }
}

I have to use connect_on_progress_closure_slow(move ..) as it says closure may outlive progress_bar. Then how can i do progress_bar.finish() on download complete.

I have even tried something like

let progress_bar = ProgressBar::new(stream.content_length().await.unwrap());

let callback = Callback::new().connect_on_progress_closure_slow(move |arg| progress_bar.inc(arg.current_chunk as u64));
       stream.download_to_dir(VIDEOS_DIR).await.map(|_| {
            progress_bar.finish();
                        Ok(())
                    })?

But i get :


error[E0382]: borrow of moved value: `progress_bar`                             
  --> src\assets.rs:59:66
   |
56 | ...et progress_bar = ProgressBar::new(stream.content_length().await.unwrap());
   |       ------------ move occurs because `progress_bar` has type `ProgressBar`, which does not implement the `Copy` trait
57 | ...
58 | ...et callback = Callback::new().connect_on_progress_closure_slow(move |arg| progress_bar.in...
   |                                                                   ---------- ------------ variable moved due to use in closure
   |                                                                   |        
   |                                                                   value moved into closure here
59 | ...tream.download_to_dir(VIDEOS_DIR).await.map(|_| {
   |                                                ^^^ value borrowed here after move
60 | ...   progress_bar.finish();
   |       ------------ borrow occurs due to use in closure

For more information about this error, try `rustc --explain E0382`.

You'll likely get faster answers in the future if you post code which isn't missing a lot of imports and other pieces. e.g. ProgressBar, AssetsError, Callback, Id, Video, Path, and VIDEOS_DIR. This makes it more work for us to run your code through various checkers.

After you move a ProgressBar to the first closure, it's gone. You can't use it anywhere else. Fortunately, ProgressBar implements Clone. From its documentation:

The progress bar is an [Arc] around its internal state. When the progress bar is cloned it just increments the refcount (so the original and its clone share the same state).

Change the callback to move a clone instead of moving the original:

let mut callback = {
    let progress_bar = progress_bar.clone();
    Callback::new().connect_on_progress_closure_slow(move |arg| {
        progress_bar.inc(arg.current_chunk as u64)
    });
};
4 Likes

Thank you! And sorry I just got so frustrated learning streams and async Tokio rust I forgot to provide those tiny details.

For future reference here is a working implementation for those who get stuck and come here
In your Cargo.toml

# For progress bar
indicatif = "0.17.6"

# For downloading neccessary youtube videos
rustube = { version = "0.6.0" , features = ["callback"]}

In your file :

use indicatif::{ProgressBar,ProgressStyle};

use rustube::{
    Video,
    Id,
    Callback,
};

const VIDEOS_DIR : &str = "assets/videos";

async fn check_download_vid_with_id(id : &str) -> Result<(),AssetsError> {
    let _dir = format!("{VIDEOS_DIR}/{id}.mp4");
    let path = Path::new(&_dir);
        
    match path.exists()  {
        true => Ok(()),
        false => {
            let _id = Id::from_str(id).unwrap();
            let _video = Video::from_id(_id.into_owned()).await.unwrap();
            
            match _video.best_video() {
                None => Err(AssetsError::VideoStreamUnavailable),
                Some(stream) => {
                    let _content_length = stream.content_length().await.unwrap();
                    let progress_bar = ProgressBar::new(_content_length);
                        
                    progress_bar.set_style(ProgressStyle::with_template("[{elapsed_precise}] {bar:40.cyan/blue} {percent}% : {msg}").unwrap());

                    let callback = {
                        let _progress_bar = progress_bar.clone();

                        Callback::new()
                            .connect_on_progress_closure_slow(move |arg| {
                                _progress_bar.inc(arg.current_chunk as u64);
                                _progress_bar.set_length(_progress_bar.length().unwrap()/*(arg.content_length.unwrap() - _progress_bar.length().unwrap()).abs()*/);
                            }) 
                            .connect_on_complete_closure(move |_| progress_bar.finish_with_message("Finished downloading asset!") )
                    };

                    stream.download_to_dir_with_callback(VIDEOS_DIR,callback).await.map(|_| Ok(()))?
                }
            }
        }
    }
}

Now replace AssetError with any Error type you need and replace VIDEOS_DIR with the directory you need to download to