Compare commits

..

4 Commits

Author SHA1 Message Date
f560a62c8f Download fixes (#63)
* refactor: Rename StoredManifest to DropData

Signed-off-by: quexeky <git@quexeky.dev>

* fix: Downloads when resuming would truncate files which had not been finished

Signed-off-by: quexeky <git@quexeky.dev>

* chore: Didn't import debug macro

Signed-off-by: quexeky <git@quexeky.dev>

* fix: Download chunks with wrong indexes

Migrated to using checksums as indexes instead

Signed-off-by: quexeky <git@quexeky.dev>

* feat: Resume download button

Also added DBWrite and DBRead structs to make database management easier

Signed-off-by: quexeky <git@quexeky.dev>

* feat: Download resuming

Signed-off-by: quexeky <git@quexeky.dev>

* feat: Resume button and PartiallyInstalled status

Signed-off-by: quexeky <git@quexeky.dev>

* feat: Download validation

Signed-off-by: quexeky <git@quexeky.dev>

* chore: Ran cargo fix & cargo fmt

Signed-off-by: quexeky <git@quexeky.dev>

* fix: download validation, installs, etc

* chore: version bump

---------

Signed-off-by: quexeky <git@quexeky.dev>
Co-authored-by: quexeky <git@quexeky.dev>
2025-07-14 16:31:06 +10:00
2874b9776b fix: Accidentally moved request when setting the header
Signed-off-by: quexeky <git@quexeky.dev>
2025-06-25 09:17:06 +10:00
afcd4e916f chore: bump version to 0.3.0-rc-4 2025-06-25 09:05:08 +10:00
885fa42ecc fix: Move Authorization header generation to download_game_chunk()
Signed-off-by: quexeky <git@quexeky.dev>
2025-06-25 06:53:42 +10:00
12 changed files with 52 additions and 16 deletions

View File

@ -76,6 +76,7 @@ const gameStatusTextStyle: { [key in GameStatusEnum]: string } = {
[GameStatusEnum.Updating]: "text-blue-500",
[GameStatusEnum.Uninstalling]: "text-zinc-100",
[GameStatusEnum.SetupRequired]: "text-yellow-500",
[GameStatusEnum.PartiallyInstalled]: "text-gray-600"
};
const gameStatusText: { [key in GameStatusEnum]: string } = {
[GameStatusEnum.Remote]: "Not installed",
@ -86,6 +87,7 @@ const gameStatusText: { [key in GameStatusEnum]: string } = {
[GameStatusEnum.Uninstalling]: "Uninstalling...",
[GameStatusEnum.SetupRequired]: "Setup required",
[GameStatusEnum.Running]: "Running",
[GameStatusEnum.PartiallyInstalled]: "Partially installed"
};
const router = useRouter();

View File

@ -1,7 +1,7 @@
{
"name": "drop-app",
"private": true,
"version": "0.3.0-rc-3",
"version": "0.3.0-rc-5",
"type": "module",
"scripts": {
"build": "nuxt build",

2
src-tauri/Cargo.lock generated
View File

@ -1247,7 +1247,7 @@ dependencies = [
[[package]]
name = "drop-app"
version = "0.3.0-rc-3"
version = "0.3.0-rc-5"
dependencies = [
"atomic-instant-full",
"boxcar",

View File

@ -1,6 +1,6 @@
[package]
name = "drop-app"
version = "0.3.0-rc-3"
version = "0.3.0-rc-5"
description = "The client application for the open-source, self-hosted game distribution platform Drop"
authors = ["Drop OSS"]
edition = "2021"

View File

@ -72,6 +72,7 @@ impl Serialize for DownloadManagerStatus {
pub enum DownloadStatus {
Queued,
Downloading,
Validating,
Error,
}

View File

@ -264,6 +264,8 @@ impl DownloadManagerBuilder {
download_agent.metadata(),
&e
);
download_agent.on_error(&app_handle, &e);
sender.send(DownloadManagerSignal::Error(e)).unwrap();
}
}
}

View File

@ -133,7 +133,11 @@ pub fn calculate_update(progress: &ProgressObject) {
let kilobytes_per_second = bytes_since_last_update / (time_since_last_update as usize).max(1);
let bytes_remaining = max - current_bytes_downloaded; // bytes
let bytes_remaining = if (max < current_bytes_downloaded) {
0
} else {
max - current_bytes_downloaded
}; // bytes
progress.update_window(kilobytes_per_second);
push_update(progress, bytes_remaining);

View File

@ -25,7 +25,7 @@ impl Display for ApplicationDownloadError {
ApplicationDownloadError::Setup(error) => write!(f, "an error occurred while setting up the download: {}", error),
ApplicationDownloadError::Lock => write!(f, "failed to acquire lock. Something has gone very wrong internally. Please restart the application"),
ApplicationDownloadError::Checksum => write!(f, "checksum failed to validate for download"),
ApplicationDownloadError::IoError(error) => write!(f, "{}", error),
ApplicationDownloadError::IoError(error) => write!(f, "io error: {}", error),
ApplicationDownloadError::DownloadError => write!(f, "download failed. See Download Manager status for specific error"),
}
}

View File

@ -284,8 +284,6 @@ impl GameDownloadAgent {
continue;
}
debug!("Continuing download chunk {}", index);
let sender = self.sender.clone();
let request = match make_request(
@ -297,7 +295,7 @@ impl GameDownloadAgent {
("name", &context.file_name),
("chunk", &context.index.to_string()),
],
|r| r.header("Authorization", generate_authorization_header()),
|r| r,
) {
Ok(request) => request,
Err(e) => {
@ -435,7 +433,8 @@ impl Downloadable for GameDownloadAgent {
&self.metadata(),
self.stored_manifest.base_path.to_string_lossy().to_string(),
app_handle,
);
)
.unwrap();
}
fn on_cancelled(&self, _app_handle: &tauri::AppHandle) {}
@ -445,6 +444,7 @@ impl Downloadable for GameDownloadAgent {
}
fn validate(&self) -> Result<bool, ApplicationDownloadError> {
*self.status.lock().unwrap() = DownloadStatus::Validating;
game_validate_logic(
&self.stored_manifest,
self.contexts.lock().unwrap().clone(),

View File

@ -5,7 +5,8 @@ use crate::download_manager::util::progress_object::ProgressHandle;
use crate::error::application_download_error::ApplicationDownloadError;
use crate::error::remote_access_error::RemoteAccessError;
use crate::games::downloads::manifest::DropDownloadContext;
use log::{debug, warn};
use crate::remote::auth::generate_authorization_header;
use log::{debug, info, warn};
use md5::{Context, Digest};
use reqwest::blocking::{RequestBuilder, Response};
@ -97,13 +98,24 @@ impl<'a> DropDownloadPipeline<'a, Response, File> {
return Ok(false);
}
let bytes_read = self.source.read(&mut copy_buf)?;
let mut bytes_read = self.source.read(&mut copy_buf)?;
current_size += bytes_read;
if current_size > self.size {
let over = current_size - self.size;
warn!("server sent too many bytes... {} over", over);
bytes_read -= over;
current_size = self.size;
}
buf_writer.write_all(&copy_buf[0..bytes_read])?;
self.progress.add(bytes_read);
if current_size == self.size {
if current_size >= self.size {
debug!(
"finished with final size of {} vs {}",
current_size, self.size
);
break;
}
}
@ -133,12 +145,15 @@ pub fn download_game_chunk(
progress.set(0);
return Ok(false);
}
let request = request.header("Authorization", generate_authorization_header());
let response = request
.header("Authorization", generate_authorization_header())
.send()
.map_err(|e| ApplicationDownloadError::Communication(e.into()))?;
if response.status() != 200 {
debug!("chunk request got status code: {}", response.status());
let err = response.json().unwrap();
return Err(ApplicationDownloadError::Communication(
RemoteAccessError::InvalidResponse(err),

View File

@ -137,7 +137,8 @@ pub fn validate_game_chunk(
let mut hasher = md5::Context::new();
let completed = validate_copy(&mut source, &mut hasher, control_flag, progress).unwrap();
let completed =
validate_copy(&mut source, &mut hasher, ctx.length, control_flag, progress).unwrap();
if !completed {
return Ok(false);
};
@ -162,12 +163,14 @@ pub fn validate_game_chunk(
fn validate_copy(
source: &mut File,
dest: &mut Context,
size: usize,
control_flag: &DownloadThreadControl,
progress: ProgressHandle,
) -> Result<bool, io::Error> {
let copy_buf_size = 512;
let mut copy_buf = vec![0; copy_buf_size];
let mut buf_writer = BufWriter::with_capacity(1024 * 1024, dest);
let mut total_bytes = 0;
loop {
if control_flag.get() == DownloadThreadControlFlag::Stop {
@ -175,12 +178,21 @@ fn validate_copy(
return Ok(false);
}
let bytes_read = source.read(&mut copy_buf)?;
let mut bytes_read = source.read(&mut copy_buf)?;
total_bytes += bytes_read;
// If we read over (likely), truncate our read to
// the right size
if total_bytes > size {
let over = total_bytes - size;
bytes_read -= over;
total_bytes = size;
}
buf_writer.write_all(&copy_buf[0..bytes_read])?;
progress.add(bytes_read);
if bytes_read == 0 {
if total_bytes >= size {
break;
}
}

View File

@ -1,7 +1,7 @@
{
"$schema": "https://schema.tauri.app/config/2.0.0",
"productName": "Drop Desktop Client",
"version": "0.3.0-rc-3",
"version": "0.3.0-rc-5",
"identifier": "dev.drop.app",
"build": {
"beforeDevCommand": "yarn dev --port 1432",