C0nt;GswYSmm)!iPj7UxB8XR$U`AXDepM}k8KD^9G4L?Mv}
zj)|i_gAw7NE}CXq1t=eMe1Br?--pKE9TgVWq1^cN3re<|p|~L|1XmBfaWEzH?Qlc&
zh+g?;m5^1Hk%sHDOafjVp$T4#1OY2mG7Bm9LytuQx(jkPWBYO0*PDl$7%z1xjy
z$lg3OQDJ04UpIj!>FrQXS<9Th>f{}*!-8gFxUKx`!14qO!?)D1
z^?3-QHh@lOVz;V~=gF!LkgM6&Mlz;iuRBe)IOgOai57swk7lYQdFz^0_qO%C0CrnZ
zgmF(>lv%)GYXgC7bVHc_#?t>`M-xn4IpjbUnP>D)kJ})QzY+ov;(%FMfZrA41jGs!
z(C{(PCL=yN=E>JGhMY|Tz@z2m@*DLLE%WJ>kt`%2o{w`nKqe?nI=T57O@3R4b%987
z)CUb?j>;R3tARAG0agd$i4s1CwL9^$WFGg<=6j2(nvPZ4h((?Qe*E08)<
z%U|i#8|BxGwf~y6k^HMBu7rsTbMIQYjy;gIe6f>=Wkx9$e_6?9Fy@S~0G!WSC<69E
zkX3$*E?~G5^P$Z00S?r~x%ze2tbP>`Zf(fM`yfgV4kz^OXKB|77nNBYYo3zLhiP&O
zWa+fNn}ix-?+SpNg1`HWe(Tn&Otq77#M+8ZKWV5<#_fBBmCE%-MGk#IC8LNQhFbSK{$bn-DVH+vBPAdw@g<
z08k9yVO2-C!{bjL=`VQtTdJJj?pI}+vHWIF1uN`UNQCH8tCIqb@%CN`*Ow@#WoSnR
zt{QlZo=0s>u_^Hk!sfi{#oBfx4I7Bl@k8(Y+~iR&^&+d$j}opIluFo(aM59n9hPNt
zZ)XLb{-{`ji`m%wzKD5x2-m1haDlrZYWjR=93D;n^+)bX12K$kft2snS&a!
zCx`nCl+2;AwM>y>g>+YjfQsV+#rDusyvUZfRcY=MvT-LWL}tYpxe69v8~_vwZv=tr
z@D8UH7qj}r0C0gsP@AEEwV5zIxB~GY46L3GIbmcj*v%tGVkLrRCHXgd|4P>(=>kOs
zzf7^L@;I@x#q$$Pgk?Ay#CS+TALD|2hiv7_$Cgo2_!|pd-UXMW17vzOE4t8^yy2}N
zFF}OBB$%6J45#u#tl(a-SHs5f`sT%OKvjkUk9^KmRPm$+7U03daV0;(^a8=q>Vk!v
zjYOES1g}*niunwbO+zloP@YN>MpOsM@LKmtZG_iJOb>YNz*ry*?^dha4YWoz&5XT?
zQ0vku{d0bN&~8+5hE1*MBZ*_cr!ip+V!KBVOiChS#KM@hNd*h`OuVU;{xxcy@Wue-
znAAC0Lo~)hNSE9IsG*fzc_mdp6J!BQ+AFoLCNg`fS;b%5*B^z88CPtAO^dPd#ul?c
zG~}pv^W?6P<5}dvFJSl$5he-poPFH(My_QA;m|y2xJc)iu_qNEWWX2H$ReyZAzyQW
zo@1GN)Xah~cZq+SVhO7N+NdjYbbe~;nuchDja2-_A?55~V;w6J7P6gEclRj#3o3jx
z5cypbHfrOMI*wen#Mrb2|4via|FGY}<)^F^U^RwgO;NBV_JfbARwwhq5tNDq3CiGT
zPIUT0wlKhT;yp%2Dn+(+vi1=WdjDh|JXex|c$mv`&RdH6QJZBV2`$JbKb-FUqMuM}
z2>S#XQN&565z-Y3lfrth{~Kf8ayi6R;MS7c7ji1Cw!(70HQQHiH>)5JAfX0Y
zG6eX$!`KB=G%1)q1*ck27fy*w`kU~HBbb>aNgxQFnbHVL;TWTk@&e=Hj%Br(YLKHM&M
zpUBB*AHtrh4fZUTOCi6?{$%E4k~ZO2Clj=CIguHeswL(Og~3onodGD8tNIs{J~H%8
z7?VeN5~(ldf>@tt*Wlhg%<8D{7b5`uqf$ju$#R)?n1TD`|L(1^6M>Y7qdc~fM%vQw
zdWH#pbPl
t!p(|%n`O0|Askys()p#K`p03Ab5|fNBk_P*|5}g_%dpjg2Tw2|zyMtP#RdQX
literal 0
HcmV?d00001
From a7f6b62cbe7cac89055da5766bff1d8416784ada Mon Sep 17 00:00:00 2001
From: VilppeRiskidev
Date: Mon, 4 Aug 2025 17:04:10 +0300
Subject: [PATCH 009/181] Calculate and display the download progress based on
the file size
* Calculate the total file size when initializing the download
* Calculate amount of downloaded bytes
* Calculate the progress percentage based on the aforementioned values
* Save the values to the local store in the callback
* (Downloading via importing a profile uses the callback values
without saving the progress to the store)
* Display the progress (percentage), current progress (file size)
and total file size to be downloaded in DownloadMonitor,
DownloadProgressModal and ImportProfileModal
---
.../profiles-modals/ImportProfileModal.vue | 16 +++--
.../views/DownloadProgressModal.vue | 6 +-
src/model/ThunderstoreVersion.ts | 10 +++
src/pages/DownloadMonitor.vue | 5 +-
.../ThunderstoreDownloaderProvider.ts | 11 +--
.../BetterThunderstoreDownloader.ts | 62 +++++++++--------
src/store/modules/DownloadModule.ts | 37 +++++++---
src/utils/DownloadUtils.ts | 67 +++++++++++++++++--
8 files changed, 152 insertions(+), 62 deletions(-)
diff --git a/src/components/profiles-modals/ImportProfileModal.vue b/src/components/profiles-modals/ImportProfileModal.vue
index 61ce4d33a..c2da3edc6 100644
--- a/src/components/profiles-modals/ImportProfileModal.vue
+++ b/src/components/profiles-modals/ImportProfileModal.vue
@@ -15,6 +15,8 @@ import { useProfilesComposable } from '../composables/ProfilesComposable';
import { computed, nextTick, ref, watch, watchEffect } from 'vue';
import { getStore } from '../../providers/generic/store/StoreProvider';
import { State } from '../../store';
+import FileUtils from "../../utils/FileUtils";
+import * as DownloadUtils from "../../utils/DownloadUtils";
const VALID_PROFILE_CODE_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
@@ -52,6 +54,7 @@ const profileList = computed(() => store.state.profiles.profileList);
const knownProfileMods = computed(() => profileMods.value.known.map((combo) => combo.getMod()));
const unknownProfileModNames = computed(() => profileMods.value.unknown.join(', '));
const isProfileCodeValid = computed(() => VALID_PROFILE_CODE_REGEX.test(profileImportCode.value));
+const profileTotalDownloadSize = ref(0);
const profileCodeInput = ref();
const profileNameInput = ref();
@@ -250,15 +253,20 @@ async function onImportTargetSelected() {
async function importProfile(targetProfileName: string, mods: ExportMod[], zipPath: string) {
activeStep.value = 'PROFILE_IS_BEING_IMPORTED';
importPhaseDescription.value = 'Downloading mods: 0%';
- const progressCallback = (progress: number|string) => typeof progress === "number"
- ? importPhaseDescription.value = `Downloading mods: ${Math.floor(progress)}%`
- : importPhaseDescription.value = progress;
+ const progressCallback = (downloadedSize: number) => {
+ importPhaseDescription.value = `Downloading mods: ${DownloadUtils.generateProgressPercentage(downloadedSize, profileTotalDownloadSize.value)}%` +
+ ` (${FileUtils.humanReadableSize(downloadedSize)} / ${FileUtils.humanReadableSize(profileTotalDownloadSize.value)})`;
+ };
const isUpdate = importUpdateSelection.value === 'UPDATE';
try {
const combos = profileMods.value.known as ThunderstoreCombo[];
+ profileTotalDownloadSize.value = await DownloadUtils.getTotalDownloadSizeInBytes(combos, store.state.download.ignoreCache);
+
await store.dispatch('download/downloadToCache', {combos, progressCallback});
- await ProfileUtils.populateImportedProfile(combos, mods, targetProfileName, isUpdate, zipPath, progressCallback);
+ await ProfileUtils.populateImportedProfile(combos, mods, targetProfileName, isUpdate, zipPath, (progress) => {
+ importPhaseDescription.value = progress;
+ });
} catch (e) {
await store.dispatch('profiles/ensureProfileExists');
closeModal();
diff --git a/src/components/views/DownloadProgressModal.vue b/src/components/views/DownloadProgressModal.vue
index 8d1a26e2c..36efbf38e 100644
--- a/src/components/views/DownloadProgressModal.vue
+++ b/src/components/views/DownloadProgressModal.vue
@@ -2,6 +2,7 @@
import { Progress } from '../all';
import { getStore } from '../../providers/generic/store/StoreProvider';
import { State } from '../../store';
+import FileUtils from '../../utils/FileUtils';
const store = getStore();
@@ -29,7 +30,10 @@ function closeModal() {
Installing {{$store.getters['download/currentDownload'].modName}}
- Downloading: {{$store.getters['download/currentDownload'].downloadProgress}}% complete
+
+ Downloading: {{$store.getters['download/currentDownload'].downloadProgress}}% complete
+ ({{FileUtils.humanReadableSize($store.getters['download/currentDownload'].downloadedSize)}} / {{FileUtils.humanReadableSize($store.getters['download/currentDownload'].totalDownloadSize)}})
+