diff --git a/src/aria2c.rs b/src/aria2c.rs index 0d6916a..9158be1 100644 --- a/src/aria2c.rs +++ b/src/aria2c.rs @@ -1,4 +1,5 @@ -use std::io::{BufRead, BufReader}; +use std::hash::{DefaultHasher, Hash, Hasher}; +use std::io::{BufRead, BufReader, Write}; #[cfg(target_os = "windows")] use std::os::windows::process::CommandExt; use std::path::Path; @@ -30,12 +31,83 @@ lazy_static! { }; } -pub struct DownloadCallbackInfo<'a> { +pub struct DownloadCallbackInfo { pub progress: f32, - pub child: &'a mut std::process::Child, } -pub fn download_file_with_progress( +pub fn download_file_with_progress_ureq( + url: &str, + output_path: &str, + progress_callback: &mut dyn FnMut(DownloadCallbackInfo), +) -> anyhow::Result<()> { + let tmp_dir_path = std::env::temp_dir().join("CelemodTemp"); + if !tmp_dir_path.exists() { + std::fs::create_dir(&tmp_dir_path)?; + } + let mut hasher = DefaultHasher::new(); + url.hash(&mut hasher); + let tmp_output_path = tmp_dir_path + .join(format!("{}.tmp", hasher.finish())) + .to_str() + .unwrap() + .to_string(); + + let output_path = Path::new(output_path); + let mut req = ureq::get(url); + let req = + req.set("Connection", "keep-alive") + .set( + "User-Agent", + format!( + "CeleMod/{}-{} ureq", + env!("VERSION"), + &env!("GIT_HASH")[..6] + ) + .as_str(), + ) + .set("Accept-Encoding", "gzip, deflate, br") + .set("Accept", "*/*") + .set("Cache-Control", "no-cache"); + + let mut resp = req.call(); + let mut file = std::fs::File::create(output_path)?; + let mut total_size = 0; + let mut downloaded_size = 0; + let mut buffer = [0u8; 1024 * 1024]; + let mut progress = 0.0; + let mut last_progress = 0.0; + match resp { + Ok(mut resp) => { + total_size = resp + .header("Content-Length") + .unwrap_or("0") + .parse() + .unwrap(); + let mut reader = resp.into_reader(); + loop { + let read_size = reader.read(&mut buffer)?; + if read_size == 0 { + break; + } + + file.write_all(&buffer[..read_size])?; + downloaded_size += read_size; + progress = (downloaded_size as f32 / total_size as f32) * 100.0; + if progress - last_progress > 0.1 { + progress_callback(DownloadCallbackInfo { progress }); + last_progress = progress; + } + } + } + Err(e) => { + bail!(e); + } + } + progress_callback(DownloadCallbackInfo { progress: 100.0 }); + Ok(()) +} + +pub fn download_file_with_progress_aria2c( url: &str, output_path: &str, progress_callback: &mut dyn FnMut(DownloadCallbackInfo), @@ -45,8 +117,6 @@ pub fn download_file_with_progress( println!("[ ARIA2C ] Downloading {} to {}", url, output_path); - - let output_path = Path::new(output_path); // 构建 aria2c 命令 let mut command = Command::new(aria2c_path); @@ -117,10 +187,7 @@ pub fn download_file_with_progress( progress }; if let Ok(progress) = progress { - progress_callback(DownloadCallbackInfo { - progress, - child: &mut child, - }); + progress_callback(DownloadCallbackInfo { progress }); } } @@ -135,7 +202,10 @@ pub fn download_file_with_progress( match child.wait() { Ok(status) => { if !status.success() || !Path::new(output_path).exists() { - bail!(format!("Failed to download file. Reason: {}", err.lock().unwrap())) + bail!(format!( + "Failed to download file. Reason: {}", + err.lock().unwrap() + )) } else { Ok(()) } @@ -143,3 +213,16 @@ pub fn download_file_with_progress( Err(_) => bail!("Failed to download file."), } } + +pub fn download_file_with_progress( + url: &str, + output_path: &str, + progress_callback: &mut dyn FnMut(DownloadCallbackInfo), + use_aria2: bool, +) -> anyhow::Result<()> { + if use_aria2 { + download_file_with_progress_aria2c(url, output_path, progress_callback, true) + } else { + download_file_with_progress_ureq(url, output_path, progress_callback) + } +} diff --git a/src/celemod-ui/src/routes/Home.tsx b/src/celemod-ui/src/routes/Home.tsx index ee17a5d..3fcdb05 100644 --- a/src/celemod-ui/src/routes/Home.tsx +++ b/src/celemod-ui/src/routes/Home.tsx @@ -196,7 +196,7 @@ export const Home = () => { }} /> - {_i18n.t('使用多线程下载')} + {_i18n.t('使用 aria2c 多线程下载')} diff --git a/src/celemod-ui/src/states.ts b/src/celemod-ui/src/states.ts index 65b401c..c3be2ee 100644 --- a/src/celemod-ui/src/states.ts +++ b/src/celemod-ui/src/states.ts @@ -158,7 +158,7 @@ export const [initGamePath, useGamePath] = createPersistedState('', stor save() }) -export const [initUseMultiThread, useUseMultiThread] = createPersistedStateByKey('useMultiThread', true) +export const [initUseMultiThread, useUseMultiThread] = createPersistedStateByKey('useMultiThread', false) export const [initAlwaysOnMods, useAlwaysOnMods] = createPersistedStateByKey('alwaysOnMods', []) diff --git a/src/main.rs b/src/main.rs index a35791e..ad17975 100644 --- a/src/main.rs +++ b/src/main.rs @@ -244,9 +244,9 @@ fn download_and_install_mod( url: &str, dest: &String, progress_callback: &mut dyn FnMut(DownloadCallbackInfo), - multi_thread: bool, + use_ureq: bool, ) -> anyhow::Result> { - aria2c::download_file_with_progress(url, dest, progress_callback, multi_thread)?; + aria2c::download_file_with_progress(url, dest, progress_callback, use_ureq)?; let yaml = extract_mod_for_yaml(&Path::new(&dest).to_path_buf())?;