diff --git a/scripts/build_html.py b/scripts/build_html.py index 248aba84..b144d5dc 100644 --- a/scripts/build_html.py +++ b/scripts/build_html.py @@ -1,5 +1,6 @@ import os import re +import gzip SRC_DIR = "src" @@ -40,12 +41,34 @@ for root, _, files in os.walk(SRC_DIR): # minified = regex.sub("\g<1>", html_content) minified = minify_html(html_content) + + # Compress with gzip (compresslevel 9 is maximum compression) + # IMPORTANT: we don't use brotli because Firefox doesn't support brotli with insecured context (only supported on HTTPS) + compressed = gzip.compress(minified.encode('utf-8'), compresslevel=9) + base_name = f"{os.path.splitext(file)[0]}Html" header_path = os.path.join(root, f"{base_name}.generated.h") with open(header_path, "w", encoding="utf-8") as h: h.write(f"// THIS FILE IS AUTOGENERATED, DO NOT EDIT MANUALLY\n\n") h.write(f"#pragma once\n") - h.write(f'constexpr char {base_name}[] PROGMEM = R"rawliteral({minified})rawliteral";\n') + h.write(f"#include \n\n") + + # Write the compressed data as a byte array + h.write(f"constexpr char {base_name}[] PROGMEM = {{\n") + + # Write bytes in rows of 16 + for i in range(0, len(compressed), 16): + chunk = compressed[i:i+16] + hex_values = ', '.join(f'0x{b:02x}' for b in chunk) + h.write(f" {hex_values},\n") + + h.write(f"}};\n\n") + h.write(f"constexpr size_t {base_name}CompressedSize = {len(compressed)};\n") + h.write(f"constexpr size_t {base_name}OriginalSize = {len(minified)};\n") print(f"Generated: {header_path}") + print(f" Original: {len(html_content)} bytes") + print(f" Minified: {len(minified)} bytes ({100*len(minified)/len(html_content):.1f}%)") + print(f" Compressed: {len(compressed)} bytes ({100*len(compressed)/len(html_content):.1f}%)") + diff --git a/src/network/CrossPointWebServer.cpp b/src/network/CrossPointWebServer.cpp index 3fd3ff87..91f1fd51 100644 --- a/src/network/CrossPointWebServer.cpp +++ b/src/network/CrossPointWebServer.cpp @@ -293,8 +293,13 @@ CrossPointWebServer::WsUploadStatus CrossPointWebServer::getWsUploadStatus() con return status; } +static void sendHtmlContent(WebServer* server, const char* data, size_t len) { + server->sendHeader("Content-Encoding", "gzip"); + server->send_P(200, "text/html", data, len); +} + void CrossPointWebServer::handleRoot() const { - server->send(200, "text/html", HomePageHtml); + sendHtmlContent(server.get(), HomePageHtml, sizeof(HomePageHtml)); LOG_DBG("WEB", "Served root page"); } @@ -385,7 +390,9 @@ bool CrossPointWebServer::isEpubFile(const String& filename) const { return lower.endsWith(".epub"); } -void CrossPointWebServer::handleFileList() const { server->send(200, "text/html", FilesPageHtml); } +void CrossPointWebServer::handleFileList() const { + sendHtmlContent(server.get(), FilesPageHtml, sizeof(FilesPageHtml)); +} void CrossPointWebServer::handleFileListData() const { // Get current path from query string (default to root) @@ -989,7 +996,7 @@ void CrossPointWebServer::handleDelete() const { } void CrossPointWebServer::handleSettingsPage() const { - server->send(200, "text/html", SettingsPageHtml); + sendHtmlContent(server.get(), SettingsPageHtml, sizeof(SettingsPageHtml)); LOG_DBG("WEB", "Served settings page"); }