feat(ui): better downloading information
This commit is contained in:
parent
557b0e4b7b
commit
44897b3e7f
8 changed files with 103 additions and 20 deletions
|
|
@ -804,16 +804,28 @@ async fn install_package(
|
|||
.get_version(project_id, version_id, &state.services)
|
||||
.await?;
|
||||
|
||||
PackageStore::install_to_cluster(
|
||||
let session = oneclient_core::notification::GroupedProgressSession::start(
|
||||
&state.services.notifier,
|
||||
format!("Installing {}", project.name),
|
||||
);
|
||||
let size = version.primary_file().map(|f| f.size).unwrap_or(0);
|
||||
let child = session.child(project.name.clone(), size);
|
||||
|
||||
let result = PackageStore::install_to_cluster(
|
||||
provider,
|
||||
&project,
|
||||
&version,
|
||||
cluster_id,
|
||||
false,
|
||||
false,
|
||||
Some(&child),
|
||||
&state.services,
|
||||
)
|
||||
.await?;
|
||||
.await;
|
||||
|
||||
child.finish();
|
||||
session.finish();
|
||||
result?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -57,6 +57,7 @@ struct TaskLine {
|
|||
struct GroupedAgg {
|
||||
children: HashMap<Uuid, TaskLine>,
|
||||
done_units: u64,
|
||||
carried: u64,
|
||||
}
|
||||
|
||||
impl GroupedAgg {
|
||||
|
|
@ -82,7 +83,8 @@ impl GroupedAgg {
|
|||
}
|
||||
|
||||
fn downloaded_bytes(&self) -> u64 {
|
||||
self.done_units
|
||||
self.carried
|
||||
+ self.done_units
|
||||
+ self
|
||||
.children
|
||||
.values()
|
||||
|
|
@ -260,8 +262,12 @@ impl Component for OnboardingDownloading {
|
|||
let is_running = *running.read();
|
||||
let is_complete = *complete.read();
|
||||
|
||||
let global = if total == 0 {
|
||||
if is_complete { 100.0 } else { 0.0 }
|
||||
let global = if is_complete {
|
||||
100.0
|
||||
} else if total == 0 {
|
||||
0.0
|
||||
} else if total_estimate_now > 0 {
|
||||
(agg_now.downloaded_bytes() as f32 / total_estimate_now as f32 * 100.0).clamp(0.0, 99.0)
|
||||
} else {
|
||||
((done as f32 + agg_now.fraction()) / total as f32 * 100.0).clamp(0.0, 100.0)
|
||||
};
|
||||
|
|
@ -531,7 +537,7 @@ fn run_install_batch(plans: Vec<ClusterPlan>, predownload: bool, handles: Instal
|
|||
|
||||
let downloaded = agg.peek().downloaded_bytes();
|
||||
let delta = downloaded.saturating_sub(prev);
|
||||
let inst = delta as f64 / 0.5;
|
||||
let inst = delta as f64;
|
||||
|
||||
speed = if speed <= 0.0 {
|
||||
inst
|
||||
|
|
@ -755,6 +761,12 @@ fn apply_grouped(agg: &mut GroupedAgg, event: GroupedProgressEvent) {
|
|||
}
|
||||
}
|
||||
GroupedProgressEvent::End { .. } => {
|
||||
agg.carried += agg.done_units
|
||||
+ agg
|
||||
.children
|
||||
.values()
|
||||
.map(|t| t.current.min(t.total))
|
||||
.sum::<u64>();
|
||||
agg.children.clear();
|
||||
agg.done_units = 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ use crate::bundles::error::BundleError;
|
|||
use crate::bundles::manager::BundlesManager;
|
||||
use crate::bundles::overrides;
|
||||
use crate::bundles::types::{BundleArchive, BundleFile, BundleFileKind};
|
||||
use crate::notification::{GroupedProgressSession, TaskPhase};
|
||||
use crate::notification::{GroupedProgressChild, GroupedProgressSession, TaskPhase};
|
||||
use crate::packages::domain::{ContentType, GameLoader};
|
||||
use crate::packages::error::PackageError;
|
||||
use crate::packages::store::{PackageStore, unlink_cluster_file};
|
||||
|
|
@ -26,6 +26,7 @@ pub async fn install_package_from_bundle(
|
|||
cluster_id: i64,
|
||||
bundle_name: &str,
|
||||
skip_compatibility: bool,
|
||||
child: Option<&GroupedProgressChild>,
|
||||
services: &LauncherServices,
|
||||
) -> LauncherResult<String> {
|
||||
let cluster = PackageStore::get_cluster(cluster_id, services).await?;
|
||||
|
|
@ -63,13 +64,14 @@ pub async fn install_package_from_bundle(
|
|||
cluster_id,
|
||||
skip_compatibility,
|
||||
false,
|
||||
child,
|
||||
services,
|
||||
)
|
||||
.await?;
|
||||
artifact.hash
|
||||
}
|
||||
BundleFileKind::External(ext) => {
|
||||
install_external(ext, &cluster, skip_compatibility, services).await?
|
||||
install_external(ext, &cluster, skip_compatibility, child, services).await?
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -90,9 +92,10 @@ async fn install_external(
|
|||
ext: &ExternalFile,
|
||||
cluster: &ClusterRow,
|
||||
skip_compatibility: bool,
|
||||
child: Option<&GroupedProgressChild>,
|
||||
services: &LauncherServices,
|
||||
) -> LauncherResult<String> {
|
||||
let artifact = crate::packages::store::download_external(ext, false, services).await?;
|
||||
let artifact = crate::packages::store::download_external(ext, false, child, services).await?;
|
||||
PackageStore::link_artifact(&artifact, cluster, Some(&ext.name), services).await?;
|
||||
|
||||
let _ = skip_compatibility;
|
||||
|
|
@ -197,8 +200,8 @@ pub async fn install_enabled_bundle_files(
|
|||
let bundle_name = &bundle_name;
|
||||
let results = futures_util::stream::iter(to_install.into_iter().map(|file| async move {
|
||||
let child = progress.map(|p| {
|
||||
let c = p.child(format!("Mod {}", file.display_name()), 1);
|
||||
c.set_phase(TaskPhase::Installing);
|
||||
let c = p.child(format!("Mod {}", file.display_name()), file.size.max(1));
|
||||
c.set_phase(TaskPhase::Downloading);
|
||||
c
|
||||
});
|
||||
|
||||
|
|
@ -207,11 +210,13 @@ pub async fn install_enabled_bundle_files(
|
|||
cluster_id,
|
||||
bundle_name,
|
||||
skip_compatibility,
|
||||
child.as_ref(),
|
||||
services,
|
||||
)
|
||||
.await;
|
||||
|
||||
if let Some(child) = child {
|
||||
child.set_phase(TaskPhase::Installing);
|
||||
child.finish();
|
||||
}
|
||||
(file.display_name(), result)
|
||||
|
|
|
|||
|
|
@ -328,8 +328,21 @@ pub async fn apply_bundle_updates(
|
|||
}
|
||||
}
|
||||
|
||||
let session = (!check.updates_available.is_empty() || !check.additions_available.is_empty())
|
||||
.then(|| {
|
||||
crate::notification::GroupedProgressSession::start(
|
||||
&services.notifier,
|
||||
"Updating bundle content",
|
||||
)
|
||||
});
|
||||
|
||||
for update in check.updates_available {
|
||||
match apply_single_update(&update, &overrides, services).await {
|
||||
let child = session.as_ref().map(|s| {
|
||||
let c = s.child(update.new_file.display_name(), update.new_file.size.max(1));
|
||||
c.set_phase(crate::notification::TaskPhase::Downloading);
|
||||
c
|
||||
});
|
||||
match apply_single_update(&update, &overrides, child.as_ref(), services).await {
|
||||
Ok(_) => result.updates_applied.push(update),
|
||||
Err(err) => result.updates_failed.push(err.to_string()),
|
||||
}
|
||||
|
|
@ -337,12 +350,21 @@ pub async fn apply_bundle_updates(
|
|||
|
||||
for addition in check.additions_available {
|
||||
let file_id = addition.new_file.kind.package_id();
|
||||
match apply_single_addition(&addition, &overrides, services).await {
|
||||
let child = session.as_ref().map(|s| {
|
||||
let c = s.child(addition.new_file.display_name(), addition.new_file.size.max(1));
|
||||
c.set_phase(crate::notification::TaskPhase::Downloading);
|
||||
c
|
||||
});
|
||||
match apply_single_addition(&addition, &overrides, child.as_ref(), services).await {
|
||||
Ok(_) => result.additions_applied.push(addition),
|
||||
Err(err) => result.additions_failed.push(format!("{file_id}: {err:#}")),
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(session) = session {
|
||||
session.finish();
|
||||
}
|
||||
|
||||
let has_changes = !result.updates_applied.is_empty()
|
||||
|| !result.removals_applied.is_empty()
|
||||
|| !result.additions_applied.is_empty();
|
||||
|
|
@ -389,6 +411,7 @@ pub async fn apply_bundle_updates(
|
|||
async fn apply_single_update(
|
||||
update: &BundlePackageUpdate,
|
||||
overrides: &[ClusterBundleOverrideRow],
|
||||
child: Option<&crate::notification::GroupedProgressChild>,
|
||||
services: &LauncherServices,
|
||||
) -> LauncherResult<()> {
|
||||
let hash = install_package_from_bundle(
|
||||
|
|
@ -396,9 +419,14 @@ async fn apply_single_update(
|
|||
update.cluster_id,
|
||||
&update.bundle_name,
|
||||
true,
|
||||
child,
|
||||
services,
|
||||
)
|
||||
.await?;
|
||||
if let Some(child) = child {
|
||||
child.set_phase(crate::notification::TaskPhase::Installing);
|
||||
child.finish();
|
||||
}
|
||||
|
||||
let file_id = update.new_file.kind.package_id();
|
||||
if should_be_disabled(&update.bundle_name, &file_id, overrides) {
|
||||
|
|
@ -415,6 +443,7 @@ async fn apply_single_update(
|
|||
async fn apply_single_addition(
|
||||
addition: &BundlePackageAddition,
|
||||
overrides: &[ClusterBundleOverrideRow],
|
||||
child: Option<&crate::notification::GroupedProgressChild>,
|
||||
services: &LauncherServices,
|
||||
) -> LauncherResult<()> {
|
||||
let hash = install_package_from_bundle(
|
||||
|
|
@ -422,9 +451,14 @@ async fn apply_single_addition(
|
|||
addition.cluster_id,
|
||||
&addition.bundle_name,
|
||||
true,
|
||||
child,
|
||||
services,
|
||||
)
|
||||
.await?;
|
||||
if let Some(child) = child {
|
||||
child.set_phase(crate::notification::TaskPhase::Installing);
|
||||
child.finish();
|
||||
}
|
||||
|
||||
let file_id = addition.new_file.kind.package_id();
|
||||
if should_be_disabled(&addition.bundle_name, &file_id, overrides) {
|
||||
|
|
|
|||
|
|
@ -168,6 +168,7 @@ async fn install_mrpack_file(
|
|||
&project,
|
||||
&version,
|
||||
false,
|
||||
None,
|
||||
services,
|
||||
)
|
||||
.await?;
|
||||
|
|
@ -189,7 +190,7 @@ async fn install_mrpack_file(
|
|||
content_type,
|
||||
};
|
||||
|
||||
let artifact = store::download_external(&external, false, services).await?;
|
||||
let artifact = store::download_external(&external, false, None, services).await?;
|
||||
PackageStore::link_artifact(&artifact, cluster, Some(&file_name), services).await?;
|
||||
|
||||
Ok(())
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
use reqwest::Method;
|
||||
use serde::de::DeserializeOwned;
|
||||
|
||||
use crate::http::{RequestClient, RequestError, ResponseOptions};
|
||||
use crate::http::{RequestClient, RequestError, ResponseNotifyOptions, ResponseOptions};
|
||||
use crate::notification::GroupedProgressChild;
|
||||
use crate::state::LauncherServices;
|
||||
|
||||
pub async fn fetch_json<T: DeserializeOwned>(
|
||||
|
|
@ -33,10 +34,14 @@ pub async fn download_url(
|
|||
client: &RequestClient,
|
||||
url: &str,
|
||||
dest: impl AsRef<std::path::Path> + Send,
|
||||
child: Option<&GroupedProgressChild>,
|
||||
services: &LauncherServices,
|
||||
) -> Result<(), RequestError> {
|
||||
let request = reqwest::Request::new(Method::GET, url.parse()?);
|
||||
let options = ResponseOptions {
|
||||
notify: child.map(|c| ResponseNotifyOptions::grouped(c.clone())),
|
||||
};
|
||||
client
|
||||
.download_file(request, dest, ResponseOptions::default(), services)
|
||||
.download_file(request, dest, options, services)
|
||||
.await
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ use crate::LauncherResult;
|
|||
use crate::crypto::{normalize_hash, sha1_file};
|
||||
use crate::packages::domain::{ContentType, ProviderId};
|
||||
use crate::packages::error::PackageError;
|
||||
use crate::notification::{GroupedProgressChild, TaskPhase};
|
||||
use crate::packages::provider::http::download_url;
|
||||
use crate::packages::types::{ExternalFile, VersionDetail, VersionFile};
|
||||
use crate::state::LauncherServices;
|
||||
|
|
@ -14,6 +15,7 @@ pub async fn ensure_artifact_file(
|
|||
hash: &str,
|
||||
url: &str,
|
||||
dest: &std::path::Path,
|
||||
child: Option<&GroupedProgressChild>,
|
||||
services: &LauncherServices,
|
||||
) -> LauncherResult<u64> {
|
||||
polyio::create_dir_all(dest.parent().unwrap_or(dest)).await?;
|
||||
|
|
@ -26,8 +28,11 @@ pub async fn ensure_artifact_file(
|
|||
tokio::fs::remove_file(dest).await?;
|
||||
}
|
||||
|
||||
download_url(&services.requester, url, dest, services).await?;
|
||||
download_url(&services.requester, url, dest, child, services).await?;
|
||||
|
||||
if let Some(child) = child {
|
||||
child.set_phase(TaskPhase::Verifying);
|
||||
}
|
||||
let actual = sha1_file(dest).await?;
|
||||
if normalize_hash(&actual) != normalize_hash(hash) {
|
||||
return Err(PackageError::HashMismatch {
|
||||
|
|
@ -40,6 +45,7 @@ pub async fn ensure_artifact_file(
|
|||
Ok(tokio::fs::metadata(dest).await?.len())
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub async fn download_version_file(
|
||||
provider: ProviderId,
|
||||
project_id: &str,
|
||||
|
|
@ -47,6 +53,7 @@ pub async fn download_version_file(
|
|||
content_type: ContentType,
|
||||
file: &VersionFile,
|
||||
force: bool,
|
||||
child: Option<&GroupedProgressChild>,
|
||||
services: &LauncherServices,
|
||||
) -> LauncherResult<ArtifactRow> {
|
||||
let hash = normalize_hash(&file.sha1);
|
||||
|
|
@ -68,7 +75,7 @@ pub async fn download_version_file(
|
|||
&file.file_name,
|
||||
)?;
|
||||
|
||||
let size = ensure_artifact_file(&hash, &file.url, &dest, services).await?;
|
||||
let size = ensure_artifact_file(&hash, &file.url, &dest, child, services).await?;
|
||||
let stored_path = relative_cache_path(&dest)?;
|
||||
|
||||
let published_at = version.published.to_rfc3339();
|
||||
|
|
@ -103,6 +110,7 @@ pub async fn download_version_file(
|
|||
pub async fn download_external(
|
||||
file: &ExternalFile,
|
||||
force: bool,
|
||||
child: Option<&GroupedProgressChild>,
|
||||
services: &LauncherServices,
|
||||
) -> LauncherResult<ArtifactRow> {
|
||||
let hash = normalize_hash(&file.sha1);
|
||||
|
|
@ -120,7 +128,7 @@ pub async fn download_external(
|
|||
&hash[..hash.len().min(16)],
|
||||
&file.name,
|
||||
)?;
|
||||
let size = ensure_artifact_file(&hash, &file.url, &dest, services).await?;
|
||||
let size = ensure_artifact_file(&hash, &file.url, &dest, child, services).await?;
|
||||
let stored_path = relative_cache_path(&dest)?;
|
||||
|
||||
artifact_dao::insert_artifact(
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ use super::domain::{ContentType, GameLoader, ProviderId};
|
|||
use super::error::PackageError;
|
||||
use super::types::{CachedArtifact, ProjectDetail, ProviderReleaseInfo, VersionDetail, LinkedArtifactInfo};
|
||||
use crate::crypto::{normalize_hash, sha1_file};
|
||||
use crate::notification::GroupedProgressChild;
|
||||
use crate::state::LauncherServices;
|
||||
use crate::{LauncherError, LauncherResult};
|
||||
use std::path::Path;
|
||||
|
|
@ -45,6 +46,7 @@ impl PackageStore {
|
|||
project: &ProjectDetail,
|
||||
version: &VersionDetail,
|
||||
force: bool,
|
||||
child: Option<&GroupedProgressChild>,
|
||||
services: &LauncherServices,
|
||||
) -> LauncherResult<ArtifactRow> {
|
||||
let file = version.primary_file().ok_or(PackageError::NoPrimaryFile)?;
|
||||
|
|
@ -56,11 +58,13 @@ impl PackageStore {
|
|||
project.content_type,
|
||||
file,
|
||||
force,
|
||||
child,
|
||||
services,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub async fn install_to_cluster(
|
||||
provider_id: ProviderId,
|
||||
project: &ProjectDetail,
|
||||
|
|
@ -68,6 +72,7 @@ impl PackageStore {
|
|||
cluster_id: i64,
|
||||
skip_compatibility: bool,
|
||||
force_download: bool,
|
||||
child: Option<&GroupedProgressChild>,
|
||||
services: &LauncherServices,
|
||||
) -> LauncherResult<ArtifactRow> {
|
||||
let cluster = Self::get_cluster(cluster_id, services).await?;
|
||||
|
|
@ -81,6 +86,7 @@ impl PackageStore {
|
|||
project,
|
||||
version,
|
||||
force_download,
|
||||
child,
|
||||
services,
|
||||
)
|
||||
.await?;
|
||||
|
|
@ -286,7 +292,7 @@ impl PackageStore {
|
|||
|
||||
let project = provider.get_project(project_id, services).await?;
|
||||
|
||||
Self::download_and_cache(provider_id, &project, &version, false, services).await
|
||||
Self::download_and_cache(provider_id, &project, &version, false, None, services).await
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue