Communitygithub.com

Konversations-Videountertitel

Brennt Untertitel für Dialog-/Konversationsvideos (Reels, Shorts, TikTok, 1:1, 16:9) anhand von Wortzeitangaben ein: jedes gesprochene Wort, eine feste untere Grundlinie mit zentrierten Zeilen (keine Hebungen, keine seitlichen Verschiebungen; Überlappungen mit Gesichtern/Händen/Feuer werden als akzeptiert protokolliert, eine optionale Kollisionshebung wird mit Grund und Pixelhöhe protokolliert), Sprecher werden durch eine Farbakzent oder Mikromarkierung unterschieden, kein Lyrik-Stil-Bounce, WCAG-Kontrast wird pro Frame geprüft.

Was ist Konversations-Videountertitel?

Dieses Skill ermöglicht die professionelle Untertitelung von Dialogvideos (z. B. Reels, Shorts, TikTok, Interviews, 1:1-Formate) durch präzises Einbrennen von Untertiteln basierend auf Wortzeitangaben. Es unterstützt eine feste untere Grundlinie, bei der alle Zeilen zentriert bleiben und keine seitlichen Verschiebungen oder Hebungen stattfinden (optional mit Kollisionshebung). Die Sprecher werden durch Farbakzente oder Mikromarkierungen unterschieden, und der Kontrast wird per Frame gegen WCAG 4.5:1 geprüft. Das Skill erstellt einen kanonischen Untertitel-Manifest (JSON), eine eingebrannte Vorschau, sowie Exporte in SRT, ASS und WebVTT, plus einen Validator, der die gerenderten Pixel gegen das Profil misst. Es eignet sich besonders für Situationen, in denen „Untertitel zu einem Dialogvideo hinzufügen“, „Untertitel wie in Shorts“ oder „Karaoke-Untertitel mit aktivem Wort“ gefordert werden.

Funktioniert mit~Claude Code~Codex CLI~Cursor
npx skills add rocketmandrey/conversational-video-subtitles-skill

In Ihrer bevorzugten KI fragen

Öffnet einen neuen Chat, in dem dieser Agent-Skill bereits geladen ist.

Dokumentation

Was macht Konversations-Videountertitel?

Überblick

Untertitel sind eine eigene Ebene: Sie enthalten jedes gesprochene Wort und hängen nie davon ab, was im Bild gezeichnet ist. Bezeichnungen von Dingen im Bild (Teilenamen, Vor-/Nachteile, Pfeile) sind eine andere Ebene mit einer anderen visuellen Sprache (Hinweise neben dem Objekt); schneide niemals ein Wort aus den Untertiteln, weil „es bereits auf dem Bildschirm ist“.

Pipeline: words.json + forbid.json (Kästchen, die Untertitel nicht abdecken dürfen) + config.json + Profil → plan → manifest.json (kanonische Daten) → burn (eingebrannte Vorschau = visuelle Wahrheit) / export srt|ass|vtt → validate.

Format-Rollen (nicht verwechseln):

FormatEnthältEnthält NICHT
manifest.jsonWörter, Zeitangaben, Layout, placement, akzeptierte Überlappungen, Hebungen mit Gründen, Render-Prüfungen: Daten-Quelle der WahrheitPixel
.srtText + TimingGewicht, Farbe, Umriss, aktives Wort, Position. Versprich niemals Stil in SRT
.assFamilie + Fett/\b700, Größe umgerechnet in libass-Metriken, Umriss, Hintergrundbox, aktives Wort pro Zeitscheibe, PositionÜberblendungen; benötigt die Schriftart: subtitles=x.ass:fontsdir=fonts
.vtt::cue CSS (Familie, Gewicht, Umriss via text-shadow, Hintergrund) + Klassen .active_<spk> / .em_<spk> / .dim / .u pro Zeitscheibeexakte Position; die Seite muss die Schriftart mit @font-face laden
eingebranntes preview.mp4alles: die einzige Quelle visueller Wahrheit—

Wann verwenden

  • Dialog-Sketche, Interviews, Erklärvideos mit Sprecher: vertikal oder quadratisch, maximal eine oder zwei Zeilen.
  • Du hast Wortzeitangaben (oder Audio: optionale Cloud-STT über Umgebungsvariablen) und weißt, wo Gesichter / Hände / Schlüsselrequisiten sind (Kästchen; für 3D-Szenen ein flaches Farb-Matte rendern und boxen).
  • Nicht für Lyrik-Videos (hüpfende, skalierende Wörter werden bewusst nicht unterstützt).

Algorithmus

