#!/usr/bin/env python3 """Image size budget check. Flags images that exceed a per-file byte budget so heavy assets don't creep back in after an optimization pass. Offline and fast — safe for the default audit run. Budgets (override on the CLI): raster (png/jpg/jpeg/webp) : 250 KB gif : 600 KB (animations are bigger) Usage: python tools/image_budget.py [--max-kb 250] [--gif-max-kb 600] """ from __future__ import annotations import argparse from pathlib import Path ROOT = Path(__file__).resolve().parents[1] # Include section asset dirs that hold page-local images (not only /assets/img). SCAN_DIRS = ( "assets/img", "images", "startpage", "computers", "gaming", "vblog", "mstartpage", ) RASTER = {".png", ".jpg", ".jpeg", ".webp"} def main() -> int: ap = argparse.ArgumentParser(description="Image size budget check") ap.add_argument("--max-kb", type=int, default=250) ap.add_argument("--gif-max-kb", type=int, default=600) args = ap.parse_args() over: list[tuple[int, str, int]] = [] # (size, path, budget) for d in SCAN_DIRS: base = ROOT / d if not base.is_dir(): continue for f in base.rglob("*"): if not f.is_file(): continue ext = f.suffix.lower() if ext == ".gif": budget = args.gif_max_kb * 1024 elif ext in RASTER: budget = args.max_kb * 1024 else: continue sz = f.stat().st_size if sz > budget: over.append((sz, f.relative_to(ROOT).as_posix(), budget)) if over: print("Images over budget:") for sz, path, budget in sorted(over, reverse=True): print(f"- {path}: {sz/1024:.0f} KB (budget {budget//1024} KB)") print("\nTip: run ./tools/optimize_images.sh") return 1 print("OK: all images within budget") return 0 if __name__ == "__main__": raise SystemExit(main())