"""모션 HTML을 MP4로 렌더링.

HTML은 렌더 계약을 따라야 함:
  window.renderAt(t)  t초의 화면을 그 자리에서 그림(Promise를 돌려줘도 됨)
  window.DURATION     전체 길이(초)
  window.READY        (선택) 글꼴·텍스처 준비가 끝나면 풀리는 Promise

사용:
  python3 -I mp4-render.py <HTML> [<MP4>] [--fps 30] [--from 초] [--to 초] [--scale 1]
필요: Python Playwright(chromium), ffmpeg
"""
import argparse
import pathlib
import subprocess
import time
import urllib.parse

from playwright.sync_api import sync_playwright


def main():
    ap = argparse.ArgumentParser(description="모션 HTML → MP4")
    ap.add_argument("html")
    ap.add_argument("mp4", nargs="?", help="기본: HTML과 같은 이름의 .mp4")
    ap.add_argument("--fps", type=int, default=30)
    ap.add_argument("--from", dest="t0", type=float, default=0.0)
    ap.add_argument("--to", dest="t1", type=float, help="기본: window.DURATION")
    ap.add_argument("--scale", type=float, default=1.0, help="1 = 1920×1080")
    a = ap.parse_args()

    html = pathlib.Path(a.html).resolve()
    out = pathlib.Path(a.mp4).resolve() if a.mp4 else html.with_suffix(".mp4")
    url = "file://" + urllib.parse.quote(str(html)) + "?record"
    w, h = round(1920 * a.scale / 2) * 2, round(1080 * a.scale / 2) * 2

    with sync_playwright() as p:
        browser = p.chromium.launch(args=["--use-angle=metal", "--ignore-gpu-blocklist"])
        page = browser.new_page(viewport={"width": w, "height": h})
        page.goto(url)
        page.wait_for_function(
            "typeof window.renderAt === 'function' && window.DURATION > 0", timeout=60000
        )
        page.evaluate("async () => { if (document.fonts) await document.fonts.ready; if (window.READY) await window.READY; }")
        t1 = a.t1 if a.t1 is not None else page.evaluate("window.DURATION")
        n = int(round((t1 - a.t0) * a.fps))

        ff = subprocess.Popen(
            ["ffmpeg", "-y", "-loglevel", "error", "-f", "image2pipe", "-framerate", str(a.fps),
             "-i", "-", "-c:v", "libx264", "-pix_fmt", "yuv420p", "-crf", "18", "-preset", "medium",
             "-movflags", "+faststart", str(out)],
            stdin=subprocess.PIPE,
        )
        start = time.time()
        for i in range(n):
            t = a.t0 + i / a.fps
            page.evaluate("t => window.renderAt(t)", t)
            ff.stdin.write(page.screenshot(type="png"))
            if i % (a.fps * 5) == 0:
                print(f"{t:6.1f}s / {t1:.1f}s  ({time.time() - start:.0f}초 경과)", flush=True)
        ff.stdin.close()
        ff.wait()
        browser.close()
    print(f"완료: {out} ({n}프레임, {a.fps}fps)")


if __name__ == "__main__":
    main()
