Marketingpraat, Snippet-productie (van aflevering naar social-clip)

Complete gids zodat iedereen in het team zelf een snippet kan maken: van kiezen welk moment je knipt, tot een 9:16-clip waarin de spreker netjes in beeld staat, met captions.

Werkverdeling nu (2026-07): de machine (Claude) levert meestal een schone 16:9-cut; de 9:16-reframe + captions doet het team zelf (bijv. in de Submagic-webapp). Deze gids geeft het volledige recept, inclusief de handmatige 9:16- en caption-route, zodat je niet afhankelijk bent van de webapp als je precies wilt sturen.

Curated (synthese). Dit bestand is met de hand samengesteld uit opgebouwde lessen; het wordt niet door scripts/build-second-brain.py gegenereerd. De crop-waardes zijn studio- en zelfs camerahoek-specifiek: gebruik ze als startpunt en meet altijd per segment.

Inhoudsopgave

  1. Wat je nodig hebt (omgeving)
  2. Waar alles staat + naamgeving
  3. Studio en wie is wie
  4. Stap 1: selecteren welk moment
  5. Stap 2: de 16:9-cut (schone volzin)
  6. Stap 3: 9:16-reframe met spreker-focus
  7. Stap 4: captions
  8. Stap 5: verificatie (verplicht)
  9. Bestaande clips repareren
  10. Kant-en-klare ffmpeg-baselines
  11. Anti-patronen
  12. Naslag: crop-waardes per aflevering

Wat je nodig hebt (omgeving)

  • ffmpeg (knippen, croppen, renderen). Voor caption-burn-in heb je een build mét libass nodig; de standaard brew-ffmpeg heeft dat niet. Installeer via de tap homebrew-ffmpeg/ffmpeg/ffmpeg.
  • python3 met numpy voor de audio-verificatie: gebruik /usr/bin/python3 (die heeft numpy op deze Mac, de brew/usr-local python niet).
  • TurboScribe-transcript van de aflevering (turn-niveau timestamps).
  • Submagic-webapp (team-standaard voor 9:16-reframe + captions), of het handmatige recept hieronder.
  • Optioneel voor DIY-captions: whisper-cpp (brew install whisper-cpp) + model ~/Marketing/Steengoed/_work/whisper-models/ggml-large-v3-turbo.bin (Nederlands, snel, accuraat).

Waar alles staat + naamgeving

Aflevering-mappen: ~/Marketing/Marketingpraat/Afleveringen/<nummer>_<naam>/. Daarin:

  • <nr>#_Marketingpraat_<naam>.mp4 (1920×1080, 25 fps, bronvideo)
  • <nr># Marketingpraat <naam> (1).txt (TurboScribe-transcript)
  • snippets/ (alle geknipte clips)

Let op: de master staat soms niet in de aflevering-map maar in ~/Downloads/ (bijv. afl-005, 4,5 GB). Check dat eerst.

Filename-conventie: snippet-<Hm>m<Hs>-<Tm>m<Ts>-<slug>-<format>.mp4

  • tijden = de conceptuele (niet-gepadde) start/eind zoals jij ze benoemt, bijv. 4m18-4m53
  • <slug> = kebab-case topic, bijv. haakbeest, roi-connectie
  • <format> = 16x9-nosubs.mp4 (raw cut / source) of 9x16-speakerfocus.mp4 (klaar voor social)
  • caption ernaast: ...-caption.txt
  • compound (meerdere stukken aan elkaar): video’s krijgen -a/-b/-c, één gedeelde ...-COMPOUND-caption.txt

Selectie-Doc per aflevering: één Google Doc in de bestaande 11_SNIPPETS-map (géén losse “Snippets”-map maken). Labels in HOOFDLETTERS (TIMESTAMPS: / TRANSCRIPT: / CAPTION:), want de Drive-connector zet geen opmaak.


Studio en wie is wie

Vaste zitposities (kleding wisselt per aflevering, dus identificeer op gezicht/haarkleur + positie, niet op shirtkleur):

  • Tonny: links in de wide shot, lichter/blond haar, kijkt naar rechts in close-up.
  • Martijn: rechts van Tonny, donkerder haar, kijkt naar links in close-up.
  • Gast(en): rechts van Martijn.

