From ea518ec4cf825fad264c2eb86e9a5d025400d94c Mon Sep 17 00:00:00 2001 From: rajk Date: Tue, 3 Feb 2026 00:32:09 +0530 Subject: [PATCH] initial support for local zip files --- app/index.html | 19 +++++++ app/src/controller.manager.js | 10 +++- app/src/controller/device.manager.js | 36 +++++++++--- app/src/controller/downloader.manager.js | 63 +++++++++++++++++++++ app/src/viewManager.js | 72 +++++++++++++++++++++--- 5 files changed, 184 insertions(+), 16 deletions(-) diff --git a/app/index.html b/app/index.html index 2036ac6..dccdbb2 100644 --- a/app/index.html +++ b/app/index.html @@ -380,6 +380,25 @@

+
+
+ + +
+ + +

diff --git a/app/src/controller.manager.js b/app/src/controller.manager.js index bfe32a7..337ad12 100644 --- a/app/src/controller.manager.js +++ b/app/src/controller.manager.js @@ -413,7 +413,7 @@ export class Controller { setResources(resources) { this.resources = resources; if (this.resources.steps) { - this.steps.push(new Step("downloading", "download", false)); + this.steps.push(new Step("downloading", "download", true)); this.steps.push( ...this.resources.steps.map((step) => { return new Step( @@ -428,4 +428,12 @@ export class Controller { } this.deviceManager.setResources(this.resources.folder, this.steps); } + + setLocalZip(file) { + this.deviceManager.setLocalZipFile(file); + } + + clearLocalZip() { + this.deviceManager.clearLocalZipFile(); + } } diff --git a/app/src/controller/device.manager.js b/app/src/controller/device.manager.js index 74aff73..f86ace7 100644 --- a/app/src/controller/device.manager.js +++ b/app/src/controller/device.manager.js @@ -27,6 +27,18 @@ export class DeviceManager { this.wasConnected = false; } + setLocalZipFile(file) { + this.downloader.setLocalZip(file); + } + + clearLocalZipFile() { + this.downloader.clearLocalZip(); + } + + hasLocalZipFile() { + return this.downloader.hasLocalZip(); + } + async init() { await this.bootloader.init(); await this.adb.init(); @@ -187,13 +199,23 @@ export class DeviceManager { async downloadAll(onProgress, onUnzip, onVerify) { try { - await this.downloader.downloadAndUnzipFolder( - this.files, - this.folder, - onProgress, - onUnzip, - onVerify, - ); + if (this.downloader.hasLocalZip()) { + await this.downloader.ingestLocalZip( + this.files, + this.folder, + onProgress, + onUnzip, + onVerify, + ); + } else { + await this.downloader.downloadAndUnzipFolder( + this.files, + this.folder, + onProgress, + onUnzip, + onVerify, + ); + } } catch (e) { throw new Error(`downloadAll error ${e.message || e}`); } diff --git a/app/src/controller/downloader.manager.js b/app/src/controller/downloader.manager.js index 4a2937c..ee732eb 100644 --- a/app/src/controller/downloader.manager.js +++ b/app/src/controller/downloader.manager.js @@ -15,6 +15,7 @@ export class Downloader { constructor() { this.db = null; this.stored = {}; + this.localZipFile = null; } async init() { @@ -25,6 +26,18 @@ export class Downloader { this.quota = await navigator.storage.estimate(); } + setLocalZip(file) { + this.localZipFile = file; + } + + clearLocalZip() { + this.localZipFile = null; + } + + hasLocalZip() { + return !!this.localZipFile; + } + /* * */ async downloadAndUnzipFolder( @@ -34,6 +47,8 @@ export class Downloader { onUnzipProgress, onVerifyProgress, ) { + await this.clearDBStore(); + this.stored = {}; let current_file; try { for (let i = 0; i < folder.length; i++) { @@ -117,6 +132,54 @@ export class Downloader { } } + async ingestLocalZip( + filesRequired, + folder, + onDownloadProgress, + onUnzipProgress, + onVerifyProgress, + ) { + await this.clearDBStore(); + this.stored = {}; + if (!this.localZipFile) { + throw new Error("No local zip file selected"); + } + const zipDescriptor = folder.find((f) => f.unzip) || folder[0]; + if (!zipDescriptor) { + throw new Error("No zip resource found to map local file"); + } + + onDownloadProgress( + this.localZipFile.size, + this.localZipFile.size, + this.localZipFile.name || zipDescriptor.name || "local.zip", + ); + + const zipReader = new ZipReader(new BlobReader(this.localZipFile)); + const filesEntries = await zipReader.getEntries(); + for (let i = 0; i < filesEntries.length; i++) { + const unzippedEntry = await this.getFileFromZip(filesEntries[i], (value, total) => { + onUnzipProgress(value, total, filesEntries[i].filename); + }); + let filename = this.getMappedName( + filesEntries[i].filename, + zipDescriptor.mapping, + ); + if (filesRequired.includes(filename)) { + await this.setInDBStore(unzippedEntry.blob, filename); + this.stored[filename] = true; + const fileSHA = await this.computeSha256( + unzippedEntry.blob, + (loaded, total) => { + onVerifyProgress(loaded, total, filename); + }, + ); + console.log(`File: ${unzippedEntry.name} SHA256: ${fileSHA}`); + } + } + await zipReader.close(); + } + async getFileFromZip(file, onProgress) { const name = file.filename; const blob = await file.getData(new BlobWriter(), { diff --git a/app/src/viewManager.js b/app/src/viewManager.js index d122881..3133a25 100644 --- a/app/src/viewManager.js +++ b/app/src/viewManager.js @@ -32,12 +32,16 @@ export default class ViewManager { $copyStep.id = step.id; $copyStep.classList.add("active"); $copyStep.classList.remove("inactive"); - const $button = $copyStep.querySelector("button"); - if ($button) { - $button.addEventListener("click", async (event) => { - event.stopPropagation(); - await this.executeStep($button, step.name); - }); + if (step.name === "downloading") { + this.bindDownloadChoice($copyStep, step); + } else { + const $button = $copyStep.querySelector("button"); + if ($button) { + $button.addEventListener("click", async (event) => { + event.stopPropagation(); + await this.executeStep($button, step.name); + }); + } } let $processCtn = document.getElementById("process-ctn"); if ($processCtn) { @@ -222,6 +226,57 @@ export default class ViewManager { } // /CONTROLLER EVENTS + bindDownloadChoice($copyStep, step) { + const downloadBtn = $copyStep.querySelector(".download-build-button"); + const localBtn = $copyStep.querySelector(".use-local-zip-button"); + const fileInput = $copyStep.querySelector(".local-zip-input"); + const errorEl = $copyStep.querySelector(".local-zip-error"); + + if (downloadBtn) { + downloadBtn.addEventListener("click", async (event) => { + event.stopPropagation(); + if (errorEl) { + errorEl.style.display = "none"; + errorEl.innerText = ""; + } + this.controller.clearLocalZip(); + await this.executeStep(downloadBtn, step.name); + }); + } + + if (localBtn && fileInput) { + localBtn.addEventListener("click", (event) => { + event.stopPropagation(); + if (errorEl) { + errorEl.style.display = "none"; + errorEl.innerText = ""; + } + fileInput.value = ""; + fileInput.click(); + }); + + fileInput.addEventListener("change", async (evt) => { + const file = evt.target.files[0]; + if (!file) { + return; + } + if (!file.name.toLowerCase().endsWith(".zip")) { + if (errorEl) { + errorEl.innerText = "Please select a .zip file."; + errorEl.style.display = "block"; + } + fileInput.value = ""; + return; + } + if (errorEl) { + errorEl.style.display = "none"; + errorEl.innerText = ""; + } + this.controller.setLocalZip(file); + await this.executeStep(localBtn, step.name); + }); + } + } } document.addEventListener("DOMContentLoaded", async () => { @@ -230,11 +285,12 @@ document.addEventListener("DOMContentLoaded", async () => { let elts = document.querySelectorAll(".card button"); for (let elt of elts) { - if (elt.parentElement.parentElement.className.includes("inactive")) { + const card = elt.closest(".card"); + if (!card || card.className.includes("inactive")) { continue; } elt.addEventListener("click", async () => { - VIEW.executeStep(elt, elt.parentElement.parentElement.id); + VIEW.executeStep(elt, card.id); }); } }); -- GitLab