Claude: The file are now extracting, albeit slowly
This commit is contained in:
parent
6aea40bfce
commit
98def5fa73
@ -6,7 +6,7 @@ use std::sync::Arc;
|
|||||||
use tokio::sync::Mutex;
|
use tokio::sync::Mutex;
|
||||||
use log::{info, error};
|
use log::{info, error};
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::io::Read;
|
use std::io::{Read, Seek};
|
||||||
use crate::app::GameType;
|
use crate::app::GameType;
|
||||||
|
|
||||||
pub struct ExtractionManager {
|
pub struct ExtractionManager {
|
||||||
@ -35,7 +35,7 @@ impl ExtractionManager {
|
|||||||
return Err(anyhow::anyhow!("Archive file does not exist"));
|
return Err(anyhow::anyhow!("Archive file does not exist"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate 7z signature
|
// Validate 7z signature and get archive size
|
||||||
let mut file = fs::File::open(&archive_path)?;
|
let mut file = fs::File::open(&archive_path)?;
|
||||||
let mut signature = [0u8; 6];
|
let mut signature = [0u8; 6];
|
||||||
file.read_exact(&mut signature)?;
|
file.read_exact(&mut signature)?;
|
||||||
@ -54,8 +54,8 @@ impl ExtractionManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Get file size for progress calculation
|
// Get file size for progress calculation
|
||||||
let file_size = fs::metadata(archive_path)?.len();
|
let archive_size = fs::metadata(archive_path)?.len();
|
||||||
info!("Archive size: {} bytes", file_size);
|
info!("Archive size: {} bytes", archive_size);
|
||||||
|
|
||||||
// We'll handle extraction in a blocking task since sevenz-rust is synchronous
|
// We'll handle extraction in a blocking task since sevenz-rust is synchronous
|
||||||
let archive_path = archive_path.to_path_buf();
|
let archive_path = archive_path.to_path_buf();
|
||||||
@ -64,12 +64,58 @@ impl ExtractionManager {
|
|||||||
|
|
||||||
info!("Starting blocking extraction task");
|
info!("Starting blocking extraction task");
|
||||||
tokio::task::spawn_blocking(move || -> Result<()> {
|
tokio::task::spawn_blocking(move || -> Result<()> {
|
||||||
info!("Opening archive file for reading");
|
// Open the archive for processing
|
||||||
let archive_file = fs::File::open(&archive_path)?;
|
let archive_file = fs::File::open(&archive_path)?;
|
||||||
|
|
||||||
|
// Create a wrapper around the file that tracks read progress
|
||||||
|
struct ProgressReader<R: Read + Seek> {
|
||||||
|
inner: R,
|
||||||
|
bytes_read: u64,
|
||||||
|
total_size: u64,
|
||||||
|
last_progress: f32,
|
||||||
|
progress_callback: Arc<Mutex<Box<dyn Fn(f32, &str) + Send + Sync>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R: Read + Seek> Read for ProgressReader<R> {
|
||||||
|
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
|
||||||
|
let bytes = self.inner.read(buf)?;
|
||||||
|
self.bytes_read += bytes as u64;
|
||||||
|
|
||||||
|
// Calculate progress as a percentage
|
||||||
|
let progress = self.bytes_read as f32 / self.total_size as f32;
|
||||||
|
|
||||||
|
// Only update progress if it has changed by at least 1%
|
||||||
|
if progress - self.last_progress >= 0.01 {
|
||||||
|
self.last_progress = progress;
|
||||||
|
let progress_callback_clone = self.progress_callback.clone();
|
||||||
|
tokio::runtime::Handle::current().block_on(async move {
|
||||||
|
let callback = progress_callback_clone.lock().await;
|
||||||
|
callback(progress, "Extracting...");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(bytes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R: Read + Seek> Seek for ProgressReader<R> {
|
||||||
|
fn seek(&mut self, pos: std::io::SeekFrom) -> std::io::Result<u64> {
|
||||||
|
self.inner.seek(pos)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let progress_reader = ProgressReader {
|
||||||
|
inner: archive_file,
|
||||||
|
bytes_read: 0,
|
||||||
|
total_size: archive_size,
|
||||||
|
last_progress: 0.0,
|
||||||
|
progress_callback: progress_callback.clone(),
|
||||||
|
};
|
||||||
|
|
||||||
info!("Starting decompression to: {}", output_dir.display());
|
info!("Starting decompression to: {}", output_dir.display());
|
||||||
// decompress takes a Read + Seek and a destination directory
|
|
||||||
match decompress(&archive_file, &output_dir) {
|
// Decompress using our progress-tracking reader
|
||||||
|
match decompress(progress_reader, &output_dir) {
|
||||||
Ok(_) => info!("Decompression completed successfully"),
|
Ok(_) => info!("Decompression completed successfully"),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
error!("Decompression failed: {}", e);
|
error!("Decompression failed: {}", e);
|
||||||
@ -77,25 +123,11 @@ impl ExtractionManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// List contents of output directory to verify extraction
|
|
||||||
if let Ok(entries) = fs::read_dir(&output_dir) {
|
|
||||||
info!("Listing extracted contents:");
|
|
||||||
for entry in entries {
|
|
||||||
if let Ok(entry) = entry {
|
|
||||||
info!(" {}", entry.path().display());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Call progress update with 100% after decompression
|
// Call progress update with 100% after decompression
|
||||||
let filename = archive_path.file_name()
|
|
||||||
.and_then(|f| f.to_str())
|
|
||||||
.unwrap_or("archive");
|
|
||||||
|
|
||||||
let progress_callback_clone = progress_callback.clone();
|
let progress_callback_clone = progress_callback.clone();
|
||||||
tokio::runtime::Handle::current().block_on(async move {
|
tokio::runtime::Handle::current().block_on(async move {
|
||||||
let callback = progress_callback_clone.lock().await;
|
let callback = progress_callback_clone.lock().await;
|
||||||
callback(1.0, filename); // 100% done
|
callback(1.0, "Complete");
|
||||||
});
|
});
|
||||||
|
|
||||||
info!("Extraction task completed successfully");
|
info!("Extraction task completed successfully");
|
||||||
@ -124,32 +156,72 @@ impl ExtractionManager {
|
|||||||
let file = fs::File::open(&archive_path)?;
|
let file = fs::File::open(&archive_path)?;
|
||||||
let mut archive = zip::ZipArchive::new(file)?;
|
let mut archive = zip::ZipArchive::new(file)?;
|
||||||
|
|
||||||
let total_entries = archive.len();
|
// Calculate total uncompressed size
|
||||||
info!("ZIP archive contains {} entries", total_entries);
|
let total_size: u64 = {
|
||||||
|
let mut size = 0;
|
||||||
|
for i in 0..archive.len() {
|
||||||
|
if let Ok(file) = archive.by_index(i) {
|
||||||
|
size += file.size();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
size
|
||||||
|
};
|
||||||
|
|
||||||
for i in 0..total_entries {
|
info!("ZIP archive total uncompressed size: {} bytes", total_size);
|
||||||
|
|
||||||
|
let mut processed_bytes: u64 = 0;
|
||||||
|
|
||||||
|
// Process files in batches for better performance
|
||||||
|
let mut pending_dirs = Vec::new();
|
||||||
|
|
||||||
|
// First pass: create all directories (this prevents race conditions)
|
||||||
|
for i in 0..archive.len() {
|
||||||
|
let file = archive.by_index(i)?;
|
||||||
|
let outpath = match file.enclosed_name() {
|
||||||
|
Some(path) => output_dir.join(path),
|
||||||
|
None => continue,
|
||||||
|
};
|
||||||
|
|
||||||
|
if file.name().ends_with('/') {
|
||||||
|
pending_dirs.push(outpath);
|
||||||
|
} else if let Some(p) = outpath.parent() {
|
||||||
|
pending_dirs.push(p.to_path_buf());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create all directories at once
|
||||||
|
for dir in pending_dirs.iter().collect::<std::collections::HashSet<_>>() {
|
||||||
|
fs::create_dir_all(dir)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Second pass: extract all files
|
||||||
|
for i in 0..archive.len() {
|
||||||
let mut file = archive.by_index(i)?;
|
let mut file = archive.by_index(i)?;
|
||||||
let outpath = match file.enclosed_name() {
|
let outpath = match file.enclosed_name() {
|
||||||
Some(path) => output_dir.join(path),
|
Some(path) => output_dir.join(path),
|
||||||
None => continue,
|
None => continue,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Update progress
|
if !file.name().ends_with('/') {
|
||||||
let progress = (i + 1) as f32 / total_entries as f32;
|
|
||||||
let filename = outpath.file_name()
|
|
||||||
.and_then(|f| f.to_str())
|
|
||||||
.unwrap_or("unknown");
|
|
||||||
|
|
||||||
if (*file.name()).ends_with('/') {
|
|
||||||
fs::create_dir_all(&outpath)?;
|
|
||||||
} else {
|
|
||||||
if let Some(p) = outpath.parent() {
|
|
||||||
if !p.exists() {
|
|
||||||
fs::create_dir_all(p)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let mut outfile = fs::File::create(&outpath)?;
|
let mut outfile = fs::File::create(&outpath)?;
|
||||||
|
let file_size = file.size();
|
||||||
std::io::copy(&mut file, &mut outfile)?;
|
std::io::copy(&mut file, &mut outfile)?;
|
||||||
|
processed_bytes += file_size;
|
||||||
|
|
||||||
|
// Update progress based on processed bytes
|
||||||
|
let progress = processed_bytes as f32 / total_size as f32;
|
||||||
|
let filename = outpath.file_name()
|
||||||
|
.and_then(|f| f.to_str())
|
||||||
|
.unwrap_or("unknown");
|
||||||
|
|
||||||
|
// Update progress less frequently to reduce overhead
|
||||||
|
if i % 10 == 0 || progress >= 1.0 {
|
||||||
|
let progress_callback_clone = progress_callback.clone();
|
||||||
|
tokio::runtime::Handle::current().block_on(async move {
|
||||||
|
let callback = progress_callback_clone.lock().await;
|
||||||
|
callback(progress, filename);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fix file permissions on Unix
|
// Fix file permissions on Unix
|
||||||
@ -160,15 +232,15 @@ impl ExtractionManager {
|
|||||||
fs::set_permissions(&outpath, fs::Permissions::from_mode(mode))?;
|
fs::set_permissions(&outpath, fs::Permissions::from_mode(mode))?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update progress on the tokio runtime
|
|
||||||
let progress_callback_clone = progress_callback.clone();
|
|
||||||
tokio::runtime::Handle::current().block_on(async move {
|
|
||||||
let callback = progress_callback_clone.lock().await;
|
|
||||||
callback(progress, filename);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ensure we show 100% at the end
|
||||||
|
let progress_callback_clone = progress_callback.clone();
|
||||||
|
tokio::runtime::Handle::current().block_on(async move {
|
||||||
|
let callback = progress_callback_clone.lock().await;
|
||||||
|
callback(1.0, "Complete");
|
||||||
|
});
|
||||||
|
|
||||||
info!("ZIP extraction completed successfully");
|
info!("ZIP extraction completed successfully");
|
||||||
Ok(())
|
Ok(())
|
||||||
}).await??;
|
}).await??;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user