Add unzip status
This commit is contained in:
4 files changed
+63
-21
No files matched your search
+21
-8
@@ -21,6 +21,7 @@ use tauri_plugin_prevent_default::Flags;
|
||||
use tokio::io::AsyncReadExt;
|
||||
use tokio::{io::AsyncWriteExt, time::timeout};
|
||||
use zip::ZipArchive;
|
||||
use zip::result::ZipError;
|
||||
|
||||
#[cfg(unix)]
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
@@ -46,20 +47,25 @@ fn should_skip(name: &str) -> bool {
|
||||
|| name.ends_with(".DS_Store")
|
||||
}
|
||||
|
||||
async fn unzip_to_dir(zip_path: PathBuf, out_dir: PathBuf) -> String {
|
||||
async fn unzip_to_dir(app: AppHandle, zip_path: PathBuf, out_dir: PathBuf, name: String) -> String {
|
||||
let _ = app.emit("unzip-start", format!("{}", name));
|
||||
let res = tauri::async_runtime::spawn_blocking(move || {
|
||||
let file = File::open(zip_path)?;
|
||||
let mut archive = ZipArchive::new(BufReader::new(file))?;
|
||||
let total = archive.len();
|
||||
|
||||
for i in 0..archive.len() {
|
||||
for i in 0..total {
|
||||
let mut entry = archive.by_index(i)?;
|
||||
let name = entry.name();
|
||||
let entry_name = entry.name();
|
||||
|
||||
if should_skip(name) {
|
||||
if should_skip(entry_name) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let outpath = out_dir.join(name);
|
||||
let outpath = out_dir.join(entry_name);
|
||||
if !outpath.starts_with(&out_dir) {
|
||||
return Err(ZipError::InvalidArchive("Path traversal detected".into()));
|
||||
}
|
||||
|
||||
if entry.is_dir() {
|
||||
create_dir_all(&outpath)?;
|
||||
@@ -81,6 +87,8 @@ async fn unzip_to_dir(zip_path: PathBuf, out_dir: PathBuf) -> String {
|
||||
std::fs::set_permissions(&outpath, std::fs::Permissions::from_mode(mode))?;
|
||||
}
|
||||
}
|
||||
|
||||
let _ = app.emit("unzip-progress", format!("{}:{}:{}", name, i + 1, total));
|
||||
}
|
||||
|
||||
Ok::<(), zip::result::ZipError>(())
|
||||
@@ -362,7 +370,6 @@ async fn download(
|
||||
}
|
||||
|
||||
println!("[download] Hash verified OK");
|
||||
let _ = app.emit("download-finishing", format!("{}", &name));
|
||||
|
||||
println!("[download] Renaming .part -> .zip: {:?}", download_zip_path);
|
||||
if let Err(e) = tokio::fs::rename(download_part_path.clone(), download_zip_path.clone()).await {
|
||||
@@ -375,7 +382,13 @@ async fn download(
|
||||
}
|
||||
|
||||
println!("[download] Unzipping to: {:?}", game_path.join(&name));
|
||||
let unzip_res = unzip_to_dir(download_zip_path.clone(), game_path.join(&name)).await;
|
||||
let unzip_res = unzip_to_dir(
|
||||
app,
|
||||
download_zip_path.clone(),
|
||||
game_path.join(&name),
|
||||
name.clone(),
|
||||
)
|
||||
.await;
|
||||
|
||||
println!("[download] Removing zip file: {:?}", download_zip_path);
|
||||
let _ = tokio::fs::remove_file(download_zip_path.clone()).await;
|
||||
@@ -391,7 +404,7 @@ async fn download(
|
||||
|
||||
println!(
|
||||
"[download] SUCCESS: '{}' downloaded, verified, and extracted",
|
||||
name
|
||||
&name
|
||||
);
|
||||
let _ = window.set_progress_bar(ProgressBarState {
|
||||
status: Some(ProgressBarStatus::None),
|
||||
|
||||
+25
-3
@@ -235,13 +235,33 @@ export default function RootLayout ({
|
||||
)
|
||||
|
||||
unlisteners.push(
|
||||
await listen<string>('download-finishing', event => {
|
||||
await listen<string>('unzip-start', event => {
|
||||
const displayName = event.payload
|
||||
setDownloadProgress(prev => {
|
||||
const i = prev.findIndex(d => d.version === displayName)
|
||||
if (i === -1) return prev
|
||||
const copy = [...prev]
|
||||
copy[i] = { ...copy[i], hash_checking: false, finishing: true }
|
||||
copy[i] = { ...copy[i], hash_checking: false, unzipping: true }
|
||||
return copy
|
||||
})
|
||||
})
|
||||
)
|
||||
|
||||
unlisteners.push(
|
||||
await listen<string>('unzip-progress', async event => {
|
||||
const [displayName, unzippedStr, unzipTotalStr] =
|
||||
event.payload.split(':')
|
||||
const unzipped = Number(unzippedStr)
|
||||
const unzipTotal = Number(unzipTotalStr)
|
||||
setDownloadProgress(prev => {
|
||||
const i = prev.findIndex(d => d.version === displayName)
|
||||
if (i === -1) return prev
|
||||
const copy = [...prev]
|
||||
copy[i] = {
|
||||
...copy[i],
|
||||
unzipped,
|
||||
unzipTotal
|
||||
}
|
||||
return copy
|
||||
})
|
||||
})
|
||||
@@ -331,9 +351,11 @@ export default function RootLayout ({
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
false,
|
||||
0,
|
||||
0
|
||||
)
|
||||
)
|
||||
|
||||
@@ -52,15 +52,20 @@ export default function DownloadsPopup () {
|
||||
Remove
|
||||
</button>
|
||||
</div>
|
||||
) : v.hash_checking || v.finishing ? (
|
||||
<span
|
||||
className={`${
|
||||
v.hash_checking ? 'text-blue-300' : 'text-green-300'
|
||||
} inline-block w-full text-center`}
|
||||
>
|
||||
{v.hash_checking ? 'Verifying file integerty' : 'Finishing'}
|
||||
...
|
||||
) : v.hash_checking ? (
|
||||
<span className='text-blue-300 inline-block w-full text-center'>
|
||||
Verifying file integerty...
|
||||
</span>
|
||||
) : v.unzipping ? (
|
||||
<div className='flex flex-col gap-1 w-full'>
|
||||
<span className='text-center'>
|
||||
Unzipped {v.unzipped} / {v.unzipTotal} files
|
||||
</span>
|
||||
<ProgressBar
|
||||
progress={(v.unzipped / v.unzipTotal) * 100}
|
||||
className='w-full'
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<div className='flex flex-col gap-1 w-full'>
|
||||
<span className='text-center'>
|
||||
|
||||
@@ -6,9 +6,11 @@ export class DownloadProgress {
|
||||
public failed: boolean,
|
||||
public queued: boolean,
|
||||
public hash_checking: boolean,
|
||||
public finishing: boolean,
|
||||
public speed: number,
|
||||
public size: number,
|
||||
public etaSecs: number
|
||||
public etaSecs: number,
|
||||
public unzipping: boolean,
|
||||
public unzipTotal: number,
|
||||
public unzipped: number
|
||||
) {}
|
||||
}
|
||||
Reference in new issue
Block a user