Mithronn / rusty_ytdl

A Rust library for Youtube video searcher and downloader
https://docs.rs/rusty_ytdl
MIT License
107 stars 20 forks source link

Error: future cannot be sent between threads safely #6

Closed AhsanSarwar45 closed 1 year ago

AhsanSarwar45 commented 1 year ago

Hi, I am trying to use this with tauri commands. But I am getting this error:

error: future cannot be sent between threads safely
   --> src\main.rs:123:1
    |
123 |   #[tauri::command]
    |   ^^^^^^^^^^^^^^^^^ future returned by `download_youtube_song` is not `Send`
...
208 |           .invoke_handler(tauri::generate_handler![
    |  _________________________-
209 | |             get_file_tags,
210 | |             get_file_tag,
211 | |             download_youtube_song
212 | |         ])
    | |_________- in this macro invocation
    |
    = help: the trait `Send` is not implemented for `dyn Stream`
note: future is not `Send` as this value is used across an await
   --> D:\Apps\rust\cargo_home\registry\src\index.crates.io-6f17d22bba15001f\rusty_ytdl-0.6.1\src\info.rs:406:47
    |
401 |         let stream = self.stream().await.unwrap();
    |             ------ has type `Box<dyn Stream>` which is not `Send`
...
406 |         while let Some(chunk) = stream.chunk().await.unwrap() {
    |                                               ^^^^^^ await occurs here, with `stream` maybe used later
...
412 |     }
    |     - `stream` is later dropped here
note: required by a bound in `ResultFutureTag::future`
   --> D:\Apps\rust\cargo_home\registry\src\index.crates.io-6f17d22bba15001f\tauri-1.4.0\src\command.rs:289:42
    |
289 |       F: Future<Output = Result<T, E>> + Send,
    |                                          ^^^^ required by this bound in `ResultFutureTag::future`

The blocking api works, but as expected, it blocks the frontend UI. I am pretty new to rust so I'm not sure but is there any way to make the async api work with tauri?

My full code is:

#[tauri::command]
async fn download_youtube_song(search_term: String, directory: String) -> Result<String, String> {
    use rusty_ytdl::search::{SearchOptions, SearchResult, SearchType, YouTube};
    use rusty_ytdl::Video;
    use rusty_ytdl::{VideoOptions, VideoQuality, VideoSearchOptions};

    let youtube_result = YouTube::new();

    let youtube = match youtube_result {
        Ok(youtube) => youtube,
        Err(error) => {
            let error_string = format!("Error creating youtube searcher: {}", error);
            eprintln!("{}", error_string);
            return Err(error_string);
        }
    };

    let options = SearchOptions {
        limit: 1,
        search_type: SearchType::Video,
        ..Default::default()
    };

    let result = youtube.search(search_term, Some(&options)).await;

    let search_result = match result {
        Ok(result) => {
            if result.is_empty() {
                let error_string = format!("Error searching for video: {}", "No results");
                eprintln!("{}", error_string);
                return Err(error_string);
            } else {
                result[0].clone()
            }
        }
        Err(error) => {
            let error_string = format!("Error searching for video: {}", error);
            eprintln!("{}", error_string);
            return Err(error_string);
        }
    };

    let id = match search_result {
        SearchResult::Video(video) => video.id,
        _ => {
            let error_string = format!("Error searching for video: {}", "Not a video");
            eprintln!("{}", error_string);
            return Err(error_string);
        }
    };

    let path_string = format!("{}/{}.mp3", directory, id);

    let path = std::path::Path::new(&path_string);

    let video_options = VideoOptions {
        quality: VideoQuality::Highest,
        filter: VideoSearchOptions::Audio,
        ..Default::default()
    };

    let video_url = format!("https://www.youtube.com/watch?v={}", id);

    let video = Video::new_with_options(video_url, video_options).map_err(|error| {
        let error_string = format!("Error getting video: {}", error);
        eprintln!("{}", error_string);
        error_string
    })?;

    video.download(path).await.map_err(|error| {
        let error_string = format!("Error downloading video: {}", error);
        eprintln!("{}", error_string);
        error_string
    })?;

    Ok(path_string.into())
}

// tauri stuff
fn main() {
    tauri::Builder::default()
        .invoke_handler(tauri::generate_handler![
             download_youtube_song
        ])
        .run(tauri::generate_context!())
        .expect("error while running tauri application");
}
Mithronn commented 1 year ago

Thanks for the share, i am working on it

AhsanSarwar45 commented 1 year ago

@Mithronn BTW, rustube works with their async api in tauri. Although I can't use that because the download speed is extremely slow :(

Mithronn commented 1 year ago

With the 0.6.2 version now Stream trait and Search struct is thread safely!

AhsanSarwar45 commented 1 year ago

@Mithronn Wow, thanks a lot!

AhsanSarwar45 commented 1 year ago

@Mithronn I tried it, and I am no longer getting the error for Stream and Search struct. Unfortunately, I am still getting a similar error for something else:

error: future cannot be sent between threads safely
   --> src\main.rs:123:1
    |
123 |   #[tauri::command]
    |   ^^^^^^^^^^^^^^^^^ future returned by `download_youtube_song` is not `Send`
...
205 |           .invoke_handler(tauri::generate_handler![
    |  _________________________-
206 | |             get_file_tags,
207 | |             get_file_tag,
208 | |             download_youtube_song
209 | |         ])
    | |_________- in this macro invocation
    |
    = help: within `deno_core::extensions::OpDecl`, the trait `Send` is not implemented for `*const c_void`
note: future is not `Send` as this value is used across an await
   --> D:\Apps\rust\cargo_home\registry\src\index.crates.io-6f17d22bba15001f\rusty_ytdl-0.6.2\src\info.rs:205:17
    |
192 |         let mut cut_after_js_script =
    |             ----------------------- has type `js_sandbox::script::Script` which is not `Send`
...
205 |                 .await?,
    |                 ^^^^^^ await occurs here, with `mut cut_after_js_script` maybe used later
...
211 |     }
    |     - `mut cut_after_js_script` is later dropped here
note: required by a bound in `ResultFutureTag::future`
   --> D:\Apps\rust\cargo_home\registry\src\index.crates.io-6f17d22bba15001f\tauri-1.4.0\src\command.rs:289:42
    |
289 |       F: Future<Output = Result<T, E>> + Send,
    |                                          ^^^^ required by this bound in `ResultFutureTag::future`
    = note: this error originates in the macro `__cmd__download_youtube_song` which comes from the expansion of the macro `tauri::generate_handler` (in Nightly builds, run with -Z macro-backtrace for more info)
Mithronn commented 1 year ago

Completely overlooked. Fixed with new version (0.6.3). Thanks for sharing!

AhsanSarwar45 commented 1 year ago

@Mithronn Thanks again, all working now!