From 13ccacf4fdb15d97944d8124aa0b249cc8fcabf2 Mon Sep 17 00:00:00 2001 From: teahubxyz Date: Tue, 27 Oct 2026 20:48:04 +0000 Subject: [PATCH] zeta-ball: mux narration per scene and concatenate Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01FAqjv3n4yABxvJPJHFRhVA --- src/zeta-ball/assemble.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 src/zeta-ball/assemble.py diff --git a/src/zeta-ball/assemble.py b/src/zeta-ball/assemble.py new file mode 100644 index 0000000..1c326ea --- /dev/null +++ b/src/zeta-ball/assemble.py @@ -0,0 +1,20 @@ +import json, subprocess +FF, FP = '/opt/homebrew/bin/ffmpeg', '/opt/homebrew/bin/ffprobe' +D = json.load(open('durations.json')); GAP = 0.6 +pcm = open('zb.pcm', 'rb').read() +V = 'media/videos/zb/720p30' +off, parts = 0.0, [] +for i, d in enumerate(D): + a = int(round(off * 24000)) * 2; n = int(round(d * 24000)) * 2 + open(f'seg_{i}.pcm', 'wb').write(pcm[a:a + n]) + off += d + GAP + vd = float(subprocess.run([FP, '-v', 'error', '-show_entries', 'format=duration', '-of', 'csv=p=0', f'{V}/S{i}.mp4'], capture_output=True, text=True).stdout) + print(f'S{i}: video {vd:.2f}s narration {d:.2f}s slack {vd - d:.2f}s') + out = f'part_{i}.mp4' + subprocess.run([FF, '-v', 'error', '-y', '-i', f'{V}/S{i}.mp4', '-f', 's16le', '-ar', '24000', '-ac', '1', '-i', f'seg_{i}.pcm', + '-filter_complex', '[1:a]apad[a]', '-map', '0:v', '-map', '[a]', '-c:v', 'copy', '-c:a', 'aac', '-b:a', '128k', + '-ar', '48000', '-shortest', out], check=True) + parts.append(out) +open('list.txt', 'w').write(''.join(f"file '{p}'\n" for p in parts)) +subprocess.run([FF, '-v', 'error', '-y', '-f', 'concat', '-safe', '0', '-i', 'list.txt', '-c:v', 'libx264', '-crf', '20', '-pix_fmt', 'yuv420p', '-c:a', 'aac', '-b:a', '128k', 'zeta-ball.mp4'], check=True) +print(subprocess.run([FP, '-v', 'error', '-show_entries', 'format=duration,size', '-of', 'default=noprint_wrappers=1', 'zeta-ball.mp4'], capture_output=True, text=True).stdout)