Speaker-ID-cue: Martijn opent de show met de intro-monoloog over de gast; Tonny niet. TurboScribe-speaker-nummers zijn per transcript willekeurig, dus verifieer wie wie is vóór je crops bepaalt (in een eerdere ronde stonden Tonny/Martijn omgedraaid, dat kostte twee re-renders). De editor knipt naar de spreker bij elke beurt, dus on-camera = meestal de spreker (op reactie-shots na).


Stap 1: selecteren welk moment

Volledige methode: de snippet-selectie skill / de rubric. De kern:

De enige harde poort: de hook. In de eerste ~5 sec moet de clip minstens één van deze drie doen (anders geen kandidaat, hoe goed de inhoud ook is):

  1. Emotie oproepen (pijn, herkenning, trots): “Als ik mijn Instagram verwijder, leid ik 100% zeker een gelukkiger leven.”
  2. Spanning / intrige: “Vier jaar geleden zijn we gestopt. Achteraf onze slechtste beslissing.”
  3. Nieuwsgierigheid / open loop: “Hoeveel mensen leren online haken denk je?”

Kun je de start verschuiven zodat de hook vooraan valt? Doen (optie A). Anders overslaan.

Format-eisen (hard, geen kwaliteitslat): standalone begrijpelijk (geen “zoals ik net zei”) en duur 13-70 sec (sweet spot 25-45).

Kwaliteitssignalen: minstens één concreet element (cijfer, voorbeeld, anekdote, beeldspraak).

3 emmers (label voor distributie, geen filter):

  1. Strategie met software-relatie (breed inzetbaar: social + ads + website + sales).
  2. 100% social (leuk/herkenbaar verhaal, houdt mensen op het platform).
  3. Pure pitch / testimonial (direct over P&P/Huddle; zeldzaam, niet forceren).

Skip casual opwarming en outro, maar behoud harde cijfers/bio-stats (die voeden de teaser).

Anti-patronen (niet selecteren): persoonlijk levensverhaal/oorsprong (context, geen snippet), twee overlappende vensters op hetzelfde thema, niche-tactiek zonder universeel toepasbare hook, filler (“ja precies”), en te vroeg afkappen terwijl de turn nog een tweede pointe heeft.

Lever op: een long-list van 20-25 kandidaten (tijdvak, emmer, hook-zin + mechanisme), plus 1 teaser-montage en een virale top-5 (gerankt op hook-kracht).


Stap 2: de 16:9-cut (schone volzin)

Doel: een clip die op een hele zin begint en eindigt, zonder afgekapte woorden.

Padding rond de gekozen tijden

  • Start mid-turn (>0,5s na turn-begin): start = user_start − 0.3.
  • Start op turn-grens (≤0,5s): start = user_start (géén pre-padding, anders pak je de vorige spreker).
  • Eind: minimaal +0.5s, meestal +1.0s. Maar: bij een gap ≤1,0s tot de volgende spreker cap je op +0.5s (anders pak je diens eerste woord). TurboScribe-turn-einde is niet het woord-einde; er zit 0,5-1,0s handover tussen.

Zin-grenzen exact vinden (belangrijker dan de padding-regels)

Padding alleen kapt nog woorden af. Detecteer de echte zin-grens met silence-detect in de bron rond je tijden:

ffmpeg -hide_banner -ss <range_start> -t <dur> -i SRC \
  -af "silencedetect=noise=-30dB:duration=0.25" -f null -
  • 0,30-0,5s stilte: vaak zin-eind
  • 0,5-1,0s: bijna zeker zin-eind
  • 1,0s: dramatische pauze of speaker-handover

  • <0,20s: woord-grens, géén zin-grens

Knip in de stilte (bijv. 50 ms na de start van de stilte), niet exact op het woord. Bij compound-snippets: knip de dramatische pauzes ertussen weg (vraag bij een cliffhanger of de pauze juist moet blijven).

KRITIEK: -ss is niet frame-accuraat op deze bron

