diff options
Diffstat (limited to 'src/files.rs')
-rw-r--r-- | src/files.rs | 199 |
1 files changed, 132 insertions, 67 deletions
diff --git a/src/files.rs b/src/files.rs index a4c128e..3a16c62 100644 --- a/src/files.rs +++ b/src/files.rs | |||
@@ -1,10 +1,13 @@ | |||
1 | use futures_util::StreamExt; | 1 | use futures_util::StreamExt; |
2 | use indicatif::{MultiProgress, ProgressBar, ProgressStyle}; | ||
2 | use reqwest::Client; | 3 | use reqwest::Client; |
3 | use std::{ | 4 | use std::{ |
5 | cmp::min, | ||
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, |
7 | }; | 9 | }; |
10 | use tokio::task::JoinSet; | ||
8 | 11 | ||
9 | use crate::{ | 12 | use crate::{ |
10 | cache::{copy_cached_version, get_cached_versions}, | 13 | cache::{copy_cached_version, get_cached_versions}, |
@@ -12,99 +15,160 @@ use crate::{ | |||
12 | db::{mods_get_info, userlist_add_disabled_versions}, | 15 | db::{mods_get_info, userlist_add_disabled_versions}, |
13 | error::{ErrorType, MLError, MLE}, | 16 | error::{ErrorType, MLError, MLE}, |
14 | modrinth::Version, | 17 | modrinth::Version, |
15 | List, | 18 | List, PROGRESS_CHARS, STYLE_BAR_BYTE, STYLE_BAR_POS, STYLE_SPINNER, |
16 | }; | 19 | }; |
17 | 20 | ||
18 | pub async fn download_versions(list: List, config: Cfg, versions: Vec<Version>) -> MLE<String> { | 21 | pub async fn download_versions( |
19 | let mut cached = get_cached_versions(&config.cache); | 22 | list: List, |
23 | config: Cfg, | ||
24 | versions: Vec<Version>, | ||
25 | progress: &MultiProgress, | ||
26 | progress_before: &ProgressBar, | ||
27 | ) -> MLE<()> { | ||
28 | let cached = get_cached_versions(&config.cache); | ||
20 | 29 | ||
21 | // println!("{:#?}", cached); | 30 | let mut js = JoinSet::new(); |
22 | 31 | ||
23 | let dl_path = String::from(&list.download_folder); | 32 | let style_spinner = ProgressStyle::with_template(STYLE_SPINNER).unwrap(); |
24 | 33 | ||
25 | println!(" └Download mods to {}", dl_path); | 34 | let all = progress.insert_before( |
35 | progress_before, | ||
36 | ProgressBar::new(versions.len().try_into().unwrap()), | ||
37 | ); | ||
38 | all.set_style( | ||
39 | ProgressStyle::with_template(STYLE_BAR_POS) | ||
40 | .unwrap() | ||
41 | .progress_chars(PROGRESS_CHARS), | ||
42 | ); | ||
43 | all.set_message(format!("✓Downloading {}", list.id)); | ||
26 | 44 | ||
27 | for ver in versions { | 45 | for ver in versions { |
28 | let project_info = mods_get_info(config.clone(), &ver.project_id)?; | 46 | let p = progress.insert_before(&all, ProgressBar::new(1)); |
29 | 47 | p.set_style(style_spinner.clone()); | |
30 | //Check cache if already downloaded | 48 | js.spawn(download_version( |
31 | let c = cached.remove(&ver.id); | 49 | config.clone(), |
32 | if c.is_some() { | 50 | list.clone(), |
33 | print!( | 51 | ver, |
34 | "\t└({})Get version {} from cache", | 52 | cached.clone(), |
35 | project_info.title, ver.id | 53 | p, |
36 | ); | 54 | )); |
37 | //Force flush of stdout, else print! doesn't print instantly | 55 | } |
38 | std::io::stdout().flush()?; | 56 | |
39 | copy_cached_version(&c.unwrap(), &dl_path); | 57 | while js.join_next().await.is_some() { |
40 | println!(" ✓"); | 58 | all.inc(1) |
41 | } else { | 59 | } |
42 | print!("\t└({})Download version {}", project_info.title, ver.id); | 60 | |
43 | //Force flush of stdout, else print! doesn't print instantly | 61 | all.finish_with_message(format!("✓Downloading {}", list.id)); |
44 | std::io::stdout().flush().unwrap(); | 62 | |
45 | let files = ver.files; | 63 | Ok(()) |
46 | let file = match files.clone().into_iter().find(|f| f.primary) { | 64 | } |
47 | Some(f) => f, | 65 | |
48 | None => files[0].clone() | 66 | async fn download_version( |
49 | }; | 67 | config: Cfg, |
50 | let mut splitname: Vec<&str> = file.filename.split('.').collect(); | 68 | list: List, |
51 | let extension = match splitname.pop().ok_or("") { | 69 | version: Version, |
52 | Ok(e) => e, | 70 | mut cached: HashMap<String, String>, |
53 | Err(..) => return Err(MLError::new(ErrorType::Other, "NO_FILE_EXTENSION")), | 71 | progress: ProgressBar, |
54 | }; | 72 | ) -> MLE<()> { |
55 | let filename = format!( | 73 | let project_info = mods_get_info(&config, &version.project_id)?; |
56 | "{}.mr.{}.{}.{}", | 74 | |
57 | splitname.join("."), | 75 | let dl_path = String::from(&list.download_folder); |
58 | ver.project_id, | 76 | |
59 | ver.id, | 77 | progress.set_message(format!("{} - {}", project_info.title, version.id)); |
60 | extension | 78 | |
61 | ); | 79 | let mut cache_msg = ""; |
62 | download_file( | 80 | //Check cache if already downloaded |
63 | file.url, | 81 | let c = cached.remove(&version.id); |
64 | list.clone().download_folder, | 82 | if c.is_some() { |
65 | filename.clone(), | 83 | progress.set_message(format!("Get {} from cache", version.id)); |
66 | ) | 84 | cache_msg = " (cached)"; |
85 | copy_cached_version(&c.unwrap(), &dl_path); | ||
86 | } else { | ||
87 | let files = version.files; | ||
88 | let file = match files.clone().into_iter().find(|f| f.primary) { | ||
89 | Some(f) => f, | ||
90 | None => files[0].clone(), | ||
91 | }; | ||
92 | let mut splitname: Vec<&str> = file.filename.split('.').collect(); | ||
93 | let extension = match splitname.pop().ok_or("") { | ||
94 | Ok(e) => e, | ||
95 | Err(..) => { | ||
96 | return Err(MLError::new(ErrorType::Other, "NO_FILE_EXTENSION")) | ||
97 | } | ||
98 | }; | ||
99 | let filename = format!( | ||
100 | "{}.mr.{}.{}.{}", | ||
101 | splitname.join("."), | ||
102 | version.project_id, | ||
103 | version.id, | ||
104 | extension | ||
105 | ); | ||
106 | |||
107 | download_file(&file.url, &list.download_folder, &filename, &progress) | ||
67 | .await?; | 108 | .await?; |
68 | println!(" ✓"); | 109 | |
69 | //Copy file to cache | 110 | progress.set_message(format!("Copy {} to cache", version.id)); |
70 | print!("\t └Copy to cache"); | 111 | let dl_path_file = format!("{}/{}", list.download_folder, filename); |
71 | //Force flush of stdout, else print! doesn't print instantly | 112 | let cache_path = format!("{}/{}", &config.cache, filename); |
72 | std::io::stdout().flush().unwrap(); | 113 | |
73 | let dl_path_file = format!("{}/{}", list.download_folder, filename); | 114 | copy(dl_path_file, cache_path)?; |
74 | let cache_path = format!("{}/{}", &config.clone().cache, filename); | ||
75 | // println!("{}:{}", dl_path_file, cache_path); | ||
76 | copy(dl_path_file, cache_path)?; | ||
77 | println!(" ✓"); | ||
78 | } | ||
79 | } | 115 | } |
80 | 116 | ||
81 | Ok(dl_path) | 117 | progress.finish_with_message(format!( |
118 | "✓{} - {}{}", | ||
119 | project_info.title, version.id, cache_msg | ||
120 | )); | ||
121 | |||
122 | Ok(()) | ||
82 | } | 123 | } |
83 | 124 | ||
84 | async fn download_file(url: String, path: String, name: String) -> MLE<()> { | 125 | async fn download_file( |
126 | url: &str, | ||
127 | path: &str, | ||
128 | name: &str, | ||
129 | progress: &ProgressBar, | ||
130 | ) -> MLE<()> { | ||
85 | let dl_path_file = format!("{}/{}", path, name); | 131 | let dl_path_file = format!("{}/{}", path, name); |
86 | let res = Client::new().get(String::from(&url)).send().await?; | 132 | let res = Client::new().get(url).send().await?; |
133 | |||
134 | let size = res.content_length().expect("Couldn't get content length"); | ||
135 | |||
136 | let style_bar_byte = ProgressStyle::with_template(STYLE_BAR_BYTE) | ||
137 | .unwrap() | ||
138 | .progress_chars(PROGRESS_CHARS); | ||
139 | |||
140 | progress.set_length(size); | ||
141 | progress.set_style(style_bar_byte); | ||
87 | 142 | ||
88 | // download chunks | 143 | // download chunks |
89 | let mut file = File::create(&dl_path_file)?; | 144 | let mut file = File::create(&dl_path_file)?; |
90 | let mut stream = res.bytes_stream(); | 145 | let mut stream = res.bytes_stream(); |
91 | 146 | ||
147 | let mut downloaded: u64 = 0; | ||
148 | |||
92 | while let Some(item) = stream.next().await { | 149 | while let Some(item) = stream.next().await { |
150 | // progress.inc(1); | ||
93 | let chunk = item?; | 151 | let chunk = item?; |
94 | file.write_all(&chunk)?; | 152 | file.write_all(&chunk)?; |
153 | |||
154 | // Progress bar | ||
155 | let new = min(downloaded + (chunk.len() as u64), size); | ||
156 | downloaded = new; | ||
157 | progress.set_position(new); | ||
158 | |||
159 | // std::thread::sleep(std::time::Duration::from_millis(100)); | ||
95 | } | 160 | } |
96 | 161 | ||
97 | Ok(()) | 162 | Ok(()) |
98 | } | 163 | } |
99 | 164 | ||
100 | pub fn disable_version( | 165 | pub fn disable_version( |
101 | config: Cfg, | 166 | config: &Cfg, |
102 | current_list: List, | 167 | current_list: List, |
103 | versionid: String, | 168 | versionid: String, |
104 | mod_id: String, | 169 | mod_id: String, |
105 | ) -> MLE<()> { | 170 | ) -> MLE<()> { |
106 | //println!("Disabling version {} for mod {}", versionid, mod_id); | 171 | let file = get_file_path(¤t_list, String::from(&versionid))?; |
107 | let file = get_file_path(current_list.clone(), String::from(&versionid))?; | ||
108 | let disabled = format!("{}.disabled", file); | 172 | let disabled = format!("{}.disabled", file); |
109 | 173 | ||
110 | rename(file, disabled)?; | 174 | rename(file, disabled)?; |
@@ -114,7 +178,7 @@ pub fn disable_version( | |||
114 | Ok(()) | 178 | Ok(()) |
115 | } | 179 | } |
116 | 180 | ||
117 | pub fn delete_version(list: List, version: String) -> MLE<()> { | 181 | pub fn delete_version(list: &List, version: String) -> MLE<()> { |
118 | let file = get_file_path(list, version)?; | 182 | let file = get_file_path(list, version)?; |
119 | 183 | ||
120 | remove_file(file)?; | 184 | remove_file(file)?; |
@@ -122,14 +186,16 @@ pub fn delete_version(list: List, version: String) -> MLE<()> { | |||
122 | Ok(()) | 186 | Ok(()) |
123 | } | 187 | } |
124 | 188 | ||
125 | pub fn get_file_path(list: List, versionid: String) -> MLE<String> { | 189 | pub fn get_file_path(list: &List, versionid: String) -> MLE<String> { |
126 | let mut names: HashMap<String, String> = HashMap::new(); | 190 | let mut names: HashMap<String, String> = HashMap::new(); |
127 | for file in read_dir(list.download_folder)? { | 191 | for file in read_dir(&list.download_folder)? { |
128 | let path = file?.path(); | 192 | let path = file?.path(); |
129 | if path.is_file() { | 193 | if path.is_file() { |
130 | let pathstr = match path.to_str().ok_or("") { | 194 | let pathstr = match path.to_str().ok_or("") { |
131 | Ok(s) => s, | 195 | Ok(s) => s, |
132 | Err(..) => return Err(MLError::new(ErrorType::Other, "INVALID_PATH")), | 196 | Err(..) => { |
197 | return Err(MLError::new(ErrorType::Other, "INVALID_PATH")) | ||
198 | } | ||
133 | }; | 199 | }; |
134 | let namesplit: Vec<&str> = pathstr.split('.').collect(); | 200 | let namesplit: Vec<&str> = pathstr.split('.').collect(); |
135 | let ver_id = namesplit[namesplit.len() - 2]; | 201 | let ver_id = namesplit[namesplit.len() - 2]; |
@@ -168,7 +234,6 @@ pub fn get_downloaded_versions(list: List) -> MLE<HashMap<String, String>> { | |||
168 | 234 | ||
169 | pub fn clean_list_dir(list: &List) -> MLE<()> { | 235 | pub fn clean_list_dir(list: &List) -> MLE<()> { |
170 | let dl_path = &list.download_folder; | 236 | let dl_path = &list.download_folder; |
171 | println!(" └Clean directory for: {}", list.id); | ||
172 | for entry in std::fs::read_dir(dl_path)? { | 237 | for entry in std::fs::read_dir(dl_path)? { |
173 | let entry = entry?; | 238 | let entry = entry?; |
174 | std::fs::remove_file(entry.path())?; | 239 | std::fs::remove_file(entry.path())?; |