Tighten the trailer to 1:30 and fix its two audio faults

Cut 3 of LIFETIME MEMBERS: the intro music no longer runs into Reg's first
line, Ambrose finishes "...still waiting for a result" (line ends are now
measured off each take instead of the QA transcript), every shot is
tighter, and the black title cards are set larger. Cut 2 stays on disk
beside it as the reference.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
wuwiandClaude Opus 5.5 committed 2026-10-05 10:56:50 +02:00
1 parent d388004eac
commit 5fba837ead
5 files changed
+65 -36

No files matched your search

+37 -28
View File
@@ -23,35 +23,38 @@ game = lambda n: os.path.join(ROOT, 'assets', 'audio', n)
# ov: [(card, t_in, t_out)] faded overlays · fi/fo: picture fade from/to black · gain: clip audio dB
# mute: drop the clip's own audio · dlg: a spoken line — its gain is MEASURED so every voice in the
# film sits at DLG LUFS (eq: lift a voice out of loud ambience first)
L3 = (0.7, 4.8)
L3 = (0.6, 4.6)
DLG = -20.0
QUIET = 2.3 # seconds into s_amb where "please" ends and the music is cut
CUT = 3 # cut 2 (1:45) is kept beside it as lifetime-members-cut2.mp4, the reference
# Line ends below are MEASURED off each take's speech-band envelope, not off the QA transcript
# (its timestamps drift): cut 2 lost the end of Ambrose's "...result" by trusting 6.6 s; it is 7.2.
EDL = [
('gates', 'gates_v1', 0.0, 7.0, dict(fi=1.2, gain=-2, afi=1.0)),
('court', 'court_v1', 0.0, 5.0, dict(gain=-4)),
('board', 'board_v1', 0.0, 5.5, dict(fo=0.5, gain=-3, afo=0.5)),
('trad', None, 0, 5.2, dict(ov=[('t_a', 0.3, 4.8), ('t_b', 2.3, 4.8)])),
('title', None, 0, 4.0, dict(ov=[('title', 0.2, 3.5)])),
('q1', None, 0, 2.3, dict(ov=[('q_start', 0.15, 2.0)])),
('reg', 'reg_v1.keep', 0.0, 7.3, dict(ov=[('lt_reg', *L3)], dlg=True)),
('tin', 'tin_v1', 0.0, 3.0, dict(gain=-6)),
('doris', 'doris_v1', 0.0, 8.0, dict(ov=[('lt_doris', *L3)], dlg=True)),
('cream', 'cream_v1', 0.0, 3.2, dict(gain=-6)),
('clive', 'clive_v1', 0.0, 6.3, dict(ov=[('lt_clive', *L3)], dlg=True)),
('bylaws', 'bylaws_v1', 0.0, 3.0, dict(gain=-6)),
('ambrose', 'ambrose_v1', 0.0, 7.0, dict(ov=[('lt_ambrose', *L3)], dlg=True)),
('archive', 'archive_v1', 0.0, 5.0, dict(fo=0.5, gain=-4, afo=0.5)),
('q2', None, 0, 2.3, dict(ov=[('q_chall', 0.15, 2.0)])),
('s_doris', 'stare_doris_v1', 0.6, 3.4, dict(dlg=True)),
('s_clive', 'stare_clive_v2', 0.8, 3.2, dict(gain=-6)),
('gates', 'gates_v1', 0.0, 5.6, dict(fi=1.0, gain=-2, afi=0.8)),
('court', 'court_v1', 0.0, 4.0, dict(gain=-4)),
('board', 'board_v1', 0.0, 4.6, dict(fo=0.5, gain=-3, afo=0.5)),
('trad', None, 0, 4.4, dict(ov=[('t_a', 0.25, 4.0), ('t_b', 1.8, 4.0)])),
('title', None, 0, 3.4, dict(ov=[('title', 0.15, 3.0)])),
('q1', None, 0, 1.9, dict(ov=[('q_start', 0.1, 1.7)])),
('reg', 'reg_v1.keep', 0.0, 7.1, dict(ov=[('lt_reg', *L3)], dlg=True)),
('tin', 'tin_v1', 0.0, 2.2, dict(gain=-6)),
('doris', 'doris_v1', 0.0, 7.6, dict(ov=[('lt_doris', *L3)], dlg=True)),
('cream', 'cream_v1', 0.4, 2.2, dict(gain=-6)),
('clive', 'clive_v1', 0.0, 6.1, dict(ov=[('lt_clive', *L3)], dlg=True)),
('bylaws', 'bylaws_v1', 0.0, 2.2, dict(gain=-6)),
('ambrose', 'ambrose_v1', 0.0, 7.9, dict(ov=[('lt_ambrose', *L3)], dlg=True, afo=0.5)),
('archive', 'archive_v1', 0.0, 3.8, dict(fo=0.5, gain=-4, afo=0.5)),
('q2', None, 0, 1.9, dict(ov=[('q_chall', 0.1, 1.7)])),
('s_doris', 'stare_doris_v1', 0.7, 2.7, dict(dlg=True)),
('s_clive', 'stare_clive_v2', 1.0, 2.8, dict(gain=-6)),
# "Quiet, please." — and the score obeys: it stops dead on the end of "please" (see MUSIC)
('s_amb', 'stare_ambrose_v1', 0.0, 3.2, dict(dlg=True, afo=0.9)),
('stands', 'stands_close_v2', 0.9, 3.5, dict(gain=-17)), # the heads creak, barely
('play', 'ambrose_night_v2', 0.0, 2.0, dict(dlg=True)),
('splat', None, 0, 1.6, dict()),
('logo', None, 0, 4.6, dict(ov=[('logo', 0.25, 4.6)], fo=0.6)),
('stinger', 'reg_dawn_v1', 0.0, 5.4, dict(fi=0.4, afi=0.35, dlg=(0.0, 2.6), lift=2, eq=True)),
('end', None, 0, 4.2, dict(ov=[('end', 0.5, 4.2)], fo=1.0)),
('s_amb', 'stare_ambrose_v1', 0.0, 3.0, dict(dlg=True, afo=0.8)),
('stands', 'stands_close_v2', 0.9, 3.0, dict(gain=-17)), # the heads creak, barely
('play', 'ambrose_night_v2', 0.0, 1.9, dict(dlg=True)),
('splat', None, 0, 1.2, dict()),
('logo', None, 0, 3.8, dict(ov=[('logo', 0.2, 3.8)], fo=0.5)),
('stinger', 'reg_dawn_v1', 0.6, 3.6, dict(fi=0.4, afi=0.35, dlg=(1.0, 2.7), lift=2, eq=True)),
('end', None, 0, 3.6, dict(ov=[('end', 0.4, 3.6)], fo=0.9)),
]
# every segment is a whole number of frames, or the audio drifts a sub-frame per cut
@@ -63,9 +66,15 @@ for name, _, _, dur, _ in EDL:
TOTAL = t
# music/effects: (file, src_in, dur, at, gain dB, fade_in, fade_out)
# m_open turns a corner at 21.0 s (segment.py's join): that turn lands on the title card, and the
# whole cue has faded out under the question card, before Reg opens his mouth (cut 2 ran it 3 s into
# his line — Oliver: "the music from the intro is overlapping the voice")
OPEN_TURN = 21.0
o_in = max(0.0, OPEN_TURN - start['title'])
o_at = max(0.0, start['title'] - OPEN_TURN)
MUSIC = [
(aud('m_open.mp3'), 0.0, 30.5, start['gates'] + 1.5, -11, 2.0, 2.5),
(aud('cue_a.mp3'), 35.0, 38.6, start['reg'], -20, 1.5, 2.0),
(aud('m_open.mp3'), o_in, start['q1'] + 1.2 - o_at, o_at, -11, 2.0, 3.4), # gone mid-question
(aud('cue_a.mp3'), 35.0, start['q2'] - start['reg'], start['reg'], -20, 1.5, 2.0),
# the drone sits low under Doris, then swells through Clive's eyes so the shush has something to stop
(aud('cue_b.mp3'), 0.0, start['s_amb'] + QUIET - start['q2'], start['q2'], -17, 1.0, 0.01,
f"'if(lt(t,{start['s_clive'] - start['q2']:.2f}),1,min(2.4,1+(t-{start['s_clive'] - start['q2']:.2f})*0.6))':eval=frame"),
@@ -181,7 +190,7 @@ def mix(parts):
# a touch of contrast, a little less saturation, a soft vignette, and live 35mm-ish grain
f.append('[0:v]eq=contrast=1.04:saturation=0.9:gamma=0.98,vignette=angle=PI/5.5,'
'noise=c0s=5:c0f=t+u,format=yuv420p[v]')
out = os.path.join(T, 'lifetime-members.mp4')
out = os.path.join(T, f'lifetime-members-cut{CUT}.mp4')
cmd += ['-filter_complex', ';'.join(f), '-map', '[v]', '-map', '[a]',
# grain is the costliest thing to encode: tune for it and cap the rate (~14 Mb/s keeps it
# postable everywhere; uncapped crf 16 came out at 36 Mb/s, 480 MB for 2 minutes)