Clipify
Finden Sie packende Momente in einem Video, schneiden Sie sie als eigenständige Clips aus, formatieren Sie sie optional von 16:9 → 9:16 (Gesichtspanorama oder geteilter Bildschirm), nutzen Sie Zeitraffer für dichtere Enthüllungen und brennen Sie Opus-artige Wort-für-Wort-Untertitel ein.
Modi
Wählen Sie einen vor Schritt 1. Die Wahl ändert, wie Sie Momente finden und ob Untertitel angewendet werden.
- Dialogmodus (Standard für Podcasts, Interviews, Redner): Whisper transkribiert, Sie scannen nach Pointen / Reaktionen / peinlichen Pausen. → Schritt 1A. Untertitel in Schritt 5.
- Aktionsmodus (Holzhacken, Sport, Schlagzeug, Trommeln, alles mit wiederholten Aufprallgeräuschen): Whisper komplett überspringen,
detect_transients.pyausführen, um jeden Schlag zu finden, auf jeden Schneiden. → Schritt 1B. Schritt 5 überspringen; die Schlaggeräusche sind der Rhythmus. - Hybrid: Dialogmodus für Sprechabschnitte, Aktionsmodus für den Rest, gegenseitig geschnitten. Verwenden Sie sowohl 1A als auch 1B.
Wenn der Benutzer sagt „Ich rede nicht darin“, sind Sie im Aktionsmodus. Wenn er sagt „auf jedes X schneiden“ (hacken, schlagen, boxen, Trommelschlag, Schwung, Fußschritt), sind Sie im Aktionsmodus. Standardmäßig sonst Dialogmodus.
Eingaben
- Ein Videodateipfad (der Benutzer wird ihn bereitstellen; sonst fragen)
- Optional: gewünschtes Format (9:16, 16:9, 1:1) — wenn nicht angegeben, nach Auswahl der Kandidaten fragen
- Optional: Untertitel-Stil-Präferenz — wenn nicht angegeben, vor der Untertitelung fragen
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(ohne--language). - ffmpeg:
-hwaccel videotoolboxzum Dekodieren und-preset ultrafastzum Rendern hinzufügen. Verwenden Sie-c:v libx264 -crf 20für das endgültige Master. - Numpy zur 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— Sprecher-Zeitachse 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 findendetect_transients.py— scharfe Aufprallgeräusche finden (Aktionsmodus); siehe--help
Arbeitsverzeichnis: /tmp/clipify/ (zu Beginn erstellen, Artefakte zum Debuggen hinterlassen).
Arbeitsablauf
Schritt 1A — Dialogmomente finden (Dialogmodus)
mkdir -p /tmp/clipify
ffmpeg -y -hwaccel videotoolbox -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
Lesen Sie das resultierende JSON (oder .txt) und wählen Sie 3–5 Kandidaten-Clips aus. Lustige Signale zum Durchsuchen:
- Pointen und Reaktionen: Wörter wie „was“, „warte“, „unmöglich“, Lachen, „haha“, Schimpfwörter
- Umkehr-Momente: Setup-Frage → unerwartete Antwort
- Peinliche Pausen: Whisper-Segment mit langer Lücke oder Füllwörter („äh“, „ähm“)
- Selbstironie / zitierfähige Einzeiler: kurze Aussagesätze, die für sich allein stehen
- Audiospitzen: erkennen via
ffmpeg -af volumedetectoder auf schnelles Hin und Her achten (abwechselnde kurze Whisper-Segmente)
Für jeden Kandidaten vorschlagen: [Start, Ende, warum-lustig, vorgeschlagener Titel]. Zielen Sie auf 10–25s Clips ab. Zeigen Sie die Liste und lassen Sie den Benutzer bestätigen/auswählen.
Schritt 1B — Jeden Schlag finden (Aktionsmodus)
Ersetzen Sie Whisper durch Audio-Transienten-Erkennung. Der Detektor findet scharfe Aufprallgeräusche (Axt-auf-Holz, Faust-auf-Pratze, Trommelfell, Ball-auf-Schläger, Holz landet auf einem Stapel), indem er den spektralen Fluss im 1–6 kHz Band berechnet — den breitbandigen Impuls, den ein Aufprall erzeugt und den Umgebung/Wind nicht haben.
ffmpeg -y -hwaccel videotoolbox -i "$VIDEO" -vn -ac 1 -ar 16000 /tmp/clipify/audio.wav
python3 <skill-dir>/scripts/detect_transients.py /tmp/clipify/audio.wav \
--min-flux 30 --min-gap 0.6 > /tmp/clipify/strikes.json
Abstimmung:
--min-flux 30ist die vernünftige Voreinstellung. Echte Aufprallgeräusche registrieren 50–300; Umgebung/Wind liegt bei 5–15. Zu viele Fehlalarme → auf 40–50 erhöhen. Zu wenige Treffer → auf 20 senken.--min-gap 0.6(Sekunden) ist der minimale Abstand zwischen zwei Schlägen. Schnelles Trommeln benötigt möglicherweise 0.2; Hacken ist bei 0.6–1.0 in Ordnung.--band 1000:6000deckt Holz/Metall/Glas-Aufprallgeräusche ab. Schwere dumpfe Schläge (Bassdrum, Körpertreffer) liegen tiefer (--band 200:2000); Pfiffe, Klirren, Snare-Schläge höher (--band 4000:10000).- Für Multi-Clip-Quellen (ein Ordner mit Telefonclips) führen Sie den Detektor auf jedem Clip aus und wählen Sie eine abwechslungsreiche Auswahl aus verschiedenen Winkeln. Zielen Sie auf 50–65 Schläge für eine 45–60s Montage bei ~0.9s pro Einstellung ab.
Jede „Einstellung“ sollte ~0.4s vor dem Schlag beginnen (Ausholbewegung zeigen) und ~0.5s danach enden (Schlag + kurzer Durchschwung). Das sind ~0.9s pro Schlag.
Schritt 1.5 — Überprüfen, ob jeder Kandidat im Bild ist
Dies immer vor dem Rendern tun, in beiden Modi. Audioerkennung findet das Geräusch eines Ereignisses; die Kamera könnte es nicht eingefangen haben. Dies zu überspringen ist der schnellste Weg, um Benutzerfeedback zu erhalten wie „Du hast auf Hiebe geschnitten, bei denen ich nicht einmal zu sehen bin.“
# Ein Bild zu jedem Kandidaten-Schlagzeitpunkt T abtasten:
for T in $STRIKE_TIMES; do
ffmpeg -y -ss "$T" -i "$VIDEO" -frames:v 1 -vf scale=320:-1 \
/tmp/clipify/verify_${T}.jpg
done
Lesen Sie jedes Überprüfungsbild und verwerfen Sie den Kandidaten, wenn:
- Die Person nach unten gebeugt, außerhalb des Bildes oder hinter einem Hindernis ist
- Ein Daumen auf der Linse ist (ja, das passiert — und Sie bemerken es nur visuell)
- Es sich eindeutig um eine andere Schallquelle handelt (Türknall, fallengelassenes Werkzeug), die den Detektor ausgelöst hat
Für ~30 Kandidaten ist dies ein schneller ffmpeg-Aufruf pro Kandidat plus eine einzige Batch-Bildlesung. Billige Versicherung.
Schritt 2 — Jeden ausgewählten Clip zuschneiden
ffmpeg -y -ss "$START" -t "$DURATION" -i "$VIDEO" -c copy /tmp/clipify/clip_$N.mp4
(-c copy für sofortigen Zuschnitt verwenden. Nur erneut codieren, wenn Schnitte bildgenau sein müssen.)
Schritt 3 — Das Ausgabeformat festlegen
Fragen Sie den Benutzer (überspringen, falls bereits angegeben): „9:16 (TikTok / Reels), 16:9 (YouTube) oder 1:1 (Insta-Feed)?“
Schritt 4 — Wenn 16:9 → 9:16: Panorama-zwischen-Gesichtern vs. geteilter Bildschirm
Quellseitenverhältnis mit ffprobe erkennen. Wenn Quelle 16:9 und Ziel 9:16, fragen:
„Zwei Optionen: (a) Hartgeschnittene Panoramafahrt, die dem Sprecher folgt (jeweils ein Gesicht auf dem Bildschirm), oder (b) Split-Screen übereinander gestapelt mit beiden Gesichtern sichtbar. Welche möchten Sie?“
Die Frage überspringen, wenn es nur ein Gesicht gibt (Einzelsprecher-Clip). Bei einem einzelnen Sprecher einfach mittig zuschneiden.
Schritt 4a — Panorama-zwischen-Gesichtern (empfohlen für schnelle Dialoge mit Gesprächsteilnehmern)
-
Die beiden Gesichts-ROIs lokalisieren. Ein Bild abtasten:
ffmpeg -ss <Mitte> -i <Clip> -frames:v 1 /tmp/clipify/probe.jpg. Lesen. Augenmaß für den Mund+Kinn-Bereich jedes Gesichts alsx,y,w,him Pixelraum der Quelle. (Kein cv2 nötig — Kamera ist innerhalb eines Clips statisch; ein Bild reicht.) Durch Zeichnen von Boxen überprüfen: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.jpgHöchstens zweimal iterieren. Boxen sollten Mund + Kinn abdecken und Hände/Mikrofone vermeiden. Nicht überoptimieren — Bewegungsdifferenz ist nachsichtig.
-
Bewegungsenergie pro Bild in jedem 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 - -
Sprecher-Zeitachse erstellen (min. Verweildauer 1.0s — kurze Einwürfe werden zum vorherigen Sprecher verschmolzen):
python3 <skill-dir>/scripts/analyze.py /tmp/clipify/L.txt /tmp/clipify/R.txt 1.0 > /tmp/clipify/segments.json -
Panorama-x-Koordinaten für einen 9:16 vertikalen Streifen aus der Quelle wählen. Bei Quellbreite W=1920 und Zielbreite W=1080, Streifenbreite = 608.
- LINKS_X =
face_links_center_x - 304(Klemmung ≥ 0) - RECHTS_X =
face_rechts_center_x - 304(Klemmung ≤ source_W - 608)
- LINKS_X =
-
Den harten Schnitt-x-Ausdruck generieren und rendern:
EXPR=$(python3 <skill-dir>/scripts/build_pan.py /tmp/clipify/segments.json $LEFT_X $RIGHT_X) ffmpeg -y -hwaccel videotoolbox -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; für 4K-Quelle entweder zuerst auf 1920×1080 skalieren oder alle Koordinaten verdoppeln.
Schritt 4b — Geteilter Bildschirm (beide Gesichter immer sichtbar)
Zwei übereinander gestapelte Kacheln, 1080×960 jede. Die Kachel des aktiven Sprechers ist oben — die Überlagerung 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]
Erstellen Sie <RIGHT_SPEAKER_ENABLE> aus segments.json als between(t,a,b)+between(t,a,b)+... über den rechten Sprechersegmenten. Kachelzuschnitte sollten auf ~720×640 um jedes Gesicht zielen (1.125:1, um 1080×960 zu entsprechen).
Schritt 4c — Zeitlupen-Enthüllungen (optional)
Wenn der Benutzer eine langsame, malerische / Panorama- / Enthüllungsaufnahme straffen möchte („in der Hälfte der Zeit“), ohne den Bogen zu verlieren, stattdessen Geschwindigkeitsrampe anstelle von Zuschneiden verwenden. Zuschneiden zwingt Sie dazu, einen Teil des Bogens zu entfernen; Geschwindigkeitsrampen bewahren alle Schläge bei höherem Tempo.
# 2x Geschwindigkeit: 12s Bogen → 6s Ausgabe, Audiotonhöhe über atempo beibehalten
ffmpeg -y -ss "$START" -i "$CLIP" -t "$ORIG_DUR" \
-vf "setpts=PTS/2,scale=1080:1920:flags=lanczos,fps=30" \
-af "atempo=2.0" \
-c:v libx264 -preset fast -crf 21 -pix_fmt yuv420p \
-c:a aac -b:a 128k /tmp/clipify/clip_2x.mp4
Faustregeln:
- 1.5x — menschliche Bewegung, die sich „etwas flott“ anfühlen soll, ohne beschleunigt auszusehen
- 2x — prägnantere Enthüllung; wirkt immer noch natürlich
- 3x–4x — Zeitraffer-Vibe (Verkettung
atempo=2.0,atempo=2.0für 4x;atempoakzeptiert pro Filterinstanz nur 0.5–2.0) -af atempoweglassen und das Segment stummschalten, wenn das Audio nur Umgebung/Wind ist und die Chipmunk-Stimme ablenken würde
Nützlich, wenn der Rest des Schnitts rhythmisch ist (Hack-Montage, Hin-und-Her-Unterhaltung) und die Enthüllung sich sonst wie eine tote Stelle anfühlen würde.
Schritt 5 — Untertitel hinzufügen
Diesen Schritt im Aktionsmodus überspringen — die Schlaggeräusche sind der Rhythmus und Untertitel stören nur die Optik.
Einmal fragen (nur wenn der Benutzer noch keinen Stil angegeben hat):
„Drei Untertitel-Stile: Opus (großes fettes Weiß, gelbe Hervorhebung des aktiven Wortes), Karaoke (4-Wort-Blöcke, grüne Hervorhebung), Minimal (saubere Helvetica, keine Hervorhebung). Oder fügen Sie ein Beispiel ein, das Ihnen gefällt.“
Wenn sie ein Referenzbild/Beispiel einfügen: Schriftart, Größe, Gewicht, Farbe, Position und Animation so genau wie möglich nachahmen — eine benutzerdefinierte 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 brennen:
ffmpeg -y -i /tmp/clipify/clip_panned.mp4 -vf "subtitles=/tmp/clipify/captions.ass" \
-c:v libx264 -preset fast -crf 20 -c:a copy "$OUTPUT.mp4"
Schritt 6 — Liefern
- Jede Ausgabe im Verzeichnis
<source_dir>/clipify_out/speichern (mkdir, falls nicht vorhanden) - Eine Zeile pro Clip ausgeben: Name, Dauer, was lustig war, Ausgabepfad
- Die erste Ausgabe mit
open <path>öffnen, damit der Benutzer sie überprüfen kann - Iteration anbieten (anderer Stil, andere ROI, zu geteiltem Bildschirm wechseln, Untertitel zeitlich anpassen)
Fallstricke (Lehren aus vorherigen Durchläufen — nicht wiederholen)
- Audioerkennung ≠ sichtbares Ereignis. Immer Schritt 1.5 (jeden Kandidatenbild überprüfen) vor dem Rendern ausführen. Der Detektor findet das Geräusch eines Hiebs, nicht ob der Hacker im Bild ist. Treffer, bei denen die Person vornübergebeugt, außerhalb des Kamerabildes oder ein Daumen auf der Linse ist, lösen dennoch den Audiodetektor aus. Vor dem Rendern abfangen.
- Spektralfluss-Band ist wichtig. Standard
--band 1000:6000deckt Holz/Metall/Glas-Aufprallgeräusche ab. Schwere tiefe Schläge (Bassdrum, Körpertreffer) benötigen--band 200:2000. Pfeifen, Klirren, Snare-Schläge benötigen--band 4000:10000. Wenn--min-flux 30nichts zurückgibt, zuerst auf 15 senken; wenn Tausende zurückgegeben werden, vor dem Erhöhen des Schwellenwerts ein anderes Band versuchen. - Geschwindigkeitsrampe-Audio paarweise.
atempoakzeptiert in einem einzelnen Filter nur 0.5–2.0; für 4x Verkettungatempo=2.0,atempo=2.0. Ohneatempoergibtsetptsallein Chipmunk-Audio. - ROIs nicht überoptimieren. Maximal zwei Iterationen. Bewegungsdifferenz ist nachsichtig — breitere ROIs, die Mund+Kinn abdecken, funktionieren gut, auch wenn sie nicht perfekt auf den Mund zentriert sind.
- Auf Szenenschnitte innerhalb eines Clips achten. Führen Sie
ffmpeg -filter:v "select='gt(scene,0.3)',showinfo" -f null -aus, um Schnitte zu zählen. Wenn ein 16:9→9:16-Clip viele Schnitte hat, funktionieren die festen Gesichts-ROIs nur für die dominante Szene; warnen Sie den Benutzer und bieten Sie an, entweder einen Einzelaufnahme-Clip zu wählen oder eine außermittige Rahmung während Schnitten zu akzeptieren. - Quellauflösung ist wichtig. Wenn die Quelle 4K ist, entweder zuerst auf 1920×1080 herunterskalieren (schneller, für 9:16 Ausgabe in Ordnung) oder alle ROI/Panorama-Koordinaten mit 2 multiplizieren.
- Eingebrannte Untertitel in der Quelle. Einige „Roh“-Clips haben dennoch Untertitel. Wenn ja, das untertitelfreie Master per 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; Whisper die gesamte Quelle nur in Schritt 1, wenn Sie ein Transkript benötigen, um lustige Momente zu finden.
- Den Plan in einem Satz nennen, dann handeln. Nicht jede Iteration kommentieren.