diff options
author | fxqnlr <[email protected]> | 2023-05-21 13:43:52 +0200 |
---|---|---|
committer | fxqnlr <[email protected]> | 2023-05-21 13:43:52 +0200 |
commit | 016e1d8d760113a64afcc5d516f08010cb566d68 (patch) | |
tree | 3de1a3bfbd3c7266dab5288240720133ffd2f602 /src | |
parent | 5a2ea0755b29a8811aeeec1c73679c5783082628 (diff) | |
download | modlist-016e1d8d760113a64afcc5d516f08010cb566d68.tar modlist-016e1d8d760113a64afcc5d516f08010cb566d68.tar.gz modlist-016e1d8d760113a64afcc5d516f08010cb566d68.zip |
added multithreaded downloads and progressbar
Diffstat (limited to 'src')
-rw-r--r-- | src/commands/modification.rs | 8 | ||||
-rw-r--r-- | src/files.rs | 160 |
2 files changed, 107 insertions, 61 deletions
diff --git a/src/commands/modification.rs b/src/commands/modification.rs index 9a1a651..d4c49d6 100644 --- a/src/commands/modification.rs +++ b/src/commands/modification.rs | |||
@@ -140,6 +140,8 @@ async fn get_mod_infos(config: Cfg, mod_ids: Vec<(String, bool)>, list: List) -> | |||
140 | 140 | ||
141 | let mut ids = vec![]; | 141 | let mut ids = vec![]; |
142 | 142 | ||
143 | println!("{:?}", mod_ids); | ||
144 | |||
143 | for id in mod_ids { | 145 | for id in mod_ids { |
144 | setmap.insert(id.0.to_string(), id.1); | 146 | setmap.insert(id.0.to_string(), id.1); |
145 | ids.push(id.0); | 147 | ids.push(id.0); |
@@ -195,14 +197,16 @@ async fn get_mod_infos(config: Cfg, mod_ids: Vec<(String, bool)>, list: List) -> | |||
195 | available_versions_vec.push(ver.id); | 197 | available_versions_vec.push(ver.id); |
196 | } | 198 | } |
197 | 199 | ||
200 | println!("{:?}", setmap); | ||
201 | |||
198 | projectinfo.push(ProjectInfo { | 202 | projectinfo.push(ProjectInfo { |
199 | mod_id: String::from(&project.id), | 203 | mod_id: String::from(&project.id), |
200 | slug: project.slug, | 204 | slug: project.slug.clone(), |
201 | title: project.title, | 205 | title: project.title, |
202 | current_version, | 206 | current_version, |
203 | applicable_versions: available_versions_vec, | 207 | applicable_versions: available_versions_vec, |
204 | download_link: file, | 208 | download_link: file, |
205 | set_version: setmap.get(&project.id).unwrap().clone(), | 209 | set_version: setmap.get(&project.slug).unwrap().clone(), |
206 | }) | 210 | }) |
207 | } else { | 211 | } else { |
208 | println!("\t └There's currently no mod version for your specified target"); | 212 | println!("\t └There's currently no mod version for your specified target"); |
diff --git a/src/files.rs b/src/files.rs index a4c128e..a4a1d3b 100644 --- a/src/files.rs +++ b/src/files.rs | |||
@@ -1,9 +1,11 @@ | |||
1 | use futures_util::StreamExt; | 1 | use futures_util::StreamExt; |
2 | use indicatif::{ProgressBar, ProgressStyle, MultiProgress}; | ||
2 | use reqwest::Client; | 3 | use reqwest::Client; |
4 | use tokio::task::JoinSet; | ||
3 | use std::{ | 5 | use std::{ |
4 | collections::HashMap, | 6 | collections::HashMap, |
5 | fs::{copy, read_dir, remove_file, rename, File}, | 7 | fs::{copy, read_dir, remove_file, rename, File}, |
6 | io::Write, | 8 | io::Write, cmp::min, |
7 | }; | 9 | }; |
8 | 10 | ||
9 | use crate::{ | 11 | use crate::{ |
@@ -15,83 +17,123 @@ use crate::{ | |||
15 | List, | 17 | List, |
16 | }; | 18 | }; |
17 | 19 | ||
18 | pub async fn download_versions(list: List, config: Cfg, versions: Vec<Version>) -> MLE<String> { | 20 | pub async fn download_versions(list: List, config: Cfg, versions: Vec<Version>) -> MLE<()> { |
19 | let mut cached = get_cached_versions(&config.cache); | 21 | let cached = get_cached_versions(&config.cache); |
20 | 22 | ||
21 | // println!("{:#?}", cached); | 23 | // println!("{:#?}", cached); |
22 | 24 | ||
23 | let dl_path = String::from(&list.download_folder); | 25 | // println!(" └Download mods to {}", dl_path); |
26 | |||
27 | let mp = MultiProgress::new(); | ||
24 | 28 | ||
25 | println!(" └Download mods to {}", dl_path); | 29 | let mut js = JoinSet::new(); |
30 | let style = ProgressStyle::with_template("{spinner:.green}{msg}\t[{bar:.green/lime}] {bytes}/{total_bytes}") | ||
31 | .unwrap() | ||
32 | .progress_chars("#>-"); | ||
26 | 33 | ||
27 | for ver in versions { | 34 | for ver in versions { |
28 | let project_info = mods_get_info(config.clone(), &ver.project_id)?; | 35 | let p = mp.add(ProgressBar::new(1)); |
29 | 36 | p.set_style(style.clone()); | |
30 | //Check cache if already downloaded | 37 | js.spawn(download_version(config.clone(), list.clone(), ver, cached.clone(), p)); |
31 | let c = cached.remove(&ver.id); | 38 | } |
32 | if c.is_some() { | 39 | |
33 | print!( | 40 | mp.clear().unwrap(); |
34 | "\t└({})Get version {} from cache", | 41 | |
35 | project_info.title, ver.id | 42 | while js.join_next().await.is_some() {} |
36 | ); | 43 | |
37 | //Force flush of stdout, else print! doesn't print instantly | 44 | Ok(()) |
38 | std::io::stdout().flush()?; | 45 | } |
39 | copy_cached_version(&c.unwrap(), &dl_path); | 46 | |
40 | println!(" ✓"); | 47 | async fn download_version(config: Cfg, list: List, version: Version, mut cached: HashMap<String, String>, progress: ProgressBar) -> MLE<()> { |
41 | } else { | 48 | let project_info = mods_get_info(config.clone(), &version.project_id)?; |
42 | print!("\t└({})Download version {}", project_info.title, ver.id); | 49 | |
43 | //Force flush of stdout, else print! doesn't print instantly | 50 | let dl_path = String::from(&list.download_folder); |
44 | std::io::stdout().flush().unwrap(); | 51 | |
45 | let files = ver.files; | 52 | progress.set_message(String::from(&version.id)); |
46 | let file = match files.clone().into_iter().find(|f| f.primary) { | 53 | |
47 | Some(f) => f, | 54 | //Check cache if already downloaded |
48 | None => files[0].clone() | 55 | let c = cached.remove(&version.id); |
49 | }; | 56 | if c.is_some() { |
50 | let mut splitname: Vec<&str> = file.filename.split('.').collect(); | 57 | print!( |
51 | let extension = match splitname.pop().ok_or("") { | 58 | "\t└({})Get version {} from cache", |
52 | Ok(e) => e, | 59 | project_info.title, version.id |
53 | Err(..) => return Err(MLError::new(ErrorType::Other, "NO_FILE_EXTENSION")), | 60 | ); |
54 | }; | 61 | //Force flush of stdout, else print! doesn't print instantly |
55 | let filename = format!( | 62 | std::io::stdout().flush()?; |
56 | "{}.mr.{}.{}.{}", | 63 | copy_cached_version(&c.unwrap(), &dl_path); |
57 | splitname.join("."), | 64 | println!(" ✓"); |
58 | ver.project_id, | 65 | } else { |
59 | ver.id, | 66 | // print!("\t└({})Download version {}", project_info.title, version.id); |
60 | extension | 67 | //Force flush of stdout, else print! doesn't print instantly |
61 | ); | 68 | std::io::stdout().flush().unwrap(); |
62 | download_file( | 69 | let files = version.files; |
63 | file.url, | 70 | let file = match files.clone().into_iter().find(|f| f.primary) { |
64 | list.clone().download_folder, | 71 | Some(f) => f, |
65 | filename.clone(), | 72 | None => files[0].clone() |
66 | ) | 73 | }; |
67 | .await?; | 74 | let mut splitname: Vec<&str> = file.filename.split('.').collect(); |
68 | println!(" ✓"); | 75 | let extension = match splitname.pop().ok_or("") { |
69 | //Copy file to cache | 76 | Ok(e) => e, |
70 | print!("\t └Copy to cache"); | 77 | Err(..) => return Err(MLError::new(ErrorType::Other, "NO_FILE_EXTENSION")), |
71 | //Force flush of stdout, else print! doesn't print instantly | 78 | }; |
72 | std::io::stdout().flush().unwrap(); | 79 | let filename = format!( |
73 | let dl_path_file = format!("{}/{}", list.download_folder, filename); | 80 | "{}.mr.{}.{}.{}", |
74 | let cache_path = format!("{}/{}", &config.clone().cache, filename); | 81 | splitname.join("."), |
75 | // println!("{}:{}", dl_path_file, cache_path); | 82 | version.project_id, |
76 | copy(dl_path_file, cache_path)?; | 83 | version.id, |
77 | println!(" ✓"); | 84 | extension |
78 | } | 85 | ); |
86 | |||
87 | download_file( | ||
88 | &file.url, | ||
89 | &list.download_folder, | ||
90 | &filename, | ||
91 | &progress | ||
92 | ) | ||
93 | .await?; | ||
94 | // println!(" ✓"); | ||
95 | //Copy file to cache | ||
96 | // print!("\t └Copy to cache"); | ||
97 | //Force flush of stdout, else print! doesn't print instantly | ||
98 | std::io::stdout().flush().unwrap(); | ||
99 | let dl_path_file = format!("{}/{}", list.download_folder, filename); | ||
100 | let cache_path = format!("{}/{}", &config.clone().cache, filename); | ||
101 | // println!("{}:{}", dl_path_file, cache_path); | ||
102 | copy(dl_path_file, cache_path)?; | ||
103 | // println!(" ✓"); | ||
79 | } | 104 | } |
80 | 105 | ||
81 | Ok(dl_path) | 106 | progress.finish_with_message(format!("✓{}", version.id)); |
107 | |||
108 | Ok(()) | ||
82 | } | 109 | } |
83 | 110 | ||
84 | async fn download_file(url: String, path: String, name: String) -> MLE<()> { | 111 | async fn download_file(url: &str, path: &str, name: &str, progress: &ProgressBar) -> MLE<()> { |
85 | let dl_path_file = format!("{}/{}", path, name); | 112 | let dl_path_file = format!("{}/{}", path, name); |
86 | let res = Client::new().get(String::from(&url)).send().await?; | 113 | let res = Client::new().get(url).send().await?; |
114 | |||
115 | let size = res.content_length().expect("Couldn't get content length"); | ||
116 | |||
117 | progress.set_length(size); | ||
118 | // progress.set_style(ProgressStyle::with_template("{spinner:.green}{msg}\t[{wide_bar:.green/lime}] {bytes}/{total_bytes}").unwrap().progress_chars("#>-")); | ||
87 | 119 | ||
88 | // download chunks | 120 | // download chunks |
89 | let mut file = File::create(&dl_path_file)?; | 121 | let mut file = File::create(&dl_path_file)?; |
90 | let mut stream = res.bytes_stream(); | 122 | let mut stream = res.bytes_stream(); |
91 | 123 | ||
124 | let mut downloaded: u64 = 0; | ||
125 | |||
92 | while let Some(item) = stream.next().await { | 126 | while let Some(item) = stream.next().await { |
127 | progress.inc(1); | ||
93 | let chunk = item?; | 128 | let chunk = item?; |
94 | file.write_all(&chunk)?; | 129 | file.write_all(&chunk)?; |
130 | |||
131 | // Progress bar | ||
132 | let new = min(downloaded + (chunk.len() as u64), size); | ||
133 | downloaded = new; | ||
134 | progress.set_position(new); | ||
135 | |||
136 | // std::thread::sleep(std::time::Duration::from_millis(100)); | ||
95 | } | 137 | } |
96 | 138 | ||
97 | Ok(()) | 139 | Ok(()) |