109 lines
4.7 KiB
Python
109 lines
4.7 KiB
Python
import os
|
|
import shutil
|
|
from PIL import Image
|
|
|
|
def get_target_max_dimension(rel_path: str, filename: str) -> int:
|
|
path_lower = rel_path.lower()
|
|
name_lower = filename.lower()
|
|
|
|
# Small UI icons
|
|
if 'icon' in name_lower or 'icon' in path_lower:
|
|
return 512
|
|
# Logos
|
|
elif 'logo' in name_lower:
|
|
return 800
|
|
# Full background banners
|
|
elif 'bg' in name_lower or 'background' in name_lower:
|
|
return 1920
|
|
# Diagrams / mind maps / hero screenshots
|
|
elif 'hero' in name_lower or 'dashboard' in name_lower or 'mindmap' in name_lower:
|
|
return 1600
|
|
# General feature cards / how-it-works / screenshots
|
|
else:
|
|
return 1200
|
|
|
|
def optimize_public_assets():
|
|
base_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
|
public_dir = os.path.join(base_dir, 'public')
|
|
backup_dir = os.path.join(base_dir, 'public_raw_backup')
|
|
|
|
print(f"=== DocQube Landing Asset Optimizer ===")
|
|
print(f"Public directory: {public_dir}")
|
|
print(f"Backup directory: {backup_dir}")
|
|
|
|
# 1. Create a full safety backup of original images if not already backed up
|
|
if not os.path.exists(backup_dir):
|
|
print("\nCreating safety backup of raw originals in public_raw_backup/...")
|
|
shutil.copytree(public_dir, backup_dir)
|
|
print("[OK] Safety backup complete.")
|
|
else:
|
|
print("\nSafety backup already exists in public_raw_backup/.")
|
|
|
|
initial_total_bytes = 0
|
|
final_total_bytes = 0
|
|
optimized_count = 0
|
|
|
|
# Walk through public directory
|
|
for root, _, files in os.walk(public_dir):
|
|
for f in files:
|
|
ext = os.path.splitext(f)[1].lower()
|
|
if ext not in ['.png', '.jpg', '.jpeg']:
|
|
continue
|
|
|
|
file_path = os.path.join(root, f)
|
|
rel_path = os.path.relpath(file_path, public_dir)
|
|
orig_size = os.path.getsize(file_path)
|
|
initial_total_bytes += orig_size
|
|
|
|
try:
|
|
with Image.open(file_path) as im:
|
|
orig_w, orig_h = im.size
|
|
max_dim = get_target_max_dimension(rel_path, f)
|
|
scale = min(1.0, max_dim / max(orig_w, orig_h))
|
|
|
|
if scale < 1.0:
|
|
new_w = max(1, int(orig_w * scale))
|
|
new_h = max(1, int(orig_h * scale))
|
|
# Lanczos high quality resampling
|
|
processed = im.resize((new_w, new_h), Image.Resampling.LANCZOS)
|
|
else:
|
|
processed = im.copy()
|
|
|
|
# 1. Save optimized WebP next to it (e.g. securityicon1.webp)
|
|
webp_path = os.path.splitext(file_path)[0] + '.webp'
|
|
processed.save(webp_path, 'WEBP', quality=86, method=6)
|
|
|
|
# 2. Overwrite the .png/.jpg in place with optimized version
|
|
# This ensures all existing <img src="...png"> tags in React/Next.js
|
|
# immediately load the ultra-fast compressed image without breaking any code!
|
|
if ext == '.png':
|
|
processed.save(file_path, 'PNG', optimize=True, compress_level=9)
|
|
else:
|
|
processed.save(file_path, 'JPEG', quality=86, optimize=True)
|
|
|
|
new_size = os.path.getsize(file_path)
|
|
final_total_bytes += new_size
|
|
optimized_count += 1
|
|
|
|
reduction = (1 - (new_size / orig_size)) * 100 if orig_size > 0 else 0
|
|
if orig_size > 300 * 1024 or reduction > 40:
|
|
print(f"Optimized {rel_path}: {orig_size/1024:.1f}KB ({orig_w}x{orig_h}) -> {new_size/1024:.1f}KB (-{reduction:.1f}%)")
|
|
|
|
except Exception as e:
|
|
print(f"Error processing {rel_path}: {e}")
|
|
final_total_bytes += orig_size
|
|
|
|
saved_bytes = initial_total_bytes - final_total_bytes
|
|
print(f"\n==========================================")
|
|
print(f"Optimization Complete!")
|
|
print(f"Total images processed: {optimized_count}")
|
|
print(f"Original size: {initial_total_bytes / (1024*1024):.2f} MB")
|
|
print(f"New size: {final_total_bytes / (1024*1024):.2f} MB")
|
|
print(f"Total saved: {saved_bytes / (1024*1024):.2f} MB (-{(saved_bytes / initial_total_bytes)*100:.1f}%)")
|
|
print(f"WebP copies: Generated for all assets")
|
|
print(f"Originals: Preserved safely in 'public_raw_backup/'")
|
|
print(f"==========================================")
|
|
|
|
if __name__ == '__main__':
|
|
optimize_public_assets()
|