web-install: Show download progress
Getting progress with the fetch() API is complicated and requires us to stream the data and create the blob ourselves, so use XMLHttpRequest instead to get live progress updates.
This commit is contained in:
parent
cdafb40dd6
commit
d44bafb8cf
@ -163,7 +163,11 @@
|
|||||||
|
|
||||||
<button id="download-release-button" disabled="disabled">Download release</button>
|
<button id="download-release-button" disabled="disabled">Download release</button>
|
||||||
|
|
||||||
<p><strong id="download-release-status"></strong></p>
|
<p id="download-release-status-container" hidden="hidden">
|
||||||
|
<strong id="download-release-status"></strong>
|
||||||
|
<br/>
|
||||||
|
<progress id="download-release-progress" hidden="hidden" max="1" value="0"></progress>
|
||||||
|
</p>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section id="flashing-factory-images">
|
<section id="flashing-factory-images">
|
||||||
|
@ -7,6 +7,26 @@ const RELEASES_URL = "https://releases.grapheneos.org";
|
|||||||
const CACHE_DB_NAME = "BlobStore";
|
const CACHE_DB_NAME = "BlobStore";
|
||||||
const CACHE_DB_VERSION = 1;
|
const CACHE_DB_VERSION = 1;
|
||||||
|
|
||||||
|
// This wraps XHR because getting progress updates with fetch() is overly complicated.
|
||||||
|
function fetchBlobWithProgress(url, onProgress) {
|
||||||
|
let xhr = new XMLHttpRequest();
|
||||||
|
xhr.open("GET", url);
|
||||||
|
xhr.responseType = "blob";
|
||||||
|
xhr.send();
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
xhr.onload = () => {
|
||||||
|
resolve(xhr.response);
|
||||||
|
};
|
||||||
|
xhr.onprogress = (event) => {
|
||||||
|
onProgress(event.loaded / event.total);
|
||||||
|
};
|
||||||
|
xhr.onerror = () => {
|
||||||
|
reject(`${xhr.status} ${xhr.statusText}`);
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
class BlobStore {
|
class BlobStore {
|
||||||
constructor() {
|
constructor() {
|
||||||
this.db = null;
|
this.db = null;
|
||||||
@ -65,19 +85,12 @@ class BlobStore {
|
|||||||
this.db.close();
|
this.db.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
async download(url, onProgress = () => {}) {
|
||||||
* Downloads the file from the given URL and saves it to this BlobStore.
|
|
||||||
*
|
|
||||||
* @param {string} url - URL of the file to download.
|
|
||||||
* @returns {blob} Blob containing the downloaded data.
|
|
||||||
*/
|
|
||||||
async download(url) {
|
|
||||||
let filename = url.split("/").pop();
|
let filename = url.split("/").pop();
|
||||||
let blob = await this.loadFile(filename);
|
let blob = await this.loadFile(filename);
|
||||||
if (blob === null) {
|
if (blob === null) {
|
||||||
console.log(`Downloading ${url}`);
|
console.log(`Downloading ${url}`);
|
||||||
let resp = await fetch(new Request(url));
|
let blob = await fetchBlobWithProgress(url, onProgress);
|
||||||
blob = await resp.blob();
|
|
||||||
console.log("File downloaded, saving...");
|
console.log("File downloaded, saving...");
|
||||||
await this.saveFile(filename, blob);
|
await this.saveFile(filename, blob);
|
||||||
console.log("File saved");
|
console.log("File saved");
|
||||||
@ -144,8 +157,10 @@ async function downloadRelease(setProgress) {
|
|||||||
// Download and cache the zip as a blob
|
// Download and cache the zip as a blob
|
||||||
setProgress(`Downloading ${latestZip}...`);
|
setProgress(`Downloading ${latestZip}...`);
|
||||||
await blobStore.init();
|
await blobStore.init();
|
||||||
await blobStore.download(`${RELEASES_URL}/${latestZip}`);
|
await blobStore.download(`${RELEASES_URL}/${latestZip}`, (progress) => {
|
||||||
return `Downloaded ${latestZip} release.`;
|
setProgress(`Downloading ${latestZip}...`, progress);
|
||||||
|
});
|
||||||
|
setProgress(`Downloaded ${latestZip} release.`, 1.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function reconnectCallback() {
|
async function reconnectCallback() {
|
||||||
@ -238,7 +253,9 @@ function addButtonHook(id, callback) {
|
|||||||
button.onclick = async () => {
|
button.onclick = async () => {
|
||||||
try {
|
try {
|
||||||
let finalStatus = await callback(statusCallback);
|
let finalStatus = await callback(statusCallback);
|
||||||
statusCallback(finalStatus);
|
if (finalStatus !== undefined) {
|
||||||
|
statusCallback(finalStatus);
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
statusCallback(`Error: ${error.message}`);
|
statusCallback(`Error: ${error.message}`);
|
||||||
statusField.className = "error-text";
|
statusField.className = "error-text";
|
||||||
|
Loading…
x
Reference in New Issue
Block a user