#!/usr/bin/env python3
"""MelonTV produced show (v4): 15-20s full-frame video.
Structure: animated INTRO (gradient bg + bouncing logo + stickers) ->
SCENE 1 (clip1 full-frame + blurred bg + decorative frame + drifting
stickers + bouncy caption) -> SCENE 2 (clip2) -> SUBSCRIBE OUTRO.
All assets generated with ImageMagick; text/animation via libass.
Every ffmpeg render hard-capped to 20% CPU / 8G RAM.
Run from melontv/:  python3 scripts/build_show.py
"""
import subprocess, os

CLIP_LEN  = 4.9667
INTRO_LEN = 4.5
OUTRO_LEN = 4.0
TRANS     = 1.0
CROP      = "crop=1880:2120:980:20"
SOURCES   = ["input/clip1.mp4", "input/clip2.mp4"]
SCENE_CAPS = ["Catboy finds a lost kitten!", "Owlette hugs her baby owl!"]
FONTDIR   = "/root/.fonts/chess_shorts"
BEBAS     = FONTDIR + "/BebasNeue-Regular.ttf"

CAP = ["systemd-run", "--scope", "--quiet",
       "-p", "CPUQuota=240%", "-p", "MemoryMax=8G", "nice", "-n", "19"]

def run(cmd):
    subprocess.run(CAP + cmd, check=True)

# ---------------------------------------------------------------- assets
def make_assets():
    a = "work/assets"
    os.makedirs(a, exist_ok=True)
    run(["convert", "-size", "760x230", "xc:none", "-fill", "#ff4fa3",
         "-draw", "roundrectangle 8,8,752,222,50,50", "-fill", "#ffffff",
         "-font", BEBAS, "-pointsize", "120", "-gravity", "center",
         "-stroke", "#7a1f52", "-strokewidth", "3", "-annotate", "+0+4",
         "MelonTV", a + "/logo.png"])
    run(["convert", "-size", "200x200", "xc:none", "-fill", "#FFD400",
         "-stroke", "#E39A00", "-strokewidth", "5", "-draw",
         "polygon 100,12 122,74 190,74 135,113 156,182 100,140 44,182 65,113 10,74 78,74",
         a + "/star.png"])
    run(["convert", "-size", "200x200", "xc:none", "-fill", "#FF4D6D",
         "-stroke", "#c92a49", "-strokewidth", "4", "-draw",
         "path 'M 100,58 C 100,18 42,18 42,72 C 42,112 100,150 100,182 C 100,150 158,112 158,72 C 158,18 100,18 100,58 Z'",
         a + "/heart.png"])
    run(["convert", "-size", "130x130", "xc:none", "-fill", "#ffffff",
         "-draw", "polygon 65,8 76,54 122,65 76,76 65,122 54,76 8,65 54,54",
         a + "/sparkle.png"])
    run(["convert", "-size", "1920x1080", "xc:none", "-stroke", "#ffffff",
         "-strokewidth", "14", "-fill", "none",
         "-draw", "roundrectangle 18,18,1902,1062,46,46", "-stroke", "#ffd400",
         "-strokewidth", "6", "-draw", "roundrectangle 30,30,1890,1050,40,40",
         a + "/frame.png"])
    print("assets ready")

# ---------------------------------------------------------------- ass helpers
ASS_HEAD = ("[Script Info]\nScriptType: v4.00+\nPlayResX: 1920\nPlayResY: 1080\n"
            "ScaledBorderAndShadow: yes\n\n[V4+ Styles]\nFormat: Name, Fontname, "
            "Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, "
            "Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, "
            "BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, "
            "Encoding\n")
ASS_EV = ("\n[Events]\nFormat: Layer, Start, End, Style, Name, MarginL, MarginR, "
          "MarginV, Effect, Text\n")

def write_ass(path, styles, events):
    with open(path, "w") as f:
        f.write(ASS_HEAD + "\n".join(styles) + "\n" + ASS_EV + "\n".join(events) + "\n")

POP = r"{\fad(120,150)\fscx66\fscy66\t(0,220,\fscx106\fscy106)\t(220,340,\fscx100\fscy100)}"

def scene_cap_file(i):
    p = "work/cap%d.ass" % i
    st = ("Style: Cap,Oswald,70,&H0000E5FF,&H000000FF,&H00202050,&H90000000,"
          "-1,0,0,0,100,100,1,0,1,5,3,2,120,120,72,1")
    ev = "Dialogue: 0,0:00:00.30,0:00:04.90,Cap,,0,0,0,,%s%s" % (POP, SCENE_CAPS[i])
    write_ass(p, [st], [ev])
    return p

