"""Generate the Media Server application icon. The SVG in ``media_server/static/icons/icon.svg`` is the single source of truth. This script rasterizes it at every Windows ICO size via ``resvg-py`` (Rust-backed, identical math to Firefox's SVG renderer) and packs them all into a multi-resolution ``icon.ico``. This replaces the original 16x16-only ICO that Windows was upscaling into mush for the installer chrome, Start Menu, desktop shortcuts, and Alt+Tab. Usage: python scripts/generate-icon.py Dependencies: pip install resvg-py Pillow """ from __future__ import annotations import io from pathlib import Path import resvg_py from PIL import Image # Sizes packed into the ICO. Windows picks the closest match per surface; # more sizes = sharper rendering everywhere (taskbar, installer header, # Alt+Tab, jump lists, Start tile, desktop, file explorer details/tiles). ICO_SIZES: tuple[int, ...] = (16, 20, 24, 32, 40, 48, 64, 96, 128, 256) # The SVG source. Squircle + diagonal teal gradient + warm parchment play # triangle with a drop shadow + ghosted echo-chevrons hinting at broadcast. SVG_SOURCE = """ """ def _render_png(size: int) -> Image.Image: """Rasterize the SVG to a PNG at ``size x size`` via resvg.""" data = resvg_py.svg_to_bytes( svg_string=SVG_SOURCE, width=size, height=size, shape_rendering="geometric_precision", ) return Image.open(io.BytesIO(bytes(data))).convert("RGBA") def main() -> None: root = Path(__file__).resolve().parent.parent out_dir = root / "media_server" / "static" / "icons" out_dir.mkdir(parents=True, exist_ok=True) # SVG — canonical source, also used as the Web UI favicon. svg_path = out_dir / "icon.svg" svg_path.write_text(SVG_SOURCE, encoding="utf-8") print(f"wrote {svg_path}") # Rasterize every ICO size via resvg. frames = [_render_png(size) for size in ICO_SIZES] # Pack into a multi-resolution ICO. The "primary" image must be the # largest; the rest go via append_images. Pillow's ICO writer then # serializes one frame per size. primary = frames[-1] ico_path = out_dir / "icon.ico" primary.save( ico_path, format="ICO", sizes=[(s, s) for s in ICO_SIZES], append_images=frames[:-1], ) print(f"wrote {ico_path} ({ico_path.stat().st_size:,} bytes, sizes={list(ICO_SIZES)})") # Largest PNG for documentation / non-Windows surfaces. png_path = out_dir / "icon-256.png" primary.save(png_path, format="PNG", optimize=True) print(f"wrote {png_path}") if __name__ == "__main__": main()