Segmentierung (chunk im Profil): Harte Trennung bei Pause > pause_s (0,35 s), Satzende, Sprecherwechsel; weiche Trennung bei Komma, sobald ein Chunk 2+ Wörter hat; begrenzt durch max_words, Pixelbreite (max_width_frac der Safe Zone) und max_dur_s (~1,2 s Sprache liest sich gut). Ein Chunk endet nie auf einem Funktionswort (не, в, а, and, the…): es geht zum nächsten Chunk. Ein einzelnes Wort schließt sich dem Nachbarn im selben Satz an, wenn es passt. Wortdauern werden auf das 2,5-fache des Medians begrenzt, damit ein Wort nicht in eine Pause hängt. Timing: Eine Einheit erscheint ≤ 50 ms vor ihrem ersten Wort, schneidet nie das vorherige Wort, bleibt hold_s nach ihrem letzten Wort, mindestens min_show_s.

Platzierung — placement: {mode: fixed_bottom_center, collision_lift: false} (Standard für Dialog).

  1. Eine Grundlinie für das gesamte Video: baseline = safe.bottom - baseline_margin_px (oder explizit setzen). Jede Einheit ist unten an dieser Grundlinie ausgerichtet (y + h = baseline) und horizontal zentriert (x + w/2 = W/2). Nichts bewegt sich: keine Hebung, keine seitliche Verschiebung, kein x pro Sprecher. Die Zeilenbreite ist so begrenzt, dass die zentrierte Zeile innerhalb der Safe Zone bleibt.
  2. Eine Zeile, die an der Grundlinie ein verbotenes Kästchen kreuzt, bleibt bestehen: Die Einheit erhält overlap_accepted: [classes] und das QA-Blatt zeichnet einen gelben Rahmen „Überlappung akzeptiert (feste Grundlinie)“. Information, kein Fehler. Das Kreuzen der Plattform-UI ist weiterhin ein Fehler (Schrift verkleinern oder Phrase aufteilen).
  3. Option collision_lift: true (Modus baseline_lift): x unter dem Sprecher (align: speaker) oder zentriert mit links/rechts Ausweichmöglichkeiten; wenn jedes x kollidiert, um ganze lift_step_px Schritte heben, bis Platz ist. Die Einheit erhält lift = {px, reason: [classes an der Grundlinie getroffen], t0, t1}, das Manifest listet alle Hebungen in collision_lifts. Kein Grund → keine Hebung. Nur verwenden, wenn die Aufgabe verlangt, Gesichter um jeden Preis freizuhalten.
  4. Niemals ein Wort fallen lassen.

Aussehen. Umriss (outline_px) + weicher Schatten + ein leichter Hintergrund, dessen Alpha pro Einheit nur so weit erhöht wird, wie nötig, um die dunkelste leuchtende Farbe ≥ 4,5:1 (WCAG) gegen das 90. Perzentil der Pixel dahinter zu halten. Sprecher unterscheiden sich durch die Farbe des aktiven Wortes und, im Hybrid-/Kartenmodus, durch einen 4 px Balken in der Sprecherfarbe; keine Sprechblasen, keine schweren Namensschilder. Ruhige Bewegung: nur Alpha-Überblendungen, keine Skalierung, keine vertikalen Rutschen.

QA (scripts/validate.py, auch durch demo ausgeführt):

  • Schema: das Manifest gegen templates/manifest.schema.json;
  • Zeilen ≤ 2;
  • Platzierung: in fixed_bottom_center ist die Streuung von y-Grundlinie und x-Zentrum über alle Einheiten 0 (0,5 px Toleranz) und es gibt keine Hebungen; in baseline_lift jede Hebung auf dem Raster, hat einen Grund, und die Einheit kollidiert tatsächlich an der Grundlinie;
  • Null Überlappungen mit der Plattform-UI auf jedem Frame; verbotene Kästchen: 0 in baseline_lift, gemeldet als overlap_accepted (Info) in fixed_bottom_center;
  • jedes Wort genau einmal, in Reihenfolge, sichtbar während es gesprochen wird;
  • Kontrast ≥ 4,5 und kein Bounce, beides gemessen pro Frame durch burn;
  • Schriftarten aufgelöst: Datei vorhanden, Pillow-Familie und -Stil, OS/2-Gewicht, fc-scan, und die libass fontselect wählt die deklarierte Datei und das Gewicht aus;
  • Pixel: auf dem eingebrannten Frame UND auf dem libass-Render des ASS ist die aktive Farbe vorhanden, der Glyphenboden ist innerhalb von 3 px des Layouts, die Strichbreite ist innerhalb von 1 px einer Referenz-Rasterung des deklarierten Gewichts, und der Umriss ist innerhalb von 1 px von outline_px.

