149 lines
5.3 KiB
Python
149 lines
5.3 KiB
Python
# SCRIPT TO GZIP CRITICAL FILES FOR ACCELERATED WEBSERVING
|
||
# see also https://community.platformio.org/t/question-esp32-compress-files-in-data-to-gzip-before-upload-possible-to-spiffs/6274/10
|
||
|
||
|
||
import glob
|
||
import shutil
|
||
import gzip
|
||
import os
|
||
import subprocess
|
||
import platform
|
||
Import("env")
|
||
Import("projenv")
|
||
|
||
def ensure_node_tool(package_name, binary_name=None):
|
||
"""Installiert das Tool lokal, wenn es fehlt – mit npm init bei Bedarf"""
|
||
if binary_name is None:
|
||
binary_name = package_name
|
||
|
||
project_dir = env.subst('$PROJECT_DIR')
|
||
tools_dir = os.path.join(project_dir, 'tools_node')
|
||
local_bin = os.path.join(tools_dir, 'node_modules', '.bin', binary_name)
|
||
|
||
os.makedirs(tools_dir, exist_ok=True)
|
||
|
||
# Initialisiere npm, falls noch nicht geschehen
|
||
if not os.path.isfile(os.path.join(tools_dir, 'package.json')):
|
||
print("🛠️ Initializing local npm project in tools_node...")
|
||
subprocess.run(['npm', 'init', '-y'], cwd=tools_dir, check=True)
|
||
|
||
# Installiere Tool (idempotent)
|
||
try:
|
||
subprocess.run(['npm', 'install', package_name], cwd=tools_dir, check=True)
|
||
except Exception as e:
|
||
print(f"❌ Fehler beim Installieren von {package_name}: {e}")
|
||
|
||
return local_bin if os.path.isfile(local_bin) else binary_name
|
||
|
||
# Tools sicherstellen
|
||
# Tools sicherstellen – Package-Name und CLI-Binary ggf. unterschiedlich
|
||
html_minifier_path = ensure_node_tool("html-minifier")
|
||
terser_path = ensure_node_tool("terser")
|
||
cssnano_path = ensure_node_tool("cssnano-cli", "cssnano")
|
||
|
||
def minify_html(input_path, output_path):
|
||
subprocess.run([html_minifier_path, '--collapse-whitespace', '--remove-comments', input_path, '-o', output_path])
|
||
|
||
def minify_js(input_path, output_path):
|
||
subprocess.run([terser_path, input_path, '-o', output_path, '-c', '-m'])
|
||
|
||
def minify_css(input_path, output_path):
|
||
subprocess.run([cssnano_path, '--no-discardUnused', input_path, output_path])
|
||
|
||
def process_file(src_path, dest_path):
|
||
_, file_extension = os.path.splitext(src_path)
|
||
os.makedirs(os.path.dirname(dest_path), exist_ok=True)
|
||
if file_extension.lower() == '.js':
|
||
minify_js(src_path, dest_path)
|
||
elif file_extension.lower() == '.css':
|
||
minify_css(src_path, dest_path)
|
||
elif file_extension.lower() in ['.html', '.htm']:
|
||
minify_html(src_path, dest_path)
|
||
else:
|
||
shutil.copy2(src_path, dest_path)
|
||
|
||
def strip_files(src_dir, dest_dir):
|
||
os.makedirs(dest_dir, exist_ok=True)
|
||
for root, _, files in os.walk(src_dir):
|
||
for filename in files:
|
||
src_path = os.path.join(root, filename)
|
||
rel_path = os.path.relpath(src_path, src_dir)
|
||
dest_path = os.path.join(dest_dir, rel_path)
|
||
process_file(src_path, dest_path)
|
||
|
||
def gzip_file(src_path, dst_path):
|
||
with open(src_path, 'rb') as src, gzip.open(dst_path, 'wb') as dst:
|
||
for chunk in iter(lambda: src.read(4096), b""):
|
||
dst.write(chunk)
|
||
|
||
def getListOfFiles(dirName):
|
||
entries = os.listdir(dirName)
|
||
allFiles = []
|
||
for entry in entries:
|
||
fullPath = os.path.join(dirName, entry)
|
||
if os.path.isdir(fullPath):
|
||
allFiles += getListOfFiles(fullPath)
|
||
else:
|
||
allFiles.append(fullPath)
|
||
return allFiles
|
||
|
||
def safe_relpath(path, start):
|
||
return os.path.relpath(path, start).replace("\\", "/")
|
||
|
||
def gzip_webfiles(source, target, env):
|
||
filetypes_to_gzip = ['.css', '.png', '.js', '.ico', '.woff2', '.json']
|
||
print('\nGZIP: Starting gzip-Process for LittleFS-Image...\n')
|
||
src_dir = os.path.join(env.get('PROJECT_DIR'), 'data_src')
|
||
temp_dir = os.path.join(env.get('PROJECT_DIR'), 'data_stripped')
|
||
dst_dir = env.get('PROJECT_DATA_DIR')
|
||
|
||
strip_files(src_dir, temp_dir)
|
||
|
||
if os.path.exists(dst_dir):
|
||
shutil.rmtree(dst_dir)
|
||
os.mkdir(dst_dir)
|
||
|
||
files_to_copy = []
|
||
files_to_gzip = []
|
||
|
||
for file in getListOfFiles(temp_dir):
|
||
_, ext = os.path.splitext(file)
|
||
if ext in filetypes_to_gzip:
|
||
files_to_gzip.append(file)
|
||
else:
|
||
files_to_copy.append(safe_relpath(file, temp_dir))
|
||
|
||
for file in files_to_copy:
|
||
full_dst = os.path.join(dst_dir, file)
|
||
os.makedirs(os.path.dirname(full_dst), exist_ok=True)
|
||
shutil.copy(os.path.join(temp_dir, file), full_dst)
|
||
|
||
was_error = False
|
||
try:
|
||
for src in files_to_gzip:
|
||
rel_path = safe_relpath(src, temp_dir)
|
||
dst_path = os.path.join(dst_dir, rel_path + '.gz')
|
||
os.makedirs(os.path.dirname(dst_path), exist_ok=True)
|
||
print('GZIP: compressing... ' + rel_path)
|
||
gzip_file(src, dst_path)
|
||
except IOError as e:
|
||
was_error = True
|
||
print('GZIP: Fehler beim Komprimieren:', e)
|
||
|
||
if was_error:
|
||
print('⚠️ GZIP: Nicht alle Dateien konnten verarbeitet werden.\n')
|
||
else:
|
||
print('✅ GZIP: Komprimierung abgeschlossen.\n')
|
||
shutil.rmtree(temp_dir)
|
||
|
||
def gzip_binffiles(source, target, env):
|
||
littlefsbin = target[0].get_abspath()
|
||
tmpbin = os.path.join(os.path.dirname(littlefsbin), 'filesystem.fs')
|
||
shutil.copyfile(littlefsbin, tmpbin)
|
||
gzip_file(tmpbin, tmpbin + '.gz')
|
||
os.remove(tmpbin)
|
||
|
||
# Hooks setzen
|
||
env.AddPreAction('$BUILD_DIR/littlefs.bin', gzip_webfiles)
|
||
env.AddPostAction('$BUILD_DIR/littlefs.bin', gzip_binffiles)
|