Files
splatter-tennis/tools/trailer/words.py
T
wuwiandClaude Opus 5.5 2de45510a3 Add a 36-second cutdown of the trailer
cut.py --short builds a social-length version from the same takes: one
line per witness cut inside their takes, then the challengers beat,
"Quiet, please", "Play", the splat and Reg's stinger. Each cut keeps its
own build folder and output file, so cut 2, cut 3 and the short all stay.
words.py gives local Whisper word timestamps for cutting inside lines.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 11:09:05 +02:00

20 lines
1.1 KiB
Python

#!/usr/bin/env python3
"""Word timestamps for takes, from a local Whisper — the cutdown cuts INSIDE lines, and the QA
listener's timestamps drift too much for that.
uv run --with faster-whisper python tools/trailer/words.py <take|file>... (take names in promo/trailer/clips)
"""
import os, subprocess, sys
import numpy as np
from faster_whisper import WhisperModel
ROOT = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
m = WhisperModel('small.en', compute_type='int8')
for name in sys.argv[1:]:
# decode with ffmpeg ourselves: faster-whisper's PyAV path breaks on newer av releases
src = name if os.path.exists(name) else os.path.join(ROOT, 'promo', 'trailer', 'clips', name + '.mp4')
raw = subprocess.run(['ffmpeg', '-v', 'error', '-i', src,
'-ac', '1', '-ar', '16000', '-f', 's16le', '-'], capture_output=True).stdout
segs, _ = m.transcribe(np.frombuffer(raw, np.int16).astype(np.float32) / 32768, word_timestamps=True)
print(name + ':', ' '.join(f'{w.word.strip()}[{w.start:.2f}-{w.end:.2f}]' for s in segs for w in s.words))