-ss drift tot ~3s (long-GOP keyframes). Twee betrouwbare routes:

  • trim/atrim-filter via -filter_complex (zie baselines), geeft corr=1.000, iets trager.
  • -ss (start−5) -copyts -i src + trim=start:end op absolute PTS: frame-accuraat én snel (geen full-decode vanaf 0). Met re-encode (libx264) is -ss vóór -i hier wél prima.

Overshoot-regel: eindig standaard 2-4 sec voorbij de punchy landing (inclusief de volgende zin), tenzij je exacte timestamps hebt gekregen. De gebruiker trimt liever zelf terug dan dat de clip te kort is.


Stap 3: 9:16-reframe met spreker-focus

Dit is de kern van “de juiste persoon mooi in beeld”.

De twee regels die altijd samen moeten kloppen

  1. De juiste persoon in beeld: de spreker (niet wie de editor toevallig liet zien).
  2. Die persoon gecentreerd: gezicht op de 50%-lijn (dead-center). Een lichte looking-room (gezicht op 57-60%, aan de kant waar iemand kijkt) mag als voorkeur, maar niet de extreme randframing van een auto-reframe.

Vuistregel-taal: “camera meer naar rechts” betekent crop_x verhogen.

MEET, eyeball niet

Schat gezichtsposities niet: dat zit er zo 100-150 px naast. Meet face-x in een grid-frame of beoordeel kandidaat-crops met een middenlijn op een frame van ≥300px breed. Kleine montage-thumbnails misleiden (ze geven een verkeerde links/rechts-indruk). Dezelfde persoon zit bovendien op een totaal andere bron-x per camerahoek, dus neem nooit één vaste waarde per persoon aan.

Specs

  • Crop 540×960, y=0, geschaald naar 1080×1920 (25 fps, h264 yuv420p, aac).
  • Max crop_x = 1920 − 540 = 1380. Center: crop_x = face_x_source − 270.
  • Uitzondering afl-005: crop 608×1080 (minder inzoom), max crop_x = 1312.

Stappenplan

  1. Scene-detect op de SOURCE (niet op de 9:16, die mist alle cuts door de gelijke achtergrond):
    ffmpeg -i SRC -vf "select='gt(scene,0.04)',metadata=print:file=/tmp/cuts.txt" -an -f null -
    Threshold 0.04 voor de Marketingpraat-studio (niet 0.15/0.2; de studio heeft constante belichting). Bij twijfel: vul aan met een tijd-gelabelde contactsheet (frame elke 2,5s), want scene-detect mist soms cuts.
  2. Spreker-tijdlijn uit het transcript. Clip-tijd = podcast-tijd − clip-start. Transcript schrijft tijden >1u als 1:0X:XX. Let op: bron-tijd kan afwijken van transcript-tijd door een pre-roll (afl-005: bron liep +30,3s voor). Verifieer de offset via audio-cross-correlatie.
  3. Per (camera-segment × spreker): extract een full-res frame, meet face-x, bepaal shot-type en of de spreker in beeld is. Bereken crop_x:
    • Spreker kijkt rechts → gezicht op 35-40%: crop_x = face_x − 0.37 × 540
    • Spreker kijkt links → gezicht op 60-65%: crop_x = face_x − 0.62 × 540
    • Recht in de camera → crop_x = face_x − 270
  4. Render in één pass met een dynamische crop-expressie over de segmenten (zie baselines).

Vaste studio-crops (startpunt, meet per aflevering)

Bevestigd op afl-001 (Tonny+Martijn), 540×960 op 1920×1080:

Shotcrop_xcrop_y
Tonny CU5100
Martijn CU8350
Wide, Tonny is spreker500
Wide, Martijn is spreker1300 (niet 1015)0

Voor 3- en 4-persoons afleveringen: opnieuw kalibreren via een grid-frame.

Reactie-shots en bronbeperkingen

De editor knipt soms naar de niet-sprekende persoon (reactie-shot) terwijl iemand doorpraat. Default: crop op wie er on camera is (dat is normale podcast-grammatica), niet op de spreker die niet in beeld is. Alleen als expliciet gevraagd “spreker altijd in beeld” trim je die stukken eruit.