def intro_tag_file():
    p = "work/intro_tag.ass"
    st = ("Style: Tag,Oswald,50,&H0000E5FF,&H000000FF,&H00202050,&H90000000,"
          "-1,0,0,0,100,100,2,0,1,3,3,5,60,60,60,1")
    ev = (r"Dialogue: 0,0:00:00.80,0:00:04.50,Tag,,0,0,0,,{\an5\pos(960,690)"
          r"\fad(400,300)}Fun stories for kids")
    write_ass(p, [st], [ev])
    return p

def outro_file():
    p = "work/outro.ass"
    st1 = ("Style: Big,Bebas Neue,150,&H00FFFFFF,&H000000FF,&H00303080,&H96000000,"
           "-1,0,0,0,100,100,3,0,1,6,4,5,60,60,60,1")
    st2 = ("Style: Sub,Oswald,50,&H0000E5FF,&H000000FF,&H00202050,&H90000000,"
           "-1,0,0,0,100,100,2,0,1,3,3,5,60,60,60,1")
    ev1 = (r"Dialogue: 0,0:00:00.20,0:00:04.00,Big,,0,0,0,,{\an5\pos(960,400)"
           r"\fad(250,250)\fscx55\fscy55\t(0,300,\fscx108\fscy108)\t(300,460,\fscx100\fscy100)}Subscribe!")
    ev2 = (r"Dialogue: 0,0:00:00.90,0:00:04.00,Sub,,0,0,0,,{\an5\pos(960,540)"
           r"\fad(400,300)}New videos every week")
    write_ass(p, [st1, st2], [ev1, ev2])
    return p

# ---------------------------------------------------------------- stages
def build_intro():
    tag = intro_tag_file()
    grad = ("gradients=s=1920x1080:c0=0xff6ec7:c1=0x6ec3ff:c2=0xffe66e:"
            "n=3:speed=0.008:d=%s:r=30" % INTRO_LEN)
    fc = (
        "[0:v]format=yuv420p[bg];"
        "[1:v]scale=760:-2,fade=t=in:st=0:d=0.4:alpha=1[lg];"
        "[bg][lg]overlay=x='(W-w)/2':y='(H-h)/2-30+55*exp(-2.5*t)*cos(7*t)'[i1];"
        "[2:v]scale=180:-2,fade=t=in:st=0.3:d=0.4:alpha=1[st];"
        "[i1][st]overlay=x='360+20*sin(1.5*t)':y='250+18*sin(2*t)'[i2];"
        "[4:v]scale=150:-2,fade=t=in:st=0.5:d=0.4:alpha=1[ht];"
        "[i2][ht]overlay=x='1400+18*sin(1.8*t)':y='230+20*sin(1.3*t)'[i3];"
        "[3:v]scale=120:-2,fade=t=in:st=0.7:d=0.4:alpha=1[sp];"
        "[i3][sp]overlay=x='1420':y='700+22*sin(2.2*t)'[i4];"
        "[i4]ass=%s:fontsdir=%s,fade=t=in:st=0:d=0.5,setsar=1,format=yuv420p[vout]"
        % (tag, FONTDIR))
    run(["ffmpeg", "-y", "-loglevel", "error",
         "-f", "lavfi", "-i", grad,
         "-loop", "1", "-i", "work/assets/logo.png",
         "-loop", "1", "-i", "work/assets/star.png",
         "-loop", "1", "-i", "work/assets/sparkle.png",
         "-loop", "1", "-i", "work/assets/heart.png",
         "-filter_complex", fc, "-map", "[vout]", "-t", str(INTRO_LEN),
         "-r", "30", "-c:v", "libx264", "-crf", "19", "-preset", "medium",
         "work/intro.mp4"])
    print("wrote work/intro.mp4")

