From e87f8035be5b5c0f9f98cb17fe80fd4aef8cf9ad Mon Sep 17 00:00:00 2001 From: jpirnay Date: Wed, 25 Mar 2026 12:10:00 +0100 Subject: [PATCH 1/2] Increase upload speed --- scripts/build_html.py | 153 +- src/network/html/FilesPage.html | 7157 ++++++++++++++++--------------- 2 files changed, 3870 insertions(+), 3440 deletions(-) diff --git a/scripts/build_html.py b/scripts/build_html.py index f2cfb7e9..32847760 100644 --- a/scripts/build_html.py +++ b/scripts/build_html.py @@ -4,27 +4,138 @@ import gzip SRC_DIR = "src" + +def strip_js_comments(js: str) -> str: + """Remove JS comments while preserving string literals and URLs.""" + result = [] + i = 0 + length = len(js) + while i < length: + # String literals — pass through unchanged + if js[i] in ('"', "'", "`"): + quote = js[i] + result.append(js[i]) + i += 1 + while i < length: + if js[i] == "\\" and i + 1 < length: + result.append(js[i : i + 2]) + i += 2 + elif js[i] == quote: + result.append(js[i]) + i += 1 + break + else: + result.append(js[i]) + i += 1 + # Block comment /* ... */ + elif js[i] == "/" and i + 1 < length and js[i + 1] == "*": + end = js.find("*/", i + 2) + i = end + 2 if end != -1 else length + # Line comment // ... + elif js[i] == "/" and i + 1 < length and js[i + 1] == "/": + end = js.find("\n", i) + if end == -1: + i = length + else: + # Keep the newline to preserve line structure + result.append("\n") + i = end + 1 + # Regex literal — pass through unchanged + # Heuristic: / after = ( , ; ! & | ? : [ { } ~ ^ or line start + elif js[i] == "/" and i > 0: + # Look back for operator context (skip whitespace) + j = i - 1 + while j >= 0 and js[j] in " \t": + j -= 1 + if j >= 0 and js[j] in "=(!,;:&|?[{}>~^+-*%": + result.append(js[i]) + i += 1 + while i < length: + if js[i] == "\\" and i + 1 < length: + result.append(js[i : i + 2]) + i += 2 + elif js[i] == "/": + result.append(js[i]) + i += 1 + # Regex flags + while i < length and js[i].isalpha(): + result.append(js[i]) + i += 1 + break + elif js[i] == "[": + # Character class — / doesn't end regex inside [] + result.append(js[i]) + i += 1 + while i < length and js[i] != "]": + if js[i] == "\\" and i + 1 < length: + result.append(js[i : i + 2]) + i += 2 + else: + result.append(js[i]) + i += 1 + else: + result.append(js[i]) + i += 1 + else: + result.append(js[i]) + i += 1 + else: + result.append(js[i]) + i += 1 + return "".join(result) + + def minify_html(html: str) -> str: # Tags where whitespace should be preserved - preserve_tags = ['pre', 'code', 'textarea', 'script', 'style'] - preserve_regex = '|'.join(preserve_tags) + preserve_tags = ["pre", "code", "textarea"] + script_style_tags = ["script", "style"] + preserve_regex = "|".join(preserve_tags) + script_style_regex = "|".join(script_style_tags) - # Protect preserve blocks with placeholders + # Protect preserve blocks (pre/code/textarea) with placeholders preserve_blocks = [] + def preserve(match): preserve_blocks.append(match.group(0)) - return f"__PRESERVE_BLOCK_{len(preserve_blocks)-1}__" + return f"__PRESERVE_BLOCK_{len(preserve_blocks) - 1}__" - html = re.sub(rf'<({preserve_regex})[\s\S]*?', preserve, html, flags=re.IGNORECASE) + html = re.sub( + rf"<({preserve_regex})[\s\S]*?", preserve, html, flags=re.IGNORECASE + ) + + # Strip JS/CSS comments inside + const xhr = new XMLHttpRequest(); + xhr.open('POST', '/move', true); + + xhr.onload = function () { + if (xhr.status === 200) { + window.location.reload(); + } else { + alert('Failed to move: ' + xhr.responseText); + } + closeMoveModal(); + }; + + xhr.onerror = function () { + alert('Failed to move - network error'); + closeMoveModal(); + }; + + xhr.send(formData); + } + hydrate(); + - + + \ No newline at end of file From 4c478c73b74be4e36fa7e0364ac4c56abf4bd8a4 Mon Sep 17 00:00:00 2001 From: jpirnay Date: Wed, 25 Mar 2026 13:03:10 +0100 Subject: [PATCH 2/2] Adjust progress bar --- src/network/html/FilesPage.html | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/src/network/html/FilesPage.html b/src/network/html/FilesPage.html index de01c7c8..884708d6 100644 --- a/src/network/html/FilesPage.html +++ b/src/network/html/FilesPage.html @@ -4851,11 +4851,11 @@ let batchOffset = 0; while (batchOffset < batchBuf.byteLength && ws.readyState === WebSocket.OPEN) { - // Backpressure: yield once when the send buffer is full. - // A single zero-delay yield lets the browser flush the - // WebSocket buffer without the ~4ms minimum of setTimeout. - if (ws.bufferedAmount > WS_CHUNK_SIZE * 4) { - await new Promise(r => setTimeout(r, 0)); + // Backpressure: yield when the send buffer builds up. + // Keeps the ESP32 from being overwhelmed while avoiding + // the ~4ms minimum of higher setTimeout values. + if (ws.bufferedAmount > WS_CHUNK_SIZE * 2) { + await new Promise(r => setTimeout(r, 1)); } if (ws.readyState !== WebSocket.OPEN) { @@ -4870,12 +4870,15 @@ offset = batchEnd; if (onProgress) { - onProgress(offset, totalSize); + // Cap at 99% — 100% is shown only when server confirms DONE + const reported = Math.min(offset, Math.floor(totalSize * 0.99)); + onProgress(reported, totalSize); } } sendingChunks = false; console.log('[WS] All chunks sent, waiting for DONE'); + if (onProgress) onProgress(totalSize, totalSize, 'saving'); } catch (err) { console.error('[WS] Error sending chunks:', err); sendingChunks = false; @@ -5076,20 +5079,24 @@ console.log(`[Upload] ${file.name} via ${uploadMethod}`); let uploadStartTime = 0; - const onProgress = (loaded, total) => { + const onProgress = (loaded, total, state) => { if (!uploadStartTime) uploadStartTime = Date.now(); const uploadPercent = Math.round((loaded / total) * 100); // If conversion succeeded, display goes from 50-100%, otherwise 0-100% const displayPercent = conversionSucceeded ? 50 + Math.round(uploadPercent / 2) : uploadPercent; progressFill.style.width = displayPercent + '%'; - const prefix = conversionSucceeded ? 'Converting & uploading' : 'Uploading'; let speedText = ''; const elapsed = (Date.now() - uploadStartTime) / 1000; if (elapsed > 0.5 && loaded > 0) { const kbps = (loaded / 1024) / elapsed; speedText = kbps >= 1024 ? ` — ${(kbps / 1024).toFixed(1)} MB/s` : ` — ${Math.round(kbps)} KB/s`; } - progressText.textContent = `${prefix} ${file.name} (${currentIndex + 1}/${files.length}) — ${uploadPercent}%${speedText}`; + if (state === 'saving') { + progressText.textContent = `Saving ${file.name} (${currentIndex + 1}/${files.length})...${speedText}`; + } else { + const prefix = conversionSucceeded ? 'Converting & uploading' : 'Uploading'; + progressText.textContent = `${prefix} ${file.name} (${currentIndex + 1}/${files.length}) — ${uploadPercent}%${speedText}`; + } }; const onComplete = () => {