summaryrefslogtreecommitdiff
path: root/src/files.rs
diff options
context:
space:
mode:
authorfxqnlr <[email protected]>2023-05-21 13:43:52 +0200
committerfxqnlr <[email protected]>2023-05-21 13:43:52 +0200
commit016e1d8d760113a64afcc5d516f08010cb566d68 (patch)
tree3de1a3bfbd3c7266dab5288240720133ffd2f602 /src/files.rs
parent5a2ea0755b29a8811aeeec1c73679c5783082628 (diff)
downloadmodlist-016e1d8d760113a64afcc5d516f08010cb566d68.tar
modlist-016e1d8d760113a64afcc5d516f08010cb566d68.tar.gz
modlist-016e1d8d760113a64afcc5d516f08010cb566d68.zip
added multithreaded downloads and progressbar
Diffstat (limited to 'src/files.rs')
-rw-r--r--src/files.rs160
1 files changed, 101 insertions, 59 deletions
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 @@
1use futures_util::StreamExt; 1use futures_util::StreamExt;
2use indicatif::{ProgressBar, ProgressStyle, MultiProgress};
2use reqwest::Client; 3use reqwest::Client;
4use tokio::task::JoinSet;
3use std::{ 5use 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
9use crate::{ 11use crate::{
@@ -15,83 +17,123 @@ use crate::{
15 List, 17 List,
16}; 18};
17 19
18pub async fn download_versions(list: List, config: Cfg, versions: Vec<Version>) -> MLE<String> { 20pub 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!(" ✓"); 47async 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
84async fn download_file(url: String, path: String, name: String) -> MLE<()> { 111async 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(())