Profile (profiles/*.json)

ProfilWasAm besten geeignet für
karaoke2–4 Wörter gleichzeitig, gesprochenes Wort in Sprecherfarbe, Rest abgedunkeltstabilste; feste Spuren, Erklärvideos
revealWörter werden beim Sprechen eingeblendet; Layout vorab festgelegt; ≤ 2 Zeilenruhige Erzählung
cardsEin Gedanke pro Karte (≤ 8 Wörter, 2 Zeilen), dichtes Panel, Schlüsselwort farbiglangsame, zitierfähige Zeilen; liest voraus
kineticEin Token nach dem anderen, größere Schriftnur Pointen; wenig Kontext
hybrid (Standard)≤ 3 Wörter, Wörter werden eingeblendet, Unterstreichung unter dem gesprochenen Wort, Hervorhebungsfarbe, SprecherbalkenDialog

Optionen aus den R&D-Systemen als Parameter: Tempo = chunk.max_dur_s: 1,2 + timing.min_show_s: 0,83 (an in hybrid); Betonung = emphasis: "energy" (Wortlautstärke z-Score pro Sprecher aus --audio), oder "keyword" mit einer keywords-Liste.

Befehle

python -m subkit demo examples/fixture                     # 5 Profile + hybrid_lift -> Vorschauen, srt/ass/vtt, validation_report.{md,json}
python -m subkit plan --words W --forbid F --config C [--profile hybrid] [--audio A] -o manifest.json
python -m subkit burn manifest.json --forbid F --video in.mp4 [--audio A] -o preview.mp4 --qa qa.jpg
python -m subkit export manifest.json --format ass -o captions.ass     # srt | ass | vtt
python scripts/check_clean.py                                          # vor einem Commit: keine Medien/Schlüssel/Absolute Pfade
ELEVENLABS_API_KEY=... python -m subkit stt audio.wav -o words.json     # optional; oder --provider groq mit GROQ_API_KEY

Fallstricke

  • libass ≠ Pillow-Schriftgröße. libass dimensioniert nach OS/2 winAscent+winDescent und setzt die Grundlinie winAscent unter die Zeilenoberkante; export konvertiert beides, sonst kommt ASS-Text ~35 % kleiner, dünner und tiefer heraus.
  • Variable Schriftarten in ASS fallen auf die Standardinstanz zurück (normalerweise Regular). Liefere eine statische Instanz des verwendeten Gewichts (hier Onest-Bold.ttf, 700) und übergib fontsdir.
  • ffmpeg -ss vor -i setzt Zeitstempel zurück: Der Untertitel-Filter rendert dann t=0. Suche nach -i beim Einbrennen von ASS.
  • \k/\kf-Karaoke ist in Zentisekunden und färbt jedes gesprochene Wort dauerhaft; für „nur das aktuelle Wort“ werden Zeitscheiben ausgegeben (wie export es tut).
  • SRT hat keinen Stil: Wenn eine Lieferung Gewicht oder Hervorhebung benötigt, liefere ASS/VTT oder das eingebrannte Video aus.
  • Eine schwebende Zeile (x pro Sprecher, Hysterese, „schönere“ Hebungen) wirkt bei Dialog wie Zittern. Behalte den Standard-Festmodus bei; in baseline_lift prüft der Validator erneut, dass jede Hebung wirklich kollidiert.
  • Kontrast wird gegen die hellste Farbe und die hellsten 10 % der Pixel dahinter gemessen, nicht den Durchschnitt: Feuer, Schnee und Glanzlichter bestehen Durchschnitte nicht.
  • Lasse nicht auf dem Bildschirm gezeichnete Wörter fallen. Verschiebe die gezeichnete Bezeichnung stattdessen in eine Anmerkungsebene.
  • Safe Zones in templates/safe_zones.json sind ungefähr; Plattform-UIs ändern sich, also überprüfe sie vor der Veröffentlichung erneut.

Checkliste

  • words.json deckt die gesamte Sprache ab (Cloud STT oder ein Forced Aligner); Sprecher gekennzeichnet
  • forbid.json: Gesichter, Hände, Feuer/Requisiten pro Einstellung (+ speakers-Kästchen für align: speaker)
  • config: Größe, fps, safe_zone, Sprecherfarben (verschiedene Farbtöne, beide ≥ 4,5:1 auf dem Hintergrund)
  • plan → Manifest; lese overlap_accepted (fest) oder collision_lifts (Hebungsmodus, jede mit echtem Grund)
  • burn → betrachte das QA-Blatt (cyan = Grundlinie, gelb = Überlappung akzeptiert, orange = Hebung mit Grund) und echte Frames
  • validate alle BESTANDEN; liefere gebranntes Video aus (+ ASS/VTT, falls ein Player sie rendert; SRT nur als Klartext)

Verwandte Skills