feat(shorts): cinematic v2 visual design + synced karaoke captions
- Fix critical bug: scenes were stacked without <Sequence> — every segment rendered simultaneously, last scene covering the whole video (root cause of 'too simple/ugly' visual) - Scene variety: kinetic word-by-word title (hook), karaoke caption card, big-number stat scene (auto-detected: $12B, 65%, 3.2M), CTA center - Karaoke captions synced to TTS: edge-tts SentenceBoundary timing via new scripts/tts-words.py (mp3 + sentences.json per segment); active sentence bright, others dimmed - Rich background per scene: 4 color palettes (indigo/pink, cyan/teal, purple/pink, orange/red), drifting orbs, dot grid, ken-burns motion - Real fonts: @fontsource Inter 700 + Montserrat 800 (display/captions) - Glass card captions, progress bar per-scene, safe zone 900x1400 - Word-boundary truncation for stat labels (no mid-word cuts) - Perf kept: no blur/SVG-noise (software renderer); ~2 min per 32s short
This commit is contained in:
1 parent
c0849dde62
commit
1db00c4182
6 files changed
+479
-158
No files matched your search
@@ -0,0 +1,33 @@
|
||||
#!/usr/bin/env python3
|
||||
"""TTS with per-sentence timing (karaoke captions).
|
||||
|
||||
Usage: tts-words.py <text> <voice> <out.mp3> <out.sentences.json>
|
||||
Writes mp3 + JSON [{w, start, end}] (seconds, relative to segment start).
|
||||
Uses edge-tts SentenceBoundary stream events.
|
||||
"""
|
||||
import asyncio
|
||||
import json
|
||||
import sys
|
||||
|
||||
import edge_tts
|
||||
|
||||
|
||||
async def main(text: str, voice: str, mp3_path: str, sentences_path: str) -> None:
|
||||
comm = edge_tts.Communicate(text, voice)
|
||||
sentences = []
|
||||
with open(mp3_path, "wb") as f:
|
||||
async for chunk in comm.stream():
|
||||
if chunk.get("type") == "audio":
|
||||
f.write(chunk.get("data", b""))
|
||||
elif chunk.get("type") == "SentenceBoundary":
|
||||
start = chunk.get("offset", 0) / 1e7
|
||||
dur = chunk.get("duration", 0) / 1e7
|
||||
sentences.append(
|
||||
{"w": chunk.get("text", ""), "start": round(start, 3), "end": round(start + dur, 3)}
|
||||
)
|
||||
with open(sentences_path, "w") as f:
|
||||
json.dump(sentences, f)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main(sys.argv[1], sys.argv[2], sys.argv[3], sys.argv[4]))
|
||||
Reference in new issue
Block a user