Staat de spreker echt niet in beeld (camera de hele tijd op de luisteraar), dan is dat een bronbeperking. Voorkeursvolgorde: (1) toon de reactie-shot netjes gekaderd en meld het, (2) pan naar max crop_x, (3) knip de clip in tot alleen het deel met spreker-on-camera. Freeze-frames: niet doen.

Belangrijk: ook een “single-speaker” snippet is bijna nooit een single-shot snippet. Draai dus altijd scene-detect, ook bij een monoloog.


Stap 4: captions

Team-standaard: upload de schone -9x16-speakerfocus.mp4 in de Submagic-webapp en laat daar de karaoke-captions maken.

Handmatige route (volledige controle): whisper-cli + libass.

  • Woord-timestamps: whisper-cli ... -ml 1 -sow -oj met het ggml-large-v3-turbo.bin-model.
  • Burn-in met ffmpeg (libass-build). ASS-stijl die werkte (“rood met witte glow, glass-look”): Montserrat Bold, 92pt (voor 1080×1920), primary rood &H000000FF, outline wit &H00FFFFFF breedte 8, blur 2, shadow 2 semi-transparant, alignment 2 (bottom-center), MarginV 220.
  • “Gesproken woord groter”-patroon: groepeer 4 woorden per regel; per regel emit je N Dialogues (één per actief woord), waarbij het actieve woord op \fscx130\fscy130 staat en de rest op 100%. Niet de hele zin inkleuren, en niet 1-woord-tegelijk (te springerig).

KRITIEK: render captions altijd op de schone -speakerfocus.mp4, nooit over een al-gecaptioned bestand (anders stapelen de captions in de pixels). Output: -9x16-speakerfocus-captions.mp4, en gebruik die nooit als input.


Stap 5: verificatie (verplicht)

  1. Audio cross-correlatie met de bron (frame-accurate start-check), via numpy op /usr/bin/python3: extract een WAV van de clip-start + het bron-venster, cross-correleer de RMS-envelope. corr=1.000 is perfect; <0.9 = drift.
  2. Volume op de grenzen:
    ffmpeg -ss 0 -t 0.2 -i OUT -af volumedetect -f null - 2>&1 | grep mean_volume
    < −40 dB = stilte = natuurlijke grens; > −30 dB = actieve speech = mogelijk afgekapt.
  3. Visuele tile op 5/25/50/75/95% van de duur, plus een frame per source-cut, met een middenlijn (drawbox=x=(iw/2)-2:w=4:h=ih:color=cyan) op ≥300px. Check dat de juiste, gecentreerde persoon in beeld staat.
  4. Duur-gate: abs(nieuwe_duur − oude_duur) < 0.15s.

Verwacht 1 iteratie op lastige (multi-shot) segmenten; de eerste render is zelden meteen perfect.


Bestaande clips repareren

Altijd eerst een backup: cp -n clip.mp4 clip.bak-voor-fix.mp4.

  • Verlengen (laatste woord valt weg): render de hele clip opnieuw uit de master met dezelfde start (via xcorr gevonden) en een later eind. Naadloos, geen las. Controleer met xcorr dat het begin exact matcht.
  • Eerder laten beginnen (prepend): plak een stuk vóór de bestaande render. Start op een schone zin-grens, scene-detect 0.04 voor cuts in dat venster, per-shot speaker-focus crop, en geef het laatste prepend-shot dezelfde crop als de bestaande clip voor een naadloze las.
  • Panning-fix (crop zat fout in één venster): surgical splice, vervang alleen de video van het foute venster, behoud de originele audio. Zet de ffmpeg-flags inline (niet in een $ENC-variabele).

Kant-en-klare ffmpeg-baselines

16:9-cut (frame-accuraat, schone zin):

ffmpeg -y -hide_banner -loglevel error -ss $((START-5)) -copyts -i SRC \
  -filter_complex "[0:v]trim=START:END,setpts=PTS-STARTPTS[v];[0:a]atrim=START:END,asetpts=PTS-STARTPTS[a]" \
  -map "[v]" -map "[a]" -c:v libx264 -preset medium -crf 18 -pix_fmt yuv420p \
  -c:a aac -b:a 192k -movflags +faststart out-16x9-nosubs.mp4