def build_scene(i):
    cap = scene_cap_file(i)
    fc = (
        "[0:v]%s,split=2[a][b];"
        "[a]scale=1920:1080:force_original_aspect_ratio=increase,crop=1920:1080,"
        "gblur=sigma=28,eq=brightness=-0.06:saturation=1.2[bg];"
        "[b]scale=-2:1004,setsar=1[fg];"
        "[bg][fg]overlay=(W-w)/2:(H-h)/2:shortest=1,"
        "eq=saturation=1.16:contrast=1.05,vignette=PI/5[base];"
        "[base][1:v]overlay=0:0[f1];"
        "[5:v]scale=300:-2,format=rgba[lg];[f1][lg]overlay=44:40[f2];"
        "[2:v]scale=150:-2[st];[f2][st]overlay=x='1560+28*sin(2*t)':y='120+20*sin(3*t)'[f3];"
        "[3:v]scale=140:-2[ht];[f3][ht]overlay=x='95':y='150+25*sin(2*t)'[f4];"
        "[4:v]scale=110:-2[sp];[f4][sp]overlay=x='1540+15*sin(4*t)':y='830+16*sin(2*t)'[f5];"
        "[f5]ass=%s:fontsdir=%s,setsar=1,format=yuv420p[vout]"
        % (CROP, cap, FONTDIR))
    run(["ffmpeg", "-y", "-loglevel", "error", "-i", SOURCES[i],
         "-i", "work/assets/frame.png", "-i", "work/assets/star.png",
         "-i", "work/assets/heart.png", "-i", "work/assets/sparkle.png",
         "-i", "work/assets/logo.png",
         "-filter_complex", fc, "-map", "[vout]", "-t", str(CLIP_LEN),
         "-r", "30", "-c:v", "libx264", "-crf", "19", "-preset", "medium",
         "-pix_fmt", "yuv420p", "-an", "work/scene%d.mp4" % i])
    print("wrote work/scene%d.mp4" % i)

def build_outro():
    outro = outro_file()
    grad = ("gradients=s=1920x1080:c0=0x6ec3ff:c1=0xffe66e:c2=0xff6ec7:"
            "n=3:speed=0.008:d=%s:r=30" % OUTRO_LEN)
    fc = (
        "[0:v]format=yuv420p[bg];"
        "[1:v]scale=520:-2,fade=t=in:st=0.2:d=0.4:alpha=1[lg];"
        "[bg][lg]overlay=x='(W-w)/2':y='720'[o1];"
        "[2:v]scale=170:-2,fade=t=in:st=0.4:d=0.4:alpha=1[ht];"
        "[o1][ht]overlay=x='1380+20*sin(2*t)':y='250+20*sin(1.5*t)'[o2];"
        "[3:v]scale=160:-2,fade=t=in:st=0.6:d=0.4:alpha=1[st];"
        "[o2][st]overlay=x='330+18*sin(1.8*t)':y='250+18*sin(2*t)'[o3];"
        "[o3]ass=%s:fontsdir=%s,fade=t=in:st=0:d=0.4,setsar=1,format=yuv420p[vout]"
        % (outro, FONTDIR))
    run(["ffmpeg", "-y", "-loglevel", "error",
         "-f", "lavfi", "-i", grad,
         "-loop", "1", "-i", "work/assets/logo.png",
         "-loop", "1", "-i", "work/assets/heart.png",
         "-loop", "1", "-i", "work/assets/star.png",
         "-filter_complex", fc, "-map", "[vout]", "-t", str(OUTRO_LEN),
         "-r", "30", "-c:v", "libx264", "-crf", "19", "-preset", "medium",
         "work/outro.mp4"])
    print("wrote work/outro.mp4")

def finalize():
    pieces = ["work/intro.mp4", "work/scene0.mp4", "work/scene1.mp4", "work/outro.mp4"]
    lengths = [INTRO_LEN, CLIP_LEN, CLIP_LEN, OUTRO_LEN]
    trans = ["fadewhite", "circleopen", "fadeslow"]
    inputs = []
    for p in pieces:
        inputs += ["-i", p]
    fc, prev, cum = [], "[0:v]", lengths[0]
    for k in range(1, len(pieces)):
        offset = round(cum - TRANS, 3)
        out = "[v%d]" % k if k < len(pieces) - 1 else "[vout]"
        fc.append("%s[%d:v]xfade=transition=%s:duration=%s:offset=%s%s"
                  % (prev, k, trans[k - 1], TRANS, offset, out))
        prev = out
        cum = cum + lengths[k] - TRANS
    total = round(cum, 2)
    print("finalize -> ~%.1fs" % total)
    run(["ffmpeg", "-y", "-loglevel", "error"] + inputs +
        ["-filter_complex", ";".join(fc), "-map", "[vout]",
         "-c:v", "libx264", "-crf", "19", "-preset", "medium",
         "-pix_fmt", "yuv420p", "-r", "30", "output/melontv_show_1080p.mp4"])
    print("wrote output/melontv_show_1080p.mp4")

if __name__ == "__main__":
    os.chdir(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
    make_assets()
    build_intro()
    build_scene(0)
    build_scene(1)
    build_outro()
    finalize()
