summaryrefslogtreecommitdiff
path: root/src/files.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/files.rs')
-rw-r--r--src/files.rs199
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 @@
1use futures_util::StreamExt; 1use futures_util::StreamExt;
2use indicatif::{MultiProgress, ProgressBar, ProgressStyle};
2use reqwest::Client; 3use reqwest::Client;
3use std::{ 4use 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};
10use tokio::task::JoinSet;
8 11
9use crate::{ 12use 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
18pub async fn download_versions(list: List, config: Cfg, versions: Vec<Version>) -> MLE<String> { 21pub 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() 66async 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
84async fn download_file(url: String, path: String, name: String) -> MLE<()> { 125async 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
100pub fn disable_version( 165pub 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(&current_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
117pub fn delete_version(list: List, version: String) -> MLE<()> { 181pub 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
125pub fn get_file_path(list: List, versionid: String) -> MLE<String> { 189pub 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
169pub fn clean_list_dir(list: &List) -> MLE<()> { 235pub 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())?;