Claude: Added download speed and estimated remaining time
This commit is contained in:
parent
6f90e4bf68
commit
b2fcbbffe1
@ -27,6 +27,8 @@ pub enum InstallerState {
|
|||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct DownloadState {
|
pub struct DownloadState {
|
||||||
pub progress: f32,
|
pub progress: f32,
|
||||||
|
pub speed: f64,
|
||||||
|
pub estimated_remaining: std::time::Duration,
|
||||||
pub completed: bool,
|
pub completed: bool,
|
||||||
pub files: Option<Vec<PathBuf>>,
|
pub files: Option<Vec<PathBuf>>,
|
||||||
pub error: Option<String>,
|
pub error: Option<String>,
|
||||||
@ -38,6 +40,8 @@ pub struct SinfarInstallerApp<> {
|
|||||||
pub install_path: Option<PathBuf>,
|
pub install_path: Option<PathBuf>,
|
||||||
pub ee_exe_path: Option<PathBuf>,
|
pub ee_exe_path: Option<PathBuf>,
|
||||||
pub download_progress: f32,
|
pub download_progress: f32,
|
||||||
|
pub download_speed: f64,
|
||||||
|
pub estimated_remaining: std::time::Duration,
|
||||||
pub extraction_progress: f32,
|
pub extraction_progress: f32,
|
||||||
pub download_error: Option<String>,
|
pub download_error: Option<String>,
|
||||||
pub install_error: Option<String>,
|
pub install_error: Option<String>,
|
||||||
@ -47,13 +51,15 @@ pub struct SinfarInstallerApp<> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl 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 {
|
Self {
|
||||||
state: InstallerState::GameSelection,
|
state: InstallerState::GameSelection,
|
||||||
game_type: Some(GameType::Diamond),
|
game_type: Some(GameType::Diamond),
|
||||||
install_path: None,
|
install_path: None,
|
||||||
ee_exe_path: None,
|
ee_exe_path: None,
|
||||||
download_progress: 0.0,
|
download_progress: 0.0,
|
||||||
|
download_speed: 0.0,
|
||||||
|
estimated_remaining: std::time::Duration::new(0, 0),
|
||||||
extraction_progress: 0.0,
|
extraction_progress: 0.0,
|
||||||
download_error: None,
|
download_error: None,
|
||||||
install_error: None,
|
install_error: None,
|
||||||
@ -91,14 +97,8 @@ impl<> eframe::App for SinfarInstallerApp<> {
|
|||||||
if self.state == InstallerState::Download {
|
if self.state == InstallerState::Download {
|
||||||
if let Ok(state) = self.download_state.lock() {
|
if let Ok(state) = self.download_state.lock() {
|
||||||
self.download_progress = state.progress;
|
self.download_progress = state.progress;
|
||||||
|
self.download_speed = state.speed;
|
||||||
// if state.completed {
|
self.estimated_remaining = state.estimated_remaining;
|
||||||
// if let Some(files) = &state.files {
|
|
||||||
// // Store the files for installation
|
|
||||||
// //self.next_state(); // Proceed to installation
|
|
||||||
// //self.start_installation();
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
if let Some(error) = &state.error {
|
if let Some(error) = &state.error {
|
||||||
self.download_error = Some(error.clone());
|
self.download_error = Some(error.clone());
|
||||||
@ -178,12 +178,10 @@ impl<> eframe::App for SinfarInstallerApp<> {
|
|||||||
// Implementation of key installer functions
|
// Implementation of key installer functions
|
||||||
impl SinfarInstallerApp<> {
|
impl SinfarInstallerApp<> {
|
||||||
pub fn start_download(&mut self) {
|
pub fn start_download(&mut self) {
|
||||||
|
|
||||||
// let temp_dir = std::env::temp_dir().join("sinfar_installer");
|
|
||||||
// println!("{}", temp_dir.display())
|
|
||||||
|
|
||||||
// Reset progress and errors
|
// Reset progress and errors
|
||||||
self.download_progress = 0.0;
|
self.download_progress = 0.0;
|
||||||
|
self.download_speed = 0.0;
|
||||||
|
self.estimated_remaining = std::time::Duration::from_secs(0);
|
||||||
self.download_error = None;
|
self.download_error = None;
|
||||||
|
|
||||||
// Initialize download state
|
// Initialize download state
|
||||||
@ -203,10 +201,12 @@ impl SinfarInstallerApp<> {
|
|||||||
// Create a download manager with progress callback
|
// Create a download manager with progress callback
|
||||||
let progress_state = download_state.clone();
|
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
|
// Update progress through shared state
|
||||||
if let Ok(mut state) = progress_state.lock() {
|
if let Ok(mut state) = progress_state.lock() {
|
||||||
state.progress = progress;
|
state.progress = progress;
|
||||||
|
state.speed = speed;
|
||||||
|
state.estimated_remaining = remaining;
|
||||||
repaint_ctx_progress.request_repaint();
|
repaint_ctx_progress.request_repaint();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -9,20 +9,19 @@ use futures_util::StreamExt;
|
|||||||
use tokio::sync::Mutex;
|
use tokio::sync::Mutex;
|
||||||
use std::fs::metadata;
|
use std::fs::metadata;
|
||||||
use reqwest::header::{HeaderMap, HeaderValue, RANGE, USER_AGENT};
|
use reqwest::header::{HeaderMap, HeaderValue, RANGE, USER_AGENT};
|
||||||
use log::{info, warn, error};
|
use log::{info, warn};
|
||||||
use tokio::fs::File;
|
use crate::app::GameType;
|
||||||
use crate::app::{GameType};
|
|
||||||
|
|
||||||
pub struct DownloadManager {
|
pub struct DownloadManager {
|
||||||
client: Client,
|
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,
|
temp_dir: PathBuf,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DownloadManager {
|
impl DownloadManager {
|
||||||
pub fn new<F>(progress_callback: F) -> Self
|
pub fn new<F>(progress_callback: F) -> Self
|
||||||
where
|
where
|
||||||
F: Fn(f32, &str) + Send + Sync + 'static
|
F: Fn(f32, &str, f64, Duration) + Send + Sync + 'static
|
||||||
{
|
{
|
||||||
let client = Client::builder()
|
let client = Client::builder()
|
||||||
.timeout(Duration::from_secs(1800))
|
.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
|
/// Downloads a file with resume capability and progress tracking
|
||||||
pub async fn download_file(&self, url: &str, output_path: &Path) -> Result<()> {
|
pub async fn download_file(&self, url: &str, output_path: &Path) -> Result<()> {
|
||||||
info!("Starting download of {} to {}", url, output_path.display());
|
info!("Starting download of {} to {}", url, output_path.display());
|
||||||
@ -126,6 +132,8 @@ impl DownloadManager {
|
|||||||
.unwrap_or("file");
|
.unwrap_or("file");
|
||||||
|
|
||||||
let mut last_update = std::time::Instant::now();
|
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 {
|
while let Some(chunk_result) = stream.next().await {
|
||||||
let chunk = chunk_result?;
|
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 now.duration_since(last_update) > Duration::from_millis(100) || content_length == downloaded_size {
|
||||||
if content_length > 0 {
|
if content_length > 0 {
|
||||||
let progress = downloaded_size as f32 / content_length as f32;
|
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;
|
let callback = progress_callback.lock().await;
|
||||||
callback(progress, filename);
|
callback(progress, filename, speed, estimated_remaining);
|
||||||
}
|
}
|
||||||
last_update = now;
|
last_update = now;
|
||||||
|
last_downloaded_size = downloaded_size;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -166,7 +189,7 @@ impl DownloadManager {
|
|||||||
} else {
|
} else {
|
||||||
// File exists, update progress to 100%
|
// File exists, update progress to 100%
|
||||||
let callback = self.progress_callback.lock().await;
|
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);
|
downloaded_files.push(content_zip_path);
|
||||||
@ -182,7 +205,7 @@ impl DownloadManager {
|
|||||||
).await?;
|
).await?;
|
||||||
} else {
|
} else {
|
||||||
let callback = self.progress_callback.lock().await;
|
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);
|
downloaded_files.push(launcher_path);
|
||||||
@ -199,7 +222,7 @@ impl DownloadManager {
|
|||||||
).await?;
|
).await?;
|
||||||
} else {
|
} else {
|
||||||
let callback = self.progress_callback.lock().await;
|
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);
|
downloaded_files.push(launcher_zip_path);
|
||||||
@ -216,7 +239,7 @@ impl DownloadManager {
|
|||||||
).await?;
|
).await?;
|
||||||
} else {
|
} else {
|
||||||
let callback = self.progress_callback.lock().await;
|
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);
|
downloaded_files.push(launcher_zip_path);
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
pub mod downloader;
|
pub mod downloader;
|
||||||
pub mod downloaderv2;
|
|
||||||
pub mod extractor;
|
pub mod extractor;
|
||||||
pub mod validator;
|
pub mod validator;
|
||||||
pub mod shortcut;
|
pub mod shortcut;
|
@ -1,14 +1,47 @@
|
|||||||
pub mod download_progress {
|
pub mod download_progress {
|
||||||
use eframe::egui::{Ui, ProgressBar};
|
use eframe::egui::{Ui, ProgressBar};
|
||||||
use crate::app::SinfarInstallerApp;
|
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) {
|
pub fn render(ui: &mut Ui, app: &mut SinfarInstallerApp) {
|
||||||
ui.heading("Downloading Sinfar Custom Content");
|
ui.heading("Downloading Sinfar Custom Content");
|
||||||
ui.add_space(10.0);
|
ui.add_space(10.0);
|
||||||
|
|
||||||
ui.horizontal(|ui| {
|
ui.vertical(|ui| {
|
||||||
ui.label("Downloading:");
|
ui.horizontal(|ui| {
|
||||||
ui.add(ProgressBar::new(app.download_progress).show_percentage());
|
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 {
|
if let Some(error) = &app.download_error {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user