#!/usr/bin/env python3 """ thumbnail.py — Resize images to max 880px width, preserve aspect ratio, no crop. Outputs compressed JPEGs to Images/{collection}/thumbnails/ Run from the SingularParticular repo root: python Images/thumbnail.py Or from inside the Images folder: python thumbnail.py Requirements: pip install Pillow """ from pathlib import Path from PIL import Image MAX_WIDTH = 360 QUALITY = 85 COLLECTIONS = ["WayBack", "YourNomadSoul", "MysterWizzard", "Exopraxist"] EXTENSIONS = {".jpg", ".jpeg", ".png", ".JPG", ".JPEG", ".PNG"} def make_thumbnail(src: Path, dest: Path): with Image.open(src) as img: orig_w, orig_h = img.size # Convert to RGB for JPEG output (handles RGBA, palette, greyscale+alpha) if img.mode not in ("RGB", "L"): img = img.convert("RGB") if orig_w > MAX_WIDTH: ratio = MAX_WIDTH / orig_w new_size = (MAX_WIDTH, int(orig_h * ratio)) img = img.resize(new_size, Image.LANCZOS) else: new_size = (orig_w, orig_h) dest.parent.mkdir(parents=True, exist_ok=True) img.save(dest, "JPEG", quality=QUALITY, optimize=True) orig_kb = src.stat().st_size // 1024 dest_kb = dest.stat().st_size // 1024 print(f" {src.name:<40} {orig_w}x{orig_h} {orig_kb}KB → {new_size[0]}x{new_size[1]} {dest_kb}KB") def main(): # Support running from repo root or from inside Images/ script_dir = Path(__file__).parent base = script_dir # Images/ total_orig = 0 total_thumb = 0 for collection in COLLECTIONS: src_dir = base / collection thumb_dir = src_dir / "thumbnails" print(f"\n── {collection} ──") if not src_dir.exists(): print(f" (folder not found: {src_dir})") continue images = sorted(f for f in src_dir.iterdir() if f.suffix in EXTENSIONS and f.is_file()) if not images: print(" (no images found)") continue for src in images: dest = thumb_dir / (src.stem + ".jpg") try: make_thumbnail(src, dest) total_orig += src.stat().st_size total_thumb += dest.stat().st_size except Exception as e: print(f" ERROR {src.name}: {e}") print(f"\n── Summary ──") print(f" Original total: {total_orig // (1024*1024)} MB") print(f" Thumbnail total: {total_thumb // (1024*1024)} MB") print(f" Saved: {(total_orig - total_thumb) // (1024*1024)} MB") print(f"\nThumbnails written to Images/{{collection}}/thumbnails/") print("Next: upload each thumbnails/ folder to FileBrowser, replacing originals.") if __name__ == "__main__": main()