Prompt48 commited on
Commit
40f83d4
·
verified ·
1 Parent(s): 356e822

Upload edit\build_slice.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. edit//build_slice.py +220 -0
edit//build_slice.py ADDED
@@ -0,0 +1,220 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Build output-timeline mapping and generate the first ~50s slice composition.
3
+ Source times -> output times using the segment list from build_cut.py.
4
+ """
5
+ import json, re, sys
6
+ from pathlib import Path
7
+
8
+ TRANSCRIPT = Path(r"D:\PromptEngineer48\In-Progress\P11-Editor\edit\transcripts\Mem0_1.json")
9
+ HF_DIR = Path(r"D:\PromptEngineer48\In-Progress\P11-Editor\edit\hf")
10
+ SKILL = Path(r"C:\Users\palas\.claude\skills\screencast-hype")
11
+
12
+ sys.path.insert(0, str(SKILL / "scripts"))
13
+ from captions_html import build_captions
14
+
15
+ # ---- same segment list as build_cut.py ----
16
+ THRESHOLD = 0.30
17
+ PAD = 0.08
18
+ FILLERS = {"uh", "um"}
19
+ VIDEO_DUR = 805.5
20
+
21
+ data = json.load(open(TRANSCRIPT, encoding="utf-8"))
22
+ words = [w for w in data["words"] if w.get("type") == "word"]
23
+ clean = [w for w in words if w["text"].strip().lower().rstrip(",.") not in FILLERS]
24
+
25
+ segs = []
26
+ s = e = None
27
+ for w in clean:
28
+ if s is None:
29
+ s, e = w["start"], w["end"]
30
+ elif w["start"] - e <= THRESHOLD:
31
+ e = w["end"]
32
+ else:
33
+ segs.append((max(0, s - PAD), e + PAD))
34
+ s, e = w["start"], w["end"]
35
+ if s is not None:
36
+ segs.append((max(0, s - PAD), e + PAD))
37
+
38
+ clamped = []
39
+ for a, b in segs:
40
+ a = round(max(0.0, a), 4)
41
+ b = round(min(VIDEO_DUR, b), 4)
42
+ if clamped and a < clamped[-1][1]:
43
+ a = clamped[-1][1]
44
+ if b > a:
45
+ clamped.append((a, b))
46
+ segs = clamped
47
+
48
+ def src_to_out(src_t):
49
+ out_offset = 0.0
50
+ for (a, b) in segs:
51
+ if src_t <= a:
52
+ return out_offset
53
+ if src_t <= b:
54
+ return out_offset + (src_t - a)
55
+ out_offset += (b - a)
56
+ return out_offset
57
+
58
+ # ---- slice window ----
59
+ SLICE_END_OUT = 50.0
60
+ out_acc = 0.0
61
+ src_end = segs[-1][1]
62
+ for (a, b) in segs:
63
+ seg_dur = b - a
64
+ if out_acc + seg_dur >= SLICE_END_OUT:
65
+ src_end = a + (SLICE_END_OUT - out_acc)
66
+ break
67
+ out_acc += seg_dur
68
+
69
+ print(f"Slice: output 0..{SLICE_END_OUT}s | source 0..{src_end:.1f}s")
70
+
71
+ # ---- timing constants ----
72
+ T_TITLE_IN = 1.0
73
+ T_TITLE_OUT = 9.0
74
+ T_CHIP_IN = src_to_out(15.0)
75
+ T_CHIP_OUT = T_CHIP_IN + 8.0
76
+ TOTAL_DUR = SLICE_END_OUT
77
+
78
+ # ---- captions (suppress during title card window) ----
79
+ raw_divs, raw_tweens = build_captions(words, start=0.0, end=src_end, per=2, map_time=src_to_out)
80
+
81
+ def filter_tweens(tweens_str):
82
+ lines = tweens_str.split("\n")
83
+ out, skip = [], False
84
+ for line in lines:
85
+ m = re.search(r',(\d+\.\d+)\);$', line)
86
+ t_val = float(m.group(1)) if m else None
87
+ if t_val is not None and T_TITLE_IN <= t_val <= T_TITLE_OUT:
88
+ skip = True
89
+ continue
90
+ if skip and "opacity:0" in line:
91
+ skip = False
92
+ continue
93
+ skip = False
94
+ out.append(line)
95
+ return "\n".join(out)
96
+
97
+ cap_divs = raw_divs
98
+
99
+ filtered = filter_tweens(raw_tweens)
100
+
101
+ # Hard-zero ALL caps at every fromTo entry point so previous cap is never
102
+ # still visible when the next one pops in. Inject tl.set(".cap",{opacity:0},T)
103
+ # just before every fromTo. GSAP processes set() before fromTo at same time,
104
+ # so the specific cap's fromTo then overrides just that element.
105
+ def inject_set_resets(tweens_str):
106
+ lines = tweens_str.split("\n ")
107
+ result = []
108
+ for line in lines:
109
+ if line.strip().startswith("tl.fromTo("):
110
+ m = re.search(r',(\d+\.\d+)\);$', line)
111
+ if m:
112
+ t = float(m.group(1))
113
+ # inject instant reset for ALL caps just before this entry
114
+ result.append(f'tl.set(".cap",{{opacity:0}},{max(t-0.001,0):.3f});')
115
+ result.append(line)
116
+ return "\n ".join(result)
117
+
118
+ cap_tweens = inject_set_resets(filtered)
119
+
120
+ # ---- generate HTML ----
121
+ html = f"""<!doctype html><html lang="en"><head><meta charset="utf-8"/>
122
+ <style>@font-face{{font-family:"Inter";font-weight:100 900;font-style:normal;src:url("capture/assets/fonts/Inter.woff2") format("woff2");}}</style>
123
+ <style>
124
+ *{{margin:0;padding:0;box-sizing:border-box;}}
125
+ #root{{position:relative;width:1920px;height:1080px;overflow:hidden;background:#0B0F14;font-family:"Inter",sans-serif;}}
126
+ .zoom-wrap{{position:absolute;inset:0;z-index:0;transform-origin:50% 45%;}}
127
+ .zoom-wrap video{{width:1920px;height:1080px;object-fit:cover;display:block;}}
128
+ .glass{{
129
+ background:rgba(255,255,255,0.07);
130
+ backdrop-filter:blur(22px) saturate(130%);
131
+ -webkit-backdrop-filter:blur(22px) saturate(130%);
132
+ border:1px solid rgba(255,255,255,0.18);
133
+ border-radius:26px;
134
+ box-shadow:0 24px 60px rgba(0,0,0,0.45);
135
+ }}
136
+ #title-card{{position:absolute;left:50%;top:42%;z-index:20;opacity:0;padding:52px 72px 56px;text-align:center;min-width:860px;}}
137
+ #title-card .eyebrow{{font-size:22px;font-weight:700;letter-spacing:5px;color:#22D3EE;text-transform:uppercase;margin-bottom:18px;}}
138
+ #title-card .headline{{font-size:76px;font-weight:900;color:#fff;line-height:1.05;}}
139
+ #title-card .headline span{{color:#22D3EE;}}
140
+ #underline{{display:block;height:4px;background:#22D3EE;border-radius:2px;width:0;margin:20px auto 0;box-shadow:0 0 16px #22D3EE99;}}
141
+ #subtitle{{font-size:28px;font-weight:600;color:#9AA7B4;margin-top:14px;opacity:0;}}
142
+ #chip1{{position:absolute;left:50%;top:72%;z-index:25;opacity:0;padding:14px 32px;display:flex;align-items:center;gap:12px;}}
143
+ #chip1 .dot{{width:10px;height:10px;border-radius:50%;background:#22D3EE;box-shadow:0 0 10px #22D3EE;}}
144
+ #chip1 .label{{font-size:26px;font-weight:800;color:#fff;letter-spacing:1px;}}
145
+ #chip1 .val{{font-size:26px;font-weight:700;color:#22D3EE;}}
146
+ .cap{{position:absolute;left:50%;bottom:52px;z-index:1;opacity:0;
147
+ font-weight:900;font-size:54px;letter-spacing:1px;color:#fff;white-space:nowrap;
148
+ background:rgba(0,0,0,0.55);padding:8px 28px;border-radius:10px;
149
+ text-shadow:0 2px 8px rgba(0,0,0,.9);}}
150
+ </style></head><body>
151
+ <div id="root" data-composition-id="root" data-width="1920" data-height="1080" data-start="0" data-duration="{TOTAL_DUR}">
152
+ <div class="zoom-wrap" id="zoom">
153
+ <video id="bg" src="base_cut.mp4" data-start="0" data-duration="{TOTAL_DUR}" data-track-index="0" muted playsinline></video>
154
+ </div>
155
+ <audio id="voice" src="base_cut.mp4" data-start="0" data-duration="{TOTAL_DUR}" data-track-index="1" data-volume="1"></audio>
156
+ <audio id="sfx_riser" src="../assets/sfx/riser.mp3" data-start="0.3" data-track-index="2" data-volume="0.7"></audio>
157
+ <audio id="sfx_impact" src="../assets/sfx/impact.mp3" data-start="{T_TITLE_IN:.1f}" data-track-index="3" data-volume="0.65"></audio>
158
+ <audio id="sfx_whoosh" src="../assets/sfx/whoosh.mp3" data-start="{T_TITLE_OUT - 0.1:.1f}" data-track-index="4" data-volume="0.55"></audio>
159
+ <audio id="sfx_pop" src="../assets/sfx/pop.mp3" data-start="{T_CHIP_IN:.1f}" data-track-index="5" data-volume="0.6"></audio>
160
+
161
+ <div id="title-card" class="glass">
162
+ <div class="eyebrow">Mem0 &mdash; Universal Memory Layer</div>
163
+ <div class="headline">Give Your <span>AI Agent</span><br>Persistent Memory</div>
164
+ <span id="underline"></span>
165
+ <div id="subtitle">56K+ GitHub Stars &bull; Free Tier Available</div>
166
+ </div>
167
+
168
+ <div id="chip1" class="glass">
169
+ <div class="dot"></div>
170
+ <div class="label">WHY AGENTS FORGET&nbsp;&nbsp;</div>
171
+ <div class="val">The Problem</div>
172
+ </div>
173
+
174
+ <div id="cap-group" style="position:absolute;inset:0;z-index:30;pointer-events:none;">
175
+ {cap_divs}
176
+ </div>
177
+
178
+ <script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>
179
+ <script>
180
+ window.__timelines = window.__timelines || {{}};
181
+ const tl = gsap.timeline({{ paused: true }});
182
+
183
+ tl.set("#zoom", {{ scale: 1.06, transformOrigin: "50% 45%" }}, 0);
184
+ tl.to("#zoom", {{ scale: 1.10, duration: 30, ease: "sine.inOut" }}, 0);
185
+
186
+ // Hide all captions during title card via group — overrides individual cap tweens
187
+ tl.to("#cap-group", {{ opacity: 0, duration: 0.05 }}, {T_TITLE_IN:.2f});
188
+ tl.to("#cap-group", {{ opacity: 1, duration: 0.05 }}, {T_TITLE_OUT:.2f});
189
+
190
+ tl.set("#title-card", {{ xPercent: -50, yPercent: -50 }}, 0);
191
+ tl.fromTo("#title-card",
192
+ {{ opacity: 0, scale: 0.88, filter: "blur(14px)" }},
193
+ {{ opacity: 1, scale: 1, filter: "blur(0px)", duration: 0.6, ease: "power3.out" }},
194
+ {T_TITLE_IN:.1f});
195
+ tl.to("#underline", {{ width: 560, duration: 0.5, ease: "power2.out" }}, {T_TITLE_IN + 0.4:.1f});
196
+ tl.fromTo("#subtitle",
197
+ {{ opacity: 0, y: 18 }},
198
+ {{ opacity: 1, y: 0, duration: 0.45, ease: "power2.out" }},
199
+ {T_TITLE_IN + 0.7:.1f});
200
+ tl.to("#title-card", {{ opacity: 0, scale: 0.96, y: -24, duration: 0.4, ease: "power2.in" }}, {T_TITLE_OUT:.1f});
201
+
202
+ tl.set("#chip1", {{ xPercent: -50 }}, 0);
203
+ tl.fromTo("#chip1",
204
+ {{ opacity: 0, scale: 0.85, y: 20 }},
205
+ {{ opacity: 1, scale: 1, y: 0, duration: 0.45, ease: "back.out(1.7)" }},
206
+ {T_CHIP_IN:.1f});
207
+ tl.to("#chip1", {{ opacity: 0, duration: 0.3, ease: "power2.in" }}, {T_CHIP_OUT:.1f});
208
+
209
+ {cap_tweens}
210
+
211
+ window.__timelines["root"] = tl;
212
+ </script>
213
+ </div>
214
+ </body></html>
215
+ """
216
+
217
+ out = HF_DIR / "index.html"
218
+ out.write_text(html, encoding="utf-8")
219
+ print(f"Written: {out}")
220
+ print(f"Caption chunks: {cap_divs.count('<div class=')}")