Claude: The file are now extracting, albeit slowly

This commit is contained in:
Fiery Imp 2025-04-23 22:18:40 -04:00
parent 6aea40bfce
commit 98def5fa73

View File

@ -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,33 +156,73 @@ 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 mut outfile = fs::File::create(&outpath)?;
let file_size = file.size();
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() let filename = outpath.file_name()
.and_then(|f| f.to_str()) .and_then(|f| f.to_str())
.unwrap_or("unknown"); .unwrap_or("unknown");
if (*file.name()).ends_with('/') { // Update progress less frequently to reduce overhead
fs::create_dir_all(&outpath)?; if i % 10 == 0 || progress >= 1.0 {
} else { let progress_callback_clone = progress_callback.clone();
if let Some(p) = outpath.parent() { tokio::runtime::Handle::current().block_on(async move {
if !p.exists() { let callback = progress_callback_clone.lock().await;
fs::create_dir_all(p)?; callback(progress, filename);
});
} }
} }
let mut outfile = fs::File::create(&outpath)?;
std::io::copy(&mut file, &mut outfile)?;
}
// Fix file permissions on Unix // Fix file permissions on Unix
#[cfg(unix)] #[cfg(unix)]
@ -160,14 +232,14 @@ 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 // Ensure we show 100% at the end
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(progress, filename); callback(1.0, "Complete");
}); });
}
info!("ZIP extraction completed successfully"); info!("ZIP extraction completed successfully");
Ok(()) Ok(())