Claude: Added download speed and estimated remaining time

This commit is contained in:
Fiery Imp 2025-04-23 16:44:54 -04:00
parent 6f90e4bf68
commit b2fcbbffe1
4 changed files with 83 additions and 28 deletions

View File

@ -27,6 +27,8 @@ pub enum InstallerState {
#[derive(Default)]
pub struct DownloadState {
pub progress: f32,
pub speed: f64,
pub estimated_remaining: std::time::Duration,
pub completed: bool,
pub files: Option<Vec<PathBuf>>,
pub error: Option<String>,
@ -38,6 +40,8 @@ pub struct SinfarInstallerApp<> {
pub install_path: Option<PathBuf>,
pub ee_exe_path: Option<PathBuf>,
pub download_progress: f32,
pub download_speed: f64,
pub estimated_remaining: std::time::Duration,
pub extraction_progress: f32,
pub download_error: Option<String>,
pub install_error: Option<String>,
@ -47,13 +51,15 @@ pub struct SinfarInstallerApp<> {
}
impl SinfarInstallerApp<> {
pub fn new(cc: &eframe::CreationContext<>, runtime: Arc<tokio::runtime::Runtime>) -> Self {
pub fn new(_cc: &eframe::CreationContext<>, runtime: Arc<tokio::runtime::Runtime>) -> Self {
Self {
state: InstallerState::GameSelection,
game_type: Some(GameType::Diamond),
install_path: None,
ee_exe_path: None,
download_progress: 0.0,
download_speed: 0.0,
estimated_remaining: std::time::Duration::new(0, 0),
extraction_progress: 0.0,
download_error: None,
install_error: None,
@ -91,14 +97,8 @@ impl<> eframe::App for SinfarInstallerApp<> {
if self.state == InstallerState::Download {
if let Ok(state) = self.download_state.lock() {
self.download_progress = state.progress;
// if state.completed {
// if let Some(files) = &state.files {
// // Store the files for installation
// //self.next_state(); // Proceed to installation
// //self.start_installation();
// }
// }
self.download_speed = state.speed;
self.estimated_remaining = state.estimated_remaining;
if let Some(error) = &state.error {
self.download_error = Some(error.clone());
@ -178,12 +178,10 @@ impl<> eframe::App for SinfarInstallerApp<> {
// Implementation of key installer functions
impl SinfarInstallerApp<> {
pub fn start_download(&mut self) {
// let temp_dir = std::env::temp_dir().join("sinfar_installer");
// println!("{}", temp_dir.display())
// Reset progress and errors
self.download_progress = 0.0;
self.download_speed = 0.0;
self.estimated_remaining = std::time::Duration::from_secs(0);
self.download_error = None;
// Initialize download state
@ -203,10 +201,12 @@ impl SinfarInstallerApp<> {
// Create a download manager with progress callback
let progress_state = download_state.clone();
let manager = DownloadManager::new(move |progress, _file| {
let manager = DownloadManager::new(move |progress, _file, speed, remaining| {
// Update progress through shared state
if let Ok(mut state) = progress_state.lock() {
state.progress = progress;
state.speed = speed;
state.estimated_remaining = remaining;
repaint_ctx_progress.request_repaint();
}
});

View File

@ -9,20 +9,19 @@ use futures_util::StreamExt;
use tokio::sync::Mutex;
use std::fs::metadata;
use reqwest::header::{HeaderMap, HeaderValue, RANGE, USER_AGENT};
use log::{info, warn, error};
use tokio::fs::File;
use crate::app::{GameType};
use log::{info, warn};
use crate::app::GameType;
pub struct DownloadManager {
client: Client,
progress_callback: Arc<Mutex<Box<dyn Fn(f32, &str) + Send + Sync>>>,
progress_callback: Arc<Mutex<Box<dyn Fn(f32, &str, f64, Duration) + Send + Sync>>>,
temp_dir: PathBuf,
}
impl DownloadManager {
pub fn new<F>(progress_callback: F) -> Self
where
F: Fn(f32, &str) + Send + Sync + 'static
F: Fn(f32, &str, f64, Duration) + Send + Sync + 'static
{
let client = Client::builder()
.timeout(Duration::from_secs(1800))
@ -45,6 +44,13 @@ impl DownloadManager {
}
}
pub fn set_progress_callback<F>(&mut self, callback: F)
where
F: Fn(f32, &str, f64, Duration) + Send + Sync + 'static,
{
self.progress_callback = Arc::new(Mutex::new(Box::new(callback)));
}
/// Downloads a file with resume capability and progress tracking
pub async fn download_file(&self, url: &str, output_path: &Path) -> Result<()> {
info!("Starting download of {} to {}", url, output_path.display());
@ -126,6 +132,8 @@ impl DownloadManager {
.unwrap_or("file");
let mut last_update = std::time::Instant::now();
let mut last_downloaded_size = downloaded_size;
let start_time = std::time::Instant::now();
while let Some(chunk_result) = stream.next().await {
let chunk = chunk_result?;
@ -138,10 +146,25 @@ impl DownloadManager {
if now.duration_since(last_update) > Duration::from_millis(100) || content_length == downloaded_size {
if content_length > 0 {
let progress = downloaded_size as f32 / content_length as f32;
// Calculate speed in bytes per second
let elapsed = now.duration_since(last_update).as_secs_f64();
let bytes_since_last = downloaded_size - last_downloaded_size;
let speed = bytes_since_last as f64 / elapsed;
// Calculate estimated time remaining
let remaining_bytes = content_length - downloaded_size;
let estimated_remaining = if speed > 0.0 {
Duration::from_secs_f64(remaining_bytes as f64 / speed)
} else {
Duration::from_secs(0)
};
let callback = progress_callback.lock().await;
callback(progress, filename);
callback(progress, filename, speed, estimated_remaining);
}
last_update = now;
last_downloaded_size = downloaded_size;
}
}
@ -166,7 +189,7 @@ impl DownloadManager {
} else {
// File exists, update progress to 100%
let callback = self.progress_callback.lock().await;
callback(1.0, "sinfar_all_files_v30.7z");
callback(1.0, "sinfar_all_files_v30.7z", 0.0, Duration::from_secs(0));
}
downloaded_files.push(content_zip_path);
@ -182,7 +205,7 @@ impl DownloadManager {
).await?;
} else {
let callback = self.progress_callback.lock().await;
callback(1.0, "sinfarx.exe");
callback(1.0, "sinfarx.exe", 0.0, Duration::from_secs(0));
}
downloaded_files.push(launcher_path);
@ -199,7 +222,7 @@ impl DownloadManager {
).await?;
} else {
let callback = self.progress_callback.lock().await;
callback(1.0, "sinfarLauncher.zip");
callback(1.0, "sinfarLauncher.zip", 0.0, Duration::from_secs(0));
}
downloaded_files.push(launcher_zip_path);
@ -216,7 +239,7 @@ impl DownloadManager {
).await?;
} else {
let callback = self.progress_callback.lock().await;
callback(1.0, "sinfarLauncher.zip");
callback(1.0, "sinfarLauncher.zip", 0.0, Duration::from_secs(0));
}
downloaded_files.push(launcher_zip_path);

View File

@ -1,5 +1,4 @@
pub mod downloader;
pub mod downloaderv2;
pub mod extractor;
pub mod validator;
pub mod shortcut;

View File

@ -1,14 +1,47 @@
pub mod download_progress {
use eframe::egui::{Ui, ProgressBar};
use crate::app::SinfarInstallerApp;
use std::time::Duration;
fn format_bytes(bytes: f64) -> String {
const KB: f64 = 1024.0;
const MB: f64 = KB * 1024.0;
if bytes >= MB {
format!("{:.1} MB/s", bytes / MB)
} else if bytes >= KB {
format!("{:.1} KB/s", bytes / KB)
} else {
format!("{:.0} B/s", bytes)
}
}
fn format_duration(duration: Duration) -> String {
let secs = duration.as_secs();
if secs >= 3600 {
format!("{:02}:{:02}:{:02} remaining", secs / 3600, (secs % 3600) / 60, secs % 60)
} else {
format!("{:02}:{:02} remaining", secs / 60, secs % 60)
}
}
pub fn render(ui: &mut Ui, app: &mut SinfarInstallerApp) {
ui.heading("Downloading Sinfar Custom Content");
ui.add_space(10.0);
ui.horizontal(|ui| {
ui.label("Downloading:");
ui.add(ProgressBar::new(app.download_progress).show_percentage());
ui.vertical(|ui| {
ui.horizontal(|ui| {
ui.label("Downloading:");
ui.add(ProgressBar::new(app.download_progress).show_percentage());
});
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));
});
}
});
if let Some(error) = &app.download_error {