Claude: Fine tuned download progress screen
This commit is contained in:
parent
ae06bec3ed
commit
9bc602c136
@ -97,8 +97,12 @@ impl<> eframe::App for SinfarInstallerApp<> {
|
||||
|
||||
if self.state == InstallerState::Download {
|
||||
if let Ok(state) = self.download_state.lock() {
|
||||
// Only update progress if not cancelled
|
||||
if !state.cancelled {
|
||||
// Set progress to 100% if completed
|
||||
if state.completed {
|
||||
self.download_progress = 1.0;
|
||||
self.download_speed = 0.0;
|
||||
self.estimated_remaining = std::time::Duration::from_secs(0);
|
||||
} else if !state.cancelled {
|
||||
self.download_progress = state.progress;
|
||||
self.download_speed = state.speed;
|
||||
self.estimated_remaining = state.estimated_remaining;
|
||||
|
@ -62,65 +62,119 @@ impl DownloadManager {
|
||||
}
|
||||
}
|
||||
|
||||
// Check if we can resume a previous download
|
||||
|
||||
// Prepare headers for potential resume
|
||||
let mut headers = HeaderMap::new();
|
||||
headers.insert(USER_AGENT, HeaderValue::from_static("Sinfar NWN Installer"));
|
||||
|
||||
let mut downloaded_size: u64 = 0;
|
||||
|
||||
if output_path.exists() {
|
||||
if let Ok(metadata) = tokio::fs::metadata(output_path).await {
|
||||
downloaded_size = metadata.len();
|
||||
if downloaded_size > 0 {
|
||||
info!("Resuming download from byte {}", downloaded_size);
|
||||
headers.insert(RANGE, HeaderValue::from_str(&format!("bytes={}-", downloaded_size))?);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let response = self.client.get(url)
|
||||
// First make a HEAD request to get the total file size
|
||||
let response_head = self.client.get(url)
|
||||
.headers(headers)
|
||||
.send()
|
||||
.await
|
||||
.context("Failed to send HTTP request")?;
|
||||
|
||||
// Check if the server supports resuming
|
||||
let can_resume = response.status().is_success() && response.status().as_u16() == 206;
|
||||
let status = response.status();
|
||||
let status = response_head.status();
|
||||
|
||||
if !status.is_success() {
|
||||
return Err(anyhow!("Server returned error status: {}", status));
|
||||
}
|
||||
|
||||
// Get total file size
|
||||
let content_length = if can_resume {
|
||||
if let Some(content_range) = response.headers().get("content-range") {
|
||||
// Get content length for progress calculation
|
||||
let total_size = response_head.content_length().unwrap_or(0);
|
||||
let content_length = if status.as_u16() == 206 {
|
||||
if let Some(content_range) = response_head.headers().get("content-range") {
|
||||
let range_str = content_range.to_str().unwrap_or("");
|
||||
if let Some(size_part) = range_str.split('/').nth(1) {
|
||||
size_part.parse::<u64>().unwrap_or(0)
|
||||
} else {
|
||||
response.content_length().unwrap_or(0) + downloaded_size
|
||||
total_size
|
||||
}
|
||||
} else {
|
||||
response.content_length().unwrap_or(0) + downloaded_size
|
||||
total_size
|
||||
}
|
||||
} else {
|
||||
response.content_length().unwrap_or(0)
|
||||
response_head.content_length().unwrap_or(total_size)
|
||||
};
|
||||
|
||||
info!("Download size: {} bytes", content_length);
|
||||
// Check if we have an existing file and its size
|
||||
let mut downloaded_size: u64 = 0;
|
||||
if output_path.exists() {
|
||||
let metadata = tokio::fs::metadata(output_path).await?;
|
||||
downloaded_size = metadata.len();
|
||||
|
||||
// Open file in appropriate mode (append if resuming, create/truncate if starting fresh)
|
||||
let mut file = if can_resume && downloaded_size > 0 {
|
||||
info!("Opening file for append");
|
||||
// If the file size matches exactly, we're done
|
||||
if content_length > 0 && downloaded_size == content_length {
|
||||
info!("File is already completely downloaded: {} bytes", content_length);
|
||||
// Call progress callback with 100%
|
||||
let progress_callback = self.progress_callback.clone();
|
||||
let filename = output_path.file_name()
|
||||
.and_then(|f| f.to_str())
|
||||
.unwrap_or("file");
|
||||
let callback = progress_callback.lock().await;
|
||||
callback(1.0, filename, 0.0, Duration::from_secs(0));
|
||||
return Ok(());
|
||||
} else if downloaded_size > content_length {
|
||||
// If local file is larger than remote, start fresh
|
||||
info!("Local file size mismatch ({} > {}), starting fresh download", downloaded_size, content_length);
|
||||
downloaded_size = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Prepare headers for potential resume
|
||||
let mut headers_toresume = HeaderMap::new();
|
||||
headers_toresume.insert(USER_AGENT, HeaderValue::from_static("Sinfar NWN Installer"));
|
||||
|
||||
if downloaded_size > 0 {
|
||||
info!("Attempting to resume from byte {}", downloaded_size);
|
||||
headers_toresume.insert(RANGE, HeaderValue::from_str(&format!("bytes={}-", downloaded_size))?);
|
||||
}
|
||||
|
||||
//Make the actual download request
|
||||
let response = self.client.get(url)
|
||||
.headers(headers_toresume)
|
||||
.send()
|
||||
.await
|
||||
.context("Failed to send HTTP request")?;
|
||||
|
||||
let status = response.status();
|
||||
|
||||
// Handle 416 Range Not Satisfiable specifically
|
||||
if status.as_u16() == 416 {
|
||||
// This can happen if the file is already complete
|
||||
if downloaded_size > 0 && downloaded_size == total_size {
|
||||
info!("File appears to be complete, got 416 with matching file size");
|
||||
let progress_callback = self.progress_callback.clone();
|
||||
let filename = output_path.file_name()
|
||||
.and_then(|f| f.to_str())
|
||||
.unwrap_or("file");
|
||||
let callback = progress_callback.lock().await;
|
||||
callback(1.0, filename, 0.0, Duration::from_secs(0));
|
||||
return Ok(());
|
||||
}
|
||||
// If sizes don't match, start fresh
|
||||
info!("Got 416 error but sizes don't match, starting fresh download");
|
||||
downloaded_size = 0;
|
||||
// Make a new request without range header
|
||||
let response = self.client.get(url)
|
||||
.header(USER_AGENT, "Sinfar NWN Installer")
|
||||
.send()
|
||||
.await
|
||||
.context("Failed to send HTTP request")?;
|
||||
if !response.status().is_success() {
|
||||
return Err(anyhow!("Server returned error status: {}", response.status()));
|
||||
}
|
||||
} else if !status.is_success() {
|
||||
return Err(anyhow!("Server returned error status: {}", status));
|
||||
}
|
||||
|
||||
info!("Total download size: {} bytes", content_length);
|
||||
|
||||
// Open file in appropriate mode
|
||||
let mut file = if downloaded_size > 0 {
|
||||
info!("Resuming download from byte {}", downloaded_size);
|
||||
tokio::fs::OpenOptions::new()
|
||||
.write(true)
|
||||
.append(true)
|
||||
.open(output_path)
|
||||
.await?
|
||||
} else {
|
||||
info!("Creating new file");
|
||||
downloaded_size = 0;
|
||||
info!("Starting fresh download");
|
||||
tokio::fs::File::create(output_path).await?
|
||||
};
|
||||
|
||||
@ -179,18 +233,11 @@ impl DownloadManager {
|
||||
// Always download the content files
|
||||
let content_zip_path = self.temp_dir.join("sinfar_all_files_v30.7z");
|
||||
|
||||
// Check if file already exists and is of expected size
|
||||
if !self.file_exists_with_size(&content_zip_path, 100_000_000) {
|
||||
// File doesn't exist or is too small, download it
|
||||
// Always try to download/resume the content files - don't skip based on existing size
|
||||
self.download_file(
|
||||
"https://sinfar.net/haks/sinfar_all_files_v30.7z",
|
||||
&content_zip_path
|
||||
).await?;
|
||||
} else {
|
||||
// File exists, update progress to 100%
|
||||
let callback = self.progress_callback.lock().await;
|
||||
callback(1.0, "sinfar_all_files_v30.7z", 0.0, Duration::from_secs(0));
|
||||
}
|
||||
|
||||
downloaded_files.push(content_zip_path);
|
||||
|
||||
@ -198,15 +245,11 @@ impl DownloadManager {
|
||||
if *game_type == GameType::Diamond {
|
||||
let launcher_path = self.temp_dir.join("sinfarx.exe");
|
||||
|
||||
if !self.file_exists_with_size(&launcher_path, 10_000) {
|
||||
// Always attempt download/resume
|
||||
self.download_file(
|
||||
"https://nwn.sinfar.net/files/sinfarx.exe",
|
||||
&launcher_path
|
||||
).await?;
|
||||
} else {
|
||||
let callback = self.progress_callback.lock().await;
|
||||
callback(1.0, "sinfarx.exe", 0.0, Duration::from_secs(0));
|
||||
}
|
||||
|
||||
downloaded_files.push(launcher_path);
|
||||
} else if *game_type == GameType::EnhancedEdition {
|
||||
@ -215,15 +258,11 @@ impl DownloadManager {
|
||||
{
|
||||
let launcher_zip_path = self.temp_dir.join("sinfarLauncher.zip");
|
||||
|
||||
if !self.file_exists_with_size(&launcher_zip_path, 10_000) {
|
||||
// Always attempt download/resume
|
||||
self.download_file(
|
||||
"https://nwn.sinfar.net/files/sinfarx/8181/win32_8181.zip",
|
||||
&launcher_zip_path
|
||||
).await?;
|
||||
} else {
|
||||
let callback = self.progress_callback.lock().await;
|
||||
callback(1.0, "sinfarLauncher.zip", 0.0, Duration::from_secs(0));
|
||||
}
|
||||
|
||||
downloaded_files.push(launcher_zip_path);
|
||||
}
|
||||
@ -232,15 +271,11 @@ impl DownloadManager {
|
||||
{
|
||||
let launcher_zip_path = self.temp_dir.join("sinfarLauncher.zip");
|
||||
|
||||
if !self.file_exists_with_size(&launcher_zip_path, 10_000) {
|
||||
// Always attempt download/resume
|
||||
self.download_file(
|
||||
"https://nwn.sinfar.net/files/sinfarx/8181/linux_8181.zip",
|
||||
&launcher_zip_path
|
||||
).await?;
|
||||
} else {
|
||||
let callback = self.progress_callback.lock().await;
|
||||
callback(1.0, "sinfarLauncher.zip", 0.0, Duration::from_secs(0));
|
||||
}
|
||||
|
||||
downloaded_files.push(launcher_zip_path);
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
pub mod download_progress {
|
||||
use eframe::egui::{Ui, ProgressBar};
|
||||
use eframe::egui::{Ui, ProgressBar, Color32};
|
||||
use crate::app::SinfarInstallerApp;
|
||||
use std::time::Duration;
|
||||
|
||||
@ -35,18 +35,22 @@ pub mod download_progress {
|
||||
ui.add(ProgressBar::new(app.download_progress).show_percentage());
|
||||
});
|
||||
|
||||
if app.download_progress > 0.0 && app.download_progress < 1.0 {
|
||||
if let Ok(state) = app.download_state.lock() {
|
||||
if state.completed {
|
||||
ui.colored_label(Color32::GREEN, "Download Complete");
|
||||
} else if app.download_progress > 0.0 && app.download_progress < 1.0 {
|
||||
ui.horizontal(|ui| {
|
||||
ui.label(format_bytes(app.download_speed));
|
||||
ui.label(" • ");
|
||||
ui.label(format_duration(app.estimated_remaining));
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
ui.add_space(10.0);
|
||||
|
||||
// Add cancel button during active download
|
||||
// Add cancel button only during active download
|
||||
if app.download_progress > 0.0 && app.download_progress < 1.0 {
|
||||
if ui.button("Cancel Download").clicked() {
|
||||
app.cancel_download();
|
||||
@ -54,7 +58,7 @@ pub mod download_progress {
|
||||
}
|
||||
|
||||
if let Some(error) = &app.download_error {
|
||||
ui.colored_label(eframe::egui::Color32::RED, format!("Error: {}", error));
|
||||
ui.colored_label(Color32::RED, format!("Error: {}", error));
|
||||
if ui.button("Retry").clicked() {
|
||||
app.download_error = None;
|
||||
app.download_progress = 0.0;
|
||||
|
Loading…
x
Reference in New Issue
Block a user