Clipify
Finde die lustigsten Momente in einem Video, schneide sie als eigenständige Clips aus, formatiere optional 16:9 → 9:16 (Gesichts-Pan oder Splitscreen) und brenne Opus-artige Wort-für-Wort-Untertitel ein.
Eingaben
- Ein Videodateipfad (der Benutzer wird ihn bereitstellen; andernfalls nachfragen)
- Optional: gewünschtes Format (9:16, 16:9, 1:1) — falls nicht angegeben, nach Auswahl der Kandidaten erfragen
- Optional: Untertitel-Stil-Präferenz — falls nicht angegeben, vor dem Untertiteln erfragen
Werkzeuge (nur den schnellsten Weg verwenden)
- Whisper:
whisper --model tiny.en --word_timestamps True --output_format json(≈10× schneller alssmall.en; Qualität für Englisch ausreichend). Für Nicht-Englisch:--model base(--languageweglassen). - ffmpeg: Hardware-Dekodierung ist optional und plattformspezifisch —
-hwaccel autoverwenden oder weglassen (macOS:videotoolbox; Linux:vaapi/cuda/keine). Für Renderings-preset ultrafasthinzufügen. Für das endgültige Master-c:v libx264 -crf 20verwenden. - Numpy für Audioausrichtung (FFT-Kreuzkorrelation). Kein scipy/cv2 erforderlich.
- Skripte:
<skill-dir>/scripts/(wobei<skill-dir>das Verzeichnis ist, das diese SKILL.md enthält — typischerweise~/.claude/skills/clipify/)analyze.py— Sprecherzeitlinie aus zwei ROI-Bewegungsdateienbuild_pan.py— ffmpeg-Crop-x-Ausdruck mit harten Schnittenbuild_ass.py— Opus-artige ASS-Untertitel aus Whisper-JSONaudio_align.py— Offset eines Sub-Clips in einer längeren Quelle finden
Arbeitsverzeichnis: /tmp/clipify/ (zu Beginn erstellen, Artefakte zum Debuggen hinterlassen).
Arbeitsablauf
Schritt 1 — Die lustigsten Teile finden
mkdir -p /tmp/clipify
ffmpeg -y -i "$VIDEO" -vn -ac 1 -ar 16000 /tmp/clipify/audio.wav
whisper /tmp/clipify/audio.wav --model tiny.en --word_timestamps True --output_format json --output_dir /tmp/clipify --language en
Das resultierende JSON (oder .txt) lesen und 3–5 Kandidaten-Clips auswählen. Lustige Signale, nach denen gesucht werden sollte:
- Pointen und Reaktionen: Wörter wie "was", "warte", "echt jetzt", Lachen, "haha", Fluchen
- Wendepunkte: Setup-Frage → unerwartete Antwort
- Peinliche Pausen: Whisper-Segment mit langer Lücke oder Füllwörter ("äh", "hm")
- Selbstironie / zitierfähige Einzeiler: Kurze, in sich geschlossene Aussagesätze
- Audio-Peaks: Erkennung via
ffmpeg -af volumedetectoder nach schnellem Hin und Her (alternierende kurze Whisper-Segmente) suchen
Für jeden Kandidaten vorschlagen: [Start, Ende, warum-lustig, vorgeschlagener Titel]. Auf 10–25s Clips abzielen. Die Liste anzeigen und den Benutzer bestätigen/auswählen lassen.
Schritt 2 — Jeden ausgewählten Clip zuschneiden
ffmpeg -y -ss "$START" -t "$DAUER" -i "$VIDEO" -c copy /tmp/clipify/clip_$N.mp4
(-c copy für sofortigen Schnitt verwenden. Nur neu codieren, wenn Schnitte frame-genau sein müssen.)
Schritt 3 — Das Ausgabeformat festlegen
Den Benutzer fragen (überspringen, falls bereits angegeben): "9:16 (TikTok / Reels), 16:9 (YouTube) oder 1:1 (Insta-Feed)?"
Schritt 4 — Wenn 16:9 → 9:16: Pan zwischen Gesichtern vs. Splitscreen
Quellseitenverhältnis mit ffprobe erkennen. Wenn Quelle 16:9 und Ziel 9:16 ist, fragen:
"Zwei Optionen: (a) Hard-Cut-Pan, der dem jeweils Sprechenden folgt (ein Gesicht gleichzeitig auf dem Bildschirm), oder (b) Splitscreen-Stapel mit beiden sichtbaren Gesichtern. Welche möchtest du?"
Die Frage überspringen, wenn es nur ein Gesicht gibt (Einzelsprecher-Clip). Bei einem Einzelsprecher einfach zentriert zuschneiden.
Schritt 4a — Pan zwischen Gesichtern (empfohlen für schnelle Talking-Head-Dialoge)
-
Die beiden Gesichts-ROIs lokalisieren. Ein Frame mustern:
ffmpeg -ss <Mitte> -i <Clip> -frames:v 1 /tmp/clipify/probe.jpg. Lesen. Die Mund+Kinn-Region jedes Gesichts alsx,y,w,him Pixelraum der Quelle grob schätzen. (Kein cv2 nötig — Kamera ist innerhalb eines Clips statisch; ein Frame reicht.) Durch Einzeichnen von Boxen verifizieren:ffmpeg -i probe.jpg -vf "drawbox=x=$LX:y=$LY:w=$LW:h=$LH:[email protected]:t=4,drawbox=x=$RX:y=$RY:w=$RW:h=$RH:[email protected]:t=4" verify.jpgMaximal zweimal iterieren. Boxen sollten Mund+Kinn abdecken und Hände/Mikrofone vermeiden. Nicht überoptimieren — Frame-Differenzierung ist verzeihend.
-
Bewegungsenergie pro Frame in jeder ROI extrahieren:
ffmpeg -y -i clip.mp4 -filter_complex " [0:v]split=2[a][b]; [a]crop=$LW:$LH:$LX:$LY,format=gray,tblend=all_mode=difference,signalstats,metadata=mode=print:key=lavfi.signalstats.YAVG:file=/tmp/clipify/L.txt[la]; [b]crop=$RW:$RH:$RX:$RY,format=gray,tblend=all_mode=difference,signalstats,metadata=mode=print:key=lavfi.signalstats.YAVG:file=/tmp/clipify/R.txt[ra] " -map "[la]" -f null - -map "[ra]" -f null - -
Sprecherzeitlinie erstellen (min. Verweildauer 1,0s — kurze Einwürfe werden dem vorherigen Sprecher zugeschlagen):
python3 <skill-dir>/scripts/analyze.py /tmp/clipify/L.txt /tmp/clipify/R.txt 1.0 > /tmp/clipify/segments.json -
Pan-x-Koordinaten für einen 9:16-Vertikalstreifen aus der Quelle wählen. Bei Quell-W=1920 und Ziel-W=1080 beträgt die Crop-Streifenbreite 608.
- LEFT_X =
face_left_center_x - 304(auf ≥ 0 begrenzen) - RIGHT_X =
face_right_center_x - 304(auf ≤ source_W - 608 begrenzen)
- LEFT_X =
-
Den Hard-Cut-x-Ausdruck generieren und rendern:
EXPR=$(python3 <skill-dir>/scripts/build_pan.py /tmp/clipify/segments.json $LEFT_X $RIGHT_X) ffmpeg -y -i clip.mp4 -filter_complex \ "[0:v]crop=608:1080:x='$EXPR':y=0,scale=1080:1920:flags=lanczos[v]" \ -map "[v]" -map 0:a -c:v libx264 -preset fast -crf 20 -pix_fmt yuv420p \ -c:a aac -b:a 192k /tmp/clipify/clip_panned.mp4Quelle 1920×1080 angenommen; bei 4K-Quelle entweder zuerst verkleinern oder alle Koordinaten verdoppeln.
Schritt 4b — Splitscreen (beide Gesichter immer sichtbar)
Zwei gestapelte Kacheln, 1080×960 jede. Die Kachel des aktiven Sprechers ist oben — das Overlay wechselt bei Sprecherwechseln.
[0:v]split=2[a0][a1];
[a0]crop=Wcrop:Hcrop:LX_tile:LY_tile,scale=1080:960,split=2[lt0][lt1];
[a1]crop=Wcrop:Hcrop:RX_tile:RY_tile,scale=1080:960,split=2[rt0][rt1];
[lt0][rt0]vstack[layoutL];
[rt1][lt1]vstack[layoutR];
[layoutL][layoutR]overlay=0:0:enable='<RIGHT_SPEAKER_ENABLE>'[v]
<RIGHT_SPEAKER_ENABLE> aus segments.json als between(t,a,b)+between(t,a,b)+... über die Segmente des rechten Sprechers erstellen. Kachel-Crops sollten ~720×640 um jedes Gesicht herum anvisieren (1,125:1, um 1080×960 zu entsprechen).
Schritt 5 — Untertitel hinzufügen
Einmal fragen (nur wenn der Benutzer nicht bereits einen Stil angegeben hat):
"Drei Untertitel-Stile: opus (groß fett weiß, gelbe Aktiv-Wort-Hervorhebung), karaoke (4-Wort-Blöcke, grüne Hervorhebung), minimal (saubere Helvetica, keine Hervorhebung). Oder füge ein Beispiel ein, das dir gefällt."
Wenn er ein Referenzbild/Beispiel einfügt: Schriftart, Größe, Gewicht, Farbe, Position und Animation so genau wie möglich nachahmen — ein benutzerdefiniertes ASS von Hand schreiben oder build_ass.py erweitern.
Sonst die Voreinstellung verwenden:
# Whisper erneut auf dem zugeschnittenen Clip ausführen, für genaue Zeitstempel relativ zum Clip-Start
whisper /tmp/clipify/clip_panned.mp4 --model tiny.en --word_timestamps True --output_format json --output_dir /tmp/clipify --language en
python3 <skill-dir>/scripts/build_ass.py /tmp/clipify/clip_panned.json /tmp/clipify/captions.ass opus
Untertitel einbrennen:
ffmpeg -y -i /tmp/clipify/clip_panned.mp4 -vf "subtitles=/tmp/clipify/captions.ass" \
-c:v libx264 -preset fast -crf 20 -c:a copy "$AUSGABE.mp4"
Schritt 6 — Liefern
- Jede Ausgabe in
<source_dir>/clipify_out/speichern (mkdir, falls nicht vorhanden) - Eine Zeile pro Clip ausgeben: Name, Dauer, was lustig war, Ausgabepfad
- Den ersten Ausgabepfad ausgeben (oder öffnen — Linux
xdg-open <Pfad>, macOSopen <Pfad>), damit der Benutzer ihn überprüfen kann - Iteration anbieten (anderer Stil, andere ROI, zu Splitscreen wechseln, Untertitel neu timen)
Fallstricke (Lehren aus früheren Durchläufen — nicht wiederholen)
- ROIs nicht überoptimieren. Maximal zwei Iterationen. Bewegungsdifferenzierung ist verzeihend — breitere ROIs, die Mund+Kinn abdecken, funktionieren auch dann gut, wenn sie nicht perfekt mundzentriert sind.
- Achtung vor Szenenschnitten innerhalb eines Clips.
ffmpeg -filter:v "select='gt(scene,0.3)',showinfo" -f null -ausführen, um Schnitte zu zählen. Wenn ein 16:9→9:16-Clip viele Schnitte hat, funktionieren die festen Gesichts-ROIs nur für die dominierende Szene; den Benutzer warnen und anbieten, entweder einen Einzel-Einstellungs-Clip auszuwählen oder während der Schnitte eine außermittige Rahmung zu akzeptieren. - Quellauflösung ist wichtig. Wenn die Quelle 4K ist, entweder zuerst auf 1920×1080 verkleinern (schneller, für 9:16-Ausgabe ausreichend) oder alle ROI/Pan-Koordinaten mit 2 multiplizieren.
- Eingebrannte Untertitel in der Quelle. Manche "rohen" Clips haben dennoch Untertitel. Wenn ja, das Master ohne Untertitel über Audio-Kreuzkorrelation (
audio_align.py) finden und von dort aus zuschneiden. - Whisper nicht auf der gesamten Spielfilmlänge ausführen, wenn ein kurzer Clip ausreicht. Whisper den zugeschnittenen Clip nach Schritt 2 ausführen; die gesamte Quelle nur in Schritt 1 mit Whisper bearbeiten, wenn ein Transkript zum Finden lustiger Momente benötigt wird.
- Den Plan in einer Zeile darlegen, dann handeln. Nicht jede Iteration kommentieren.