from __future__ import annotations import json import sys from pathlib import Path from PIL import Image, ImageDraw, ImageFont ROOT = Path(sys.argv[1] if len(sys.argv) > 1 else "public/ai-courseware-templates/extracted") OUTPUT = Path(sys.argv[2] if len(sys.argv) > 2 else "artifacts/music-template-extraction/extracted-contact-sheets") def font(size: int, bold: bool = False): path = Path(r"C:\Windows\Fonts\msyhbd.ttc" if bold else r"C:\Windows\Fonts\msyh.ttc") try: return ImageFont.truetype(str(path), size) except OSError: return ImageFont.load_default() def main() -> None: items = json.loads((ROOT / "manifest.json").read_text(encoding="utf-8"))["items"] OUTPUT.mkdir(parents=True, exist_ok=True) thumb = (150, 84) margin = 14 row_height = 132 page_size = 6 width = margin * 11 + thumb[0] * 10 for page_index, start in enumerate(range(0, len(items), page_size), start=1): page_items = items[start:start + page_size] canvas = Image.new("RGB", (width, margin + row_height * len(page_items)), "#E9EDF2") draw = ImageDraw.Draw(canvas) for row, item in enumerate(page_items): top = margin + row * row_height draw.text((margin, top), f"{item['id']} {item['name']} | {item['scene']}", font=font(20, True), fill="#202631") for column, background in enumerate(item["backgrounds"]): source = ROOT / item["id"] / Path(background).name with Image.open(source) as raw: image = raw.convert("RGB").resize(thumb, Image.Resampling.LANCZOS) left = margin + column * (thumb[0] + margin) image_top = top + 36 canvas.paste(image, (left, image_top)) draw.rectangle((left, image_top, left + thumb[0], image_top + thumb[1]), outline="#AEB8C5", width=1) canvas.save(OUTPUT / f"templates-{page_index:02d}.jpg", quality=92, optimize=True) print(json.dumps({"templates": len(items), "sheets": (len(items) + page_size - 1) // page_size}, ensure_ascii=False)) if __name__ == "__main__": main()