9:16-render met dynamische spreker-crop:

ffmpeg -y -hide_banner -loglevel error -i SRC -filter_complex \
  "[0:v]trim=START:END,setpts=PTS-STARTPTS,crop=540:960:x='<crop_expr>':y=0,scale=1080:1920:flags=lanczos[v];
   [0:a]atrim=START:END,asetpts=PTS-STARTPTS[a]" \
  -map "[v]" -map "[a]" -c:v libx264 -preset medium -crf 18 -pix_fmt yuv420p \
  -c:a aac -b:a 192k -movflags +faststart out-9x16-speakerfocus.mp4

<crop_expr> = if(lt(t\,T1)\,X1\,if(lt(t\,T2)\,X2\,X3)), komma’s backslash-escaped binnen -filter_complex. Alternatief zonder escaping: knip per segment met vaste crop en concat ze aan elkaar (makkelijker debugbaar; compound = segmenten met een gat waar de brug uit is).

Scene-detect op source: select='gt(scene,0.04)',metadata=print. Grid-frame om face-x te meten: -vf "drawgrid=width=192:height=108:thickness=2:color=red@0.6". Contactsheet: frame elke 15s (-vf scale=300:-2), dan tile=6x8.

Bij diepe if-nesting die breekt op bash-quoting: schrijf de filtergraph naar een bestand en gebruik -filter_complex_script.


Anti-patronen

  • -ss vertrouwen zonder cross-correlatie (drift tot 3s).
  • Scene-detect op de 9:16 doen (mist alles) in plaats van op de source.
  • Scene-detect-threshold 0.15+ (te hoog voor deze studio; gebruik 0.04).
  • Padding-regels toepassen zonder silence-detect (blijft afkappen).
  • Crops eyeballen op thumbnails (meet op full-res / met middenlijn).
  • Eén vaste crop_x aannemen per persoon of per aflevering (varieert per camerahoek).
  • Een “single-speaker” snippet als single-shot behandelen (editor knipt naar reactie-shots).
  • Captions over een al-gecaptioned bestand renderen (stack-bug).
  • Compound-joints niet schoonmaken (lekt audio-restjes zoals “En…”).
  • Overschrijven zonder .bak. Iteraties zijn de norm.
  • Meer dan 2x blind gokken bij een mid-zin cut: vraag de exacte tijd of plak ruim erbij zodat er handmatig getrimd kan worden.

Naslag: crop-waardes per aflevering

Deze waardes zijn per camerahoek gemeten en niet 1-op-1 overdraagbaar naar andere afleveringen. Altijd zelf meten; dit is context.

Afl-001 (Tonny+Martijn): Tonny CU 510 · Martijn CU 835 · Wide-Tonny 50 · Wide-Martijn 1300. (540×960)

Afl-002 (Celien, 3 sprekers; Speaker 1=Celien, 2=Martijn, 3=Tonny): Celien CU ~800 (varieert 876-1040 per hoek) · Celien 3-shot wide 1280-1380 · Martijn hosts-2-shot 565 (soms 800-840) · Tonny hosts-2-shot 50-100 · Tonny tegen muur 140. (540×960)

Afl-003 (Arjan solo + 2 hosts; naam-labels in transcript): Arjan medium 900 (dead-center 945, maar Thomas wilde iets looking-room) · Arjan profiel-CU 935 · Tonny CU 355 · Tonny afgedraaid 110 · Martijn reactie 590-600. (540×960)

Afl-005 (Tonny+Martijn NYC; 608×1080, max 1312): Tonny CU 356 · Martijn CU 992 · wide op linker-host 26. Master stond in ~/Downloads/.


Bronnen

Gedestilleerd uit de opgebouwde snippet-lessen (selectie-rubric v0.4, de 9:16-workflow, render-lessons v2 en v3, de recenter-speaker-methode, de reframe-reparaties en de tooling-naslag) en ~/Marketing/Marketingpraat/snippet-training-log.md. De snippet-selectie skill bevat de gestripte deel-versie van de selectie-rubric.