Upload edit\gen_full.py with huggingface_hub
Browse files- edit//gen_full.py +222 -0
edit//gen_full.py
ADDED
|
@@ -0,0 +1,222 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Generate hf/index.html for the FULL LM Studio MTP video (all chapters)."""
|
| 2 |
+
import json, sys
|
| 3 |
+
from pathlib import Path
|
| 4 |
+
sys.path.insert(0, r"C:\Users\palas\.claude\skills\screencast-hype\scripts")
|
| 5 |
+
from captions_html import build_captions
|
| 6 |
+
import cutmap
|
| 7 |
+
M = cutmap.map_time
|
| 8 |
+
DUR = round(cutmap.out_duration() + 0.3, 2)
|
| 9 |
+
|
| 10 |
+
ROOT = Path(r"D:\PromptEngineer48\In-Progress\P11-Editor\edit")
|
| 11 |
+
words = json.load(open(ROOT/"transcripts/LMSTUDIO-MTP1.json", encoding="utf-8"))["words"]
|
| 12 |
+
|
| 13 |
+
body = [] # HTML element strings
|
| 14 |
+
tw = [] # GSAP tween JS lines
|
| 15 |
+
center = [] # ids needing xPercent/yPercent centering
|
| 16 |
+
sfx = [] # (file, src_time, vol)
|
| 17 |
+
track = [2] # mutable track index counter for sfx
|
| 18 |
+
|
| 19 |
+
def add_sfx(f, t, v=0.8):
|
| 20 |
+
sfx.append((f, M(t), v, track[0])); track[0]+=1
|
| 21 |
+
|
| 22 |
+
# ---------- captions ----------
|
| 23 |
+
cap_divs, cap_tw = build_captions(words, 0, cutmap.SRC_DURATION, per=2, map_time=M)
|
| 24 |
+
body.append(cap_divs)
|
| 25 |
+
tw.append(cap_tw)
|
| 26 |
+
|
| 27 |
+
# ---------- helper builders ----------
|
| 28 |
+
def title_card(cid, kicker, title, subtitle, accent, tin, tout):
|
| 29 |
+
center.append(cid)
|
| 30 |
+
body.append(f'''<div class="title-card glass" id="{cid}">
|
| 31 |
+
{'<div class="kicker">'+kicker+'</div>' if kicker else ''}
|
| 32 |
+
<div class="bigt">{title}</div>
|
| 33 |
+
<div class="uline" id="{cid}_u" style="background:{accent};box-shadow:0 0 22px {accent}"></div>
|
| 34 |
+
<div class="subt" id="{cid}_s" style="color:{accent}">{subtitle}</div>
|
| 35 |
+
</div>''')
|
| 36 |
+
tw.append(f'tl.set("#{cid}",{{xPercent:-50,yPercent:-50}},0);')
|
| 37 |
+
tw.append(f'tl.fromTo("#{cid}",{{opacity:0,scale:0.88,filter:"blur(14px)"}},{{opacity:1,scale:1,filter:"blur(0px)",duration:0.55,ease:"power3.out"}},{M(tin):.2f});')
|
| 38 |
+
tw.append(f'tl.to("#{cid}_u",{{width:480,duration:0.5,ease:"power2.out"}},{M(tin)+0.35:.2f});')
|
| 39 |
+
tw.append(f'tl.fromTo("#{cid}_s",{{opacity:0,y:16}},{{opacity:1,y:0,duration:0.4,ease:"power2.out"}},{M(tin)+0.6:.2f});')
|
| 40 |
+
tw.append(f'tl.to("#{cid}",{{opacity:0,scale:0.96,y:-20,duration:0.4,ease:"power2.in"}},{M(tout):.2f});')
|
| 41 |
+
|
| 42 |
+
def chip(cid, text, accent, tin, tout):
|
| 43 |
+
body.append(f'<div class="chip glass" id="{cid}" style="border-color:{accent}66"><span style="color:{accent}">●</span> {text}</div>')
|
| 44 |
+
center.append(cid)
|
| 45 |
+
tw.append(f'tl.set("#{cid}",{{xPercent:-50}},0);')
|
| 46 |
+
tw.append(f'tl.fromTo("#{cid}",{{opacity:0,y:-26,scale:0.9}},{{opacity:1,y:0,scale:1,duration:0.45,ease:"back.out(2)"}},{M(tin):.2f});')
|
| 47 |
+
tw.append(f'tl.to("#{cid}",{{opacity:0,y:-18,duration:0.35,ease:"power2.in"}},{M(tout):.2f});')
|
| 48 |
+
|
| 49 |
+
def corner_badge(cid, text, color, tin, tout):
|
| 50 |
+
body.append(f'<div class="cbadge" id="{cid}" style="border-color:{color};color:{color}"><b style="color:{color}">{text}</b></div>')
|
| 51 |
+
tw.append(f'tl.fromTo("#{cid}",{{opacity:0,x:40}},{{opacity:1,x:0,duration:0.4,ease:"power3.out"}},{M(tin):.2f});')
|
| 52 |
+
tw.append(f'tl.to("#{cid}",{{opacity:0,x:40,duration:0.3,ease:"power2.in"}},{M(tout):.2f});')
|
| 53 |
+
|
| 54 |
+
def stat_pop(cid, big, small, accent, tin, tout):
|
| 55 |
+
center.append(cid)
|
| 56 |
+
body.append(f'''<div class="statpop glass" id="{cid}">
|
| 57 |
+
<span class="sp_big" style="color:{accent}">{big}</span>
|
| 58 |
+
<span class="sp_small">{small}</span></div>''')
|
| 59 |
+
tw.append(f'tl.set("#{cid}",{{xPercent:-50,yPercent:-50}},0);')
|
| 60 |
+
tw.append(f'tl.fromTo("#{cid}",{{opacity:0,scale:0.6}},{{opacity:1,scale:1,duration:0.45,ease:"back.out(2.4)"}},{M(tin):.2f});')
|
| 61 |
+
tw.append(f'tl.to("#{cid}",{{opacity:0,scale:0.9,duration:0.35,ease:"power2.in"}},{M(tout):.2f});')
|
| 62 |
+
|
| 63 |
+
def sponsor(cid, name, tag, accent, tin, tout):
|
| 64 |
+
center.append(cid)
|
| 65 |
+
body.append(f'''<div class="sponsor glass" id="{cid}">
|
| 66 |
+
<div class="sp_name" style="color:{accent}">{name}</div>
|
| 67 |
+
<div class="sp_tag">{tag}</div></div>''')
|
| 68 |
+
tw.append(f'tl.set("#{cid}",{{xPercent:-50,yPercent:-50}},0);')
|
| 69 |
+
tw.append(f'tl.fromTo("#{cid}",{{opacity:0,y:40,scale:0.92}},{{opacity:1,y:0,scale:1,duration:0.5,ease:"back.out(1.6)"}},{M(tin):.2f});')
|
| 70 |
+
tw.append(f'tl.to("#{cid}",{{opacity:0,y:-26,duration:0.4,ease:"power2.in"}},{M(tout):.2f});')
|
| 71 |
+
|
| 72 |
+
# ---------- COLD OPEN counter ----------
|
| 73 |
+
def counter(cid, label, tin, tbefore, tafter, tbadge, tout):
|
| 74 |
+
center.append(cid)
|
| 75 |
+
body.append(f'''<div class="hook-card glass" id="{cid}">
|
| 76 |
+
<div class="hook-label">{label}</div>
|
| 77 |
+
<div class="row" id="{cid}_no"><span class="tag" style="color:#F87171">WITHOUT MTP</span><span><span class="num" style="color:#F87171">62</span><span class="unit">tok/s</span></span></div>
|
| 78 |
+
<div class="row" id="{cid}_yes"><span class="tag" style="color:#A3E635">WITH MTP</span><span><span class="num" style="color:#A3E635">78</span><span class="unit">tok/s</span></span></div>
|
| 79 |
+
<div class="badge" id="{cid}_b">+25% FASTER</div></div>''')
|
| 80 |
+
tw.append(f'tl.set("#{cid}",{{xPercent:-50,yPercent:-50}},0);')
|
| 81 |
+
tw.append(f'tl.fromTo("#{cid}",{{opacity:0,scale:0.9,y:30}},{{opacity:1,scale:1,y:0,duration:0.5,ease:"back.out(1.6)"}},{M(tin):.2f});')
|
| 82 |
+
tw.append(f'tl.fromTo("#{cid}_no",{{opacity:0,x:-40}},{{opacity:1,x:0,duration:0.4,ease:"power3.out"}},{M(tbefore):.2f});')
|
| 83 |
+
tw.append(f'tl.fromTo("#{cid}_yes",{{opacity:0,x:-40}},{{opacity:1,x:0,duration:0.4,ease:"power3.out"}},{M(tafter):.2f});')
|
| 84 |
+
tw.append(f'tl.fromTo("#{cid}_b",{{opacity:0,scale:0.4,rotation:-30}},{{opacity:1,scale:1,rotation:-6,duration:0.5,ease:"back.out(3)"}},{M(tbadge):.2f});')
|
| 85 |
+
tw.append(f'tl.to("#{cid}",{{opacity:0,scale:0.96,y:-24,duration:0.4,ease:"power2.in"}},{M(tout):.2f});')
|
| 86 |
+
|
| 87 |
+
# === COLD OPEN ===
|
| 88 |
+
counter("hook","TOKENS / SECOND",6.5,7.94,12.98,14.45,19.2)
|
| 89 |
+
add_sfx("riser.mp3",6.0,0.6); add_sfx("pop.mp3",7.94); add_sfx("impact.mp3",12.98,0.85); add_sfx("ding.mp3",14.45,0.7)
|
| 90 |
+
|
| 91 |
+
# === CH1 What is MTP ===
|
| 92 |
+
title_card("c_mtp","","WHAT IS MTP?","MULTI-TOKEN PREDICTION","#22D3EE",24.2,28.8); add_sfx("whoosh.mp3",24.2)
|
| 93 |
+
|
| 94 |
+
# === CH2 The Slow Way (lower-third style as title) ===
|
| 95 |
+
title_card("c_slow","THE SLOW WAY","STANDARD AUTOREGRESSION","3 PASSES · FULL 27B COST EACH","#F87171",79.8,85.5); add_sfx("whoosh.mp3",79.8)
|
| 96 |
+
|
| 97 |
+
# === CH3 The Fast Way ===
|
| 98 |
+
title_card("c_fast","THE FAST WAY","MTP: DRAFT + VERIFY","ONE PASS · MULTIPLE TOKENS","#A3E635",102.5,108.5); add_sfx("whoosh.mp3",102.5)
|
| 99 |
+
|
| 100 |
+
# === CH4 The Numbers (stat card with 3 rows) ===
|
| 101 |
+
center.append("c_num")
|
| 102 |
+
body.append('''<div class="title-card glass" id="c_num" style="gap:14px">
|
| 103 |
+
<div class="kicker">THE NUMBERS</div>
|
| 104 |
+
<div class="numrow" id="c_num_1"><b style="color:#A3E635">~2× FASTER</b><span>2.5 tokens / verified pass</span></div>
|
| 105 |
+
<div class="numrow" id="c_num_2"><b style="color:#22D3EE">75–80% ACCEPTED</b><span>on Qwen3</span></div>
|
| 106 |
+
<div class="numrow" id="c_num_3"><b style="color:#FFFFFF">+1 GB VRAM</b><span>the only cost</span></div>
|
| 107 |
+
</div>''')
|
| 108 |
+
tw.append('tl.set("#c_num",{xPercent:-50,yPercent:-50},0);')
|
| 109 |
+
tw.append(f'tl.fromTo("#c_num",{{opacity:0,scale:0.9}},{{opacity:1,scale:1,duration:0.5,ease:"power3.out"}},{M(148.5):.2f});')
|
| 110 |
+
tw.append(f'tl.fromTo("#c_num_1",{{opacity:0,x:-30}},{{opacity:1,x:0,duration:0.4,ease:"power3.out"}},{M(150.18):.2f});')
|
| 111 |
+
tw.append(f'tl.fromTo("#c_num_2",{{opacity:0,x:-30}},{{opacity:1,x:0,duration:0.4,ease:"power3.out"}},{M(155.0):.2f});')
|
| 112 |
+
tw.append(f'tl.fromTo("#c_num_3",{{opacity:0,x:-30}},{{opacity:1,x:0,duration:0.4,ease:"power3.out"}},{M(160.1):.2f});')
|
| 113 |
+
tw.append(f'tl.to("#c_num",{{opacity:0,scale:0.96,y:-20,duration:0.4,ease:"power2.in"}},{M(167):.2f});')
|
| 114 |
+
add_sfx("whoosh.mp3",148.5); add_sfx("ding.mp3",150.18,0.6); add_sfx("ding.mp3",155.0,0.6); add_sfx("ding.mp3",160.1,0.6)
|
| 115 |
+
|
| 116 |
+
# === CH5 Install + beta chip ===
|
| 117 |
+
title_card("c_install","STEP 1","INSTALL LM STUDIO","DOWNLOAD FOR WINDOWS","#22D3EE",177.22,181.8); add_sfx("whoosh.mp3",177.22)
|
| 118 |
+
chip("ch_beta","GET THE BETA → v0.4.14","#22D3EE",205.54,213); add_sfx("pop.mp3",205.54)
|
| 119 |
+
|
| 120 |
+
# === CH6 Pick a model ===
|
| 121 |
+
title_card("c_model","STEP 2","PICK AN MTP MODEL","SEARCH: MTP","#22D3EE",223.3,227.8); add_sfx("whoosh.mp3",223.3)
|
| 122 |
+
add_sfx("keyboard.mp3",225.78,0.5)
|
| 123 |
+
chip("ch_gpu","8 GB GPU → 4B MTP MODEL","#A3E635",246,252.5); add_sfx("pop.mp3",246)
|
| 124 |
+
|
| 125 |
+
# === CH7 Test 1 MTP OFF ===
|
| 126 |
+
corner_badge("b_off","✗ MTP OFF","#F87171",255,279.4)
|
| 127 |
+
add_sfx("keyboard.mp3",264.98,0.5)
|
| 128 |
+
stat_pop("p_18","≈18","tok/s · MTP OFF","#F87171",276,279.4); add_sfx("impact.mp3",276,0.85)
|
| 129 |
+
|
| 130 |
+
# === CH8 Test 2 MTP ON ===
|
| 131 |
+
corner_badge("b_on","✓ MTP ON · DRAFTS 3","#A3E635",279.6,332)
|
| 132 |
+
stat_pop("p_20","≈20","tok/s · +11%","#A3E635",317.8,326); add_sfx("impact.mp3",317.8,0.85); add_sfx("ding.mp3",318.2,0.6)
|
| 133 |
+
|
| 134 |
+
# === CH9 Real benchmark ===
|
| 135 |
+
counter("c_real","THE REAL TEST · 500-WORD ESSAY",336,338.8,345.16,352.52,354.2)
|
| 136 |
+
add_sfx("whoosh.mp3",336); add_sfx("pop.mp3",338.8); add_sfx("impact.mp3",345.16,0.9); add_sfx("ding.mp3",352.52,0.7)
|
| 137 |
+
|
| 138 |
+
# === CH10 Outro ===
|
| 139 |
+
sponsor("sp_run","RunPod","GPUs I USE · LINK IN DESCRIPTION","#22D3EE",369,377); add_sfx("whoosh.mp3",369)
|
| 140 |
+
sponsor("sp_host","Hostinger","HOSTING & VPS · LINK IN DESCRIPTION","#A3E635",377.5,385.5); add_sfx("whoosh.mp3",377.5)
|
| 141 |
+
center.append("subbtn")
|
| 142 |
+
body.append('<div class="subbtn" id="subbtn">▶ SUBSCRIBE</div>')
|
| 143 |
+
tw.append('tl.set("#subbtn",{xPercent:-50,yPercent:-50},0);')
|
| 144 |
+
tw.append(f'tl.fromTo("#subbtn",{{opacity:0,scale:0.5}},{{opacity:1,scale:1,duration:0.5,ease:"back.out(2.4)"}},{M(387):.2f});')
|
| 145 |
+
tw.append(f'tl.to("#subbtn",{{scale:1.08,duration:0.4,yoyo:true,repeat:3,ease:"sine.inOut"}},{M(388):.2f});')
|
| 146 |
+
add_sfx("ding.mp3",387,0.8)
|
| 147 |
+
|
| 148 |
+
# === intro whoosh ===
|
| 149 |
+
add_sfx("whoosh.mp3",0.1,0.5)
|
| 150 |
+
|
| 151 |
+
# ---------- ZOOM keyframes (src time, scale, dur, ease) ----------
|
| 152 |
+
# Baseline stays >=1.12 through browser sections (diagram + outro web) to crop the
|
| 153 |
+
# address bar / tab bar / taskbar. Punches to ~1.4 on the result readouts.
|
| 154 |
+
zk = [(6.2,1.28,2.0,"power2.inOut"),(19.4,1.13,1.6,"power2.inOut"),
|
| 155 |
+
(60,1.15,6,"sine.inOut"),(95,1.12,6,"sine.inOut"),(150,1.13,4,"sine.inOut"),
|
| 156 |
+
(177,1.12,1.5,"power2.inOut"),(255,1.11,1.5,"sine.inOut"),
|
| 157 |
+
(275.4,1.40,1.2,"power2.inOut"),(279.6,1.12,1.0,"power2.inOut"),
|
| 158 |
+
(287,1.18,1.5,"sine.inOut"),(317.2,1.40,1.0,"power2.inOut"),
|
| 159 |
+
(326,1.12,1.2,"power2.inOut"),(336,1.14,1.0,"power2.inOut"),(356,1.12,1.5,"power2.inOut")]
|
| 160 |
+
zoom_tw = ['tl.set(".zoom-wrap",{scale:1.0,transformOrigin:"50% 50%"},0);']
|
| 161 |
+
for t,s,d,e in zk:
|
| 162 |
+
zoom_tw.append(f'tl.to(".zoom-wrap",{{scale:{s},duration:{d},ease:"{e}"}},{M(t):.2f});')
|
| 163 |
+
|
| 164 |
+
# ---------- SFX elements ----------
|
| 165 |
+
sfx_els = "\n".join(
|
| 166 |
+
f' <audio id="sfx{ti}" src="../assets/sfx/{f}" data-start="{t:.2f}" data-track-index="{ti}" data-volume="{v}"></audio>'
|
| 167 |
+
for (f,t,v,ti) in sfx)
|
| 168 |
+
|
| 169 |
+
set_center = "\n ".join(tw[i] for i in range(0)) # centering already inline per element
|
| 170 |
+
|
| 171 |
+
HTML = f"""<!doctype html><html lang="en"><head><meta charset="utf-8"/>
|
| 172 |
+
<style>
|
| 173 |
+
*{{margin:0;padding:0;box-sizing:border-box;}}
|
| 174 |
+
#root{{position:relative;width:1920px;height:1080px;overflow:hidden;background:#0B0F14;font-family:"Inter",sans-serif;}}
|
| 175 |
+
.zoom-wrap{{position:absolute;inset:0;z-index:0;transform-origin:50% 45%;}}
|
| 176 |
+
.zoom-wrap video{{width:1920px;height:1080px;object-fit:cover;display:block;}}
|
| 177 |
+
.cap{{position:absolute;left:50%;bottom:90px;z-index:40;opacity:0;font-weight:900;font-size:56px;letter-spacing:1px;color:#fff;white-space:nowrap;text-shadow:0 4px 18px rgba(0,0,0,.85),0 2px 4px rgba(0,0,0,.9);}}
|
| 178 |
+
.glass{{background:rgba(255,255,255,0.07);backdrop-filter:blur(22px) saturate(130%);-webkit-backdrop-filter:blur(22px) saturate(130%);border:1px solid rgba(255,255,255,0.18);border-radius:26px;box-shadow:0 24px 60px rgba(0,0,0,.45);}}
|
| 179 |
+
.title-card{{position:absolute;left:50%;top:50%;z-index:25;opacity:0;padding:54px 80px;text-align:center;display:flex;flex-direction:column;align-items:center;gap:16px;}}
|
| 180 |
+
.kicker{{font-weight:700;font-size:30px;letter-spacing:8px;color:#9AA7B4;}}
|
| 181 |
+
.bigt{{font-weight:800;font-size:104px;color:#fff;letter-spacing:1px;line-height:1;}}
|
| 182 |
+
.uline{{height:8px;width:0;border-radius:6px;}}
|
| 183 |
+
.subt{{font-weight:700;font-size:40px;letter-spacing:3px;opacity:0;}}
|
| 184 |
+
.numrow{{display:flex;align-items:baseline;gap:22px;opacity:0;}}
|
| 185 |
+
.numrow b{{font-weight:800;font-size:56px;}}
|
| 186 |
+
.numrow span{{font-weight:600;font-size:30px;color:#9AA7B4;}}
|
| 187 |
+
.chip{{position:absolute;left:50%;top:70px;z-index:30;opacity:0;padding:18px 34px;font-weight:800;font-size:38px;color:#fff;white-space:nowrap;}}
|
| 188 |
+
.cbadge{{position:absolute;right:84px;top:48px;z-index:30;opacity:0;padding:13px 24px;border:2px solid;border-radius:16px;font-weight:800;font-size:32px;background:rgba(11,15,20,0.62);backdrop-filter:blur(10px);}}
|
| 189 |
+
.statpop{{position:absolute;left:50%;top:50%;z-index:28;opacity:0;padding:40px 64px;display:flex;flex-direction:column;align-items:center;gap:6px;}}
|
| 190 |
+
.sp_big{{font-weight:800;font-size:150px;line-height:.9;font-variant-numeric:tabular-nums;}}
|
| 191 |
+
.sp_small{{font-weight:700;font-size:34px;color:#9AA7B4;letter-spacing:2px;}}
|
| 192 |
+
.sponsor{{position:absolute;left:50%;top:50%;z-index:26;opacity:0;padding:48px 72px;text-align:center;display:flex;flex-direction:column;gap:12px;}}
|
| 193 |
+
.sp_name{{font-weight:800;font-size:88px;letter-spacing:1px;}}
|
| 194 |
+
.sp_tag{{font-weight:700;font-size:32px;color:#9AA7B4;letter-spacing:2px;}}
|
| 195 |
+
.subbtn{{position:absolute;left:50%;top:50%;z-index:30;opacity:0;padding:30px 60px;background:#F23A3A;color:#fff;font-weight:800;font-size:64px;border-radius:22px;box-shadow:0 16px 50px rgba(242,58,58,.5);letter-spacing:1px;}}
|
| 196 |
+
.hook-card{{position:absolute;left:50%;top:50%;z-index:20;opacity:0;width:760px;padding:46px 56px;display:flex;flex-direction:column;gap:20px;}}
|
| 197 |
+
.hook-label{{font-weight:700;font-size:24px;letter-spacing:5px;color:#9AA7B4;text-transform:uppercase;}}
|
| 198 |
+
.row{{display:flex;align-items:baseline;justify-content:space-between;opacity:0;}}
|
| 199 |
+
.row .tag{{font-weight:700;font-size:32px;letter-spacing:2px;}}
|
| 200 |
+
.row .num{{font-weight:800;font-size:108px;line-height:.9;font-variant-numeric:tabular-nums;}}
|
| 201 |
+
.row .unit{{font-weight:700;font-size:28px;color:#9AA7B4;margin-left:10px;}}
|
| 202 |
+
.badge{{position:absolute;right:-30px;top:-30px;opacity:0;font-weight:800;font-size:32px;color:#0B0F14;background:#A3E635;padding:14px 24px;border-radius:18px;box-shadow:0 12px 30px rgba(163,230,53,.45);}}
|
| 203 |
+
#c_real{{background:rgba(11,15,20,0.66);border-color:rgba(255,255,255,0.22);}}
|
| 204 |
+
#c_real .hook-label{{color:#C7D2DD;}}
|
| 205 |
+
</style></head><body>
|
| 206 |
+
<div id="root" data-composition-id="root" data-width="1920" data-height="1080" data-start="0" data-duration="{DUR}">
|
| 207 |
+
<div class="zoom-wrap"><video id="bg" src="base_full.mp4" data-start="0" data-duration="{DUR}" data-track-index="0" muted playsinline></video></div>
|
| 208 |
+
<audio id="voice" src="base_full.mp4" data-start="0" data-duration="{DUR}" data-track-index="1" data-volume="1"></audio>
|
| 209 |
+
{sfx_els}
|
| 210 |
+
{chr(10).join(body)}
|
| 211 |
+
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>
|
| 212 |
+
<script>
|
| 213 |
+
window.__timelines = window.__timelines || {{}};
|
| 214 |
+
const tl = gsap.timeline({{ paused: true }});
|
| 215 |
+
{chr(10)+' '.join(zoom_tw)}
|
| 216 |
+
{chr(10)+' '.join(tw)}
|
| 217 |
+
window.__timelines["root"] = tl;
|
| 218 |
+
</script>
|
| 219 |
+
</div></body></html>"""
|
| 220 |
+
|
| 221 |
+
(ROOT/"hf"/"index.html").write_text(HTML, encoding="utf-8")
|
| 222 |
+
print(f"wrote full index.html duration={DUR}s sfx={len(sfx)} elements~{len(body)}")
|