Files

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()