Communitygithub.com

rocketmandrey/sous-titres-vidéo-conversationnels

Graver des sous-titres pour vidéos courtes de dialogue / conversation (Reels, Shorts, TikTok, 1:1, 16:9) à partir de timings de mots : chaque mot parlé, une ligne de base fixe en bas centrée (pas de décalages latéraux ni de levées ; les chevauchements avec visages/mains/feu sont enregistrés comme acceptés, une levée optionnelle par collision est journalisée avec raison et px), locuteur distingué par une couleur d'accent ou micro-marqueur, pas de rebond façon paroles de chanson, contraste WCAG vérifié image par image. Produit un manifeste canonique des sous-titres (JSON), un aperçu gravé, SRT simple, ASS et WebVTT stylisés, plus un validateur qui mesure les pixels rendus par rapport au profil. À utiliser quand on demande « ajouter des sous-titres à une vidéo de dialogue », « субтитры для диалога / скетча / рилса », « fais des subs comme dans les shorts », « graver des sous-titres sans couvrir les visages », « sous-titres karaoké avec mot actif », « vérifie les sous-titres par rapport à la zone de sécurité », ou pour exporter SRT / ASS / VTT à partir de timestamps de mots (ElevenLabs Scribe, Groq Whisper).

Qu'est-ce que sous-titres-vidéo-conversationnels ?

Cette compétence permet de générer des sous-titres conversationnels de haute qualité pour des vidéos courtes (Reels, Shorts, TikTok, etc.) en utilisant des timings de mots précis. Elle est conçue pour les dialogues, sketches, interviews et vidéos explicatives, avec un accent particulier sur la lisibilité et le respect des zones de sécurité (visages, mains, éléments clés). La pipeline comprend : un fichier de mots (`words.json`), un fichier de zones interdites (`forbid.json`), une configuration (`config.json`) et un profil de style. Le processus génère d'abord un manifeste canonique (manifest.json) contenant les données de placement et de chevauchement, puis produit un aperçu vidéo gravé (preview.mp4), ainsi que des fichiers SRT, ASS et WebVTT. Un validateur intégré vérifie la conformité au profil : placement fixes (pas de rebond), contraste WCAG ≥ 4.5:1, pas de chevauchement avec l'interface utilisateur de la plateforme, et précision des polices. La compétence prend en charge plusieurs profils visuels (karaoké, révélation, cartes, cinétique, hybride) et offre des options de levée de collision pour éviter de couvrir les visages, avec journalisation des raisons. Elle est utilisable via ligne de commande (`python -m subkit`) et peut convertir des fichiers audio en timings de mots via les API ElevenLabs ou Groq Whisper. Les sorties sont compatibles avec toutes les principales plateformes de diffusion vidéo.

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

Demander à votre IA préférée

Ouvre une nouvelle conversation avec cette compétence d'agent déjà préchargée.

Documentation

Sous-titres vidéo conversationnels

Aperçu

Les sous-titres forment leur propre couche : ils portent chaque mot prononcé et ne dépendent jamais de ce qui est dessiné dans l'image. Les étiquettes des éléments dans l'image (noms de pièces, avantages/inconvénients, flèches) sont une couche différente avec un langage visuel différent (annotations à côté de l'objet) ; ne coupez jamais un mot des sous-titres parce qu'« il est déjà à l'écran ».

Pipeline : words.json + forbid.json (boîtes que les sous-titres ne doivent pas couvrir) + config.json + profile → plan → manifest.json (données canoniques) → burn (aperçu gravé = vérité visuelle) / export srt|ass|vtt → validate.

Rôles des formats (ne pas les mélanger) :

formatcontientne contient PAS
manifest.jsonmots, timings, mise en page, placement, chevauchements acceptés, levées avec raisons, vérifications de rendu : source de vérité pour les donnéespixels
.srttexte + timinggraisse, couleur, contour, mot actif, position. Ne promettez jamais de style dans SRT
.assfamille + Gras/\b700, taille convertie en métriques libass, contour, boîte de fond, mot actif par tranche de temps, positionfondus ; nécessite la police : subtitles=x.ass:fontsdir=fonts
.vttCSS ::cue (famille, graisse, contour via text-shadow, fond) + classes .active_<spk> / .em_<spk> / .dim / .u par tranche de tempsposition exacte ; la page doit charger la police avec @font-face
preview.mp4 gravétout : la seule source de vérité visuelle—

Quand l'utiliser

  • Sketches dialogués, interviews, vidéos explicatives avec narrateur : format vertical ou carré, une ou deux lignes maximum.
  • Vous avez des timings de mots (ou audio : STT cloud optionnelle via des clés d'environnement) et vous savez où se trouvent les visages / mains / accessoires clés (boîtes ; pour les scènes 3D, générez un masque de couleur unie et encadrez-le).
  • Pas pour les vidéos lyriques (mots rebondissants, à l'échelle, délibérément non pris en charge).

Algorithme

Segmentation (chunk dans le profil) : rupture dure sur pause > pause_s (0,35 s), fin de phrase, changement de locuteur ; rupture douce sur une virgule une fois qu'un bloc a 2+ mots ; limité par max_words, largeur en pixels (max_width_frac de la zone de sécurité) et max_dur_s (~1,2 s de parole se lit bien). Un bloc ne se termine jamais sur un mot fonctionnel (не, в, а, and, the…) : il passe au bloc suivant. Un mot isolé rejoint son voisin de la même phrase s'il tient. Les durées des mots sont plafonnées à 2,5× la médiane pour qu'un mot ne traîne pas dans une pause. Minutage : une unité apparaît ≤ 50 ms avant son premier mot, ne coupe jamais le mot précédent, reste hold_s après son dernier mot, au moins min_show_s.

Placement — placement: {mode: fixed_bottom_center, collision_lift: false} (par défaut pour le dialogue).

  1. Une seule ligne de base pour toute la vidéo : baseline = safe.bottom - baseline_margin_px (ou définie explicitement). Chaque unité est alignée en bas sur celle-ci (y + h = baseline) et centrée sur l'image (x + w/2 = W/2). Rien ne bouge : pas de levée, pas de décalage latéral, pas de x par locuteur. La largeur de ligne est limitée pour que la ligne centrée reste à l'intérieur de la zone de sécurité.
  2. Une ligne qui croise une boîte interdite à la ligne de base reste en place : l'unité reçoit overlap_accepted: [classes] et la feuille de QA dessine un cadre jaune « chevauchement accepté (ligne de base fixe) ». Information, pas un échec. Croiser l'interface utilisateur de la plateforme reste une erreur (réduisez la police ou fractionnez la phrase).
  3. Option collision_lift: true (mode baseline_lift) : x sous le locuteur (align: speaker) ou centré avec solutions de repli gauche/droite ; si chaque x entre en collision, soulevez par pas entiers de lift_step_px jusqu'à être libre. L'unité reçoit lift = {px, reason: [classes heurté à la ligne de base], t0, t1}, le manifeste liste toutes les levées dans collision_lifts. Pas de raison → pas de levée. Utilisez-le uniquement lorsque le brief demande de garder les visages dégagés à tout prix.
  4. Ne supprimez jamais un mot.

Apparence. Contour (outline_px) + ombre douce + un fond léger dont l'alpha est augmenté par unité juste assez pour que la couleur la plus sombre éclairée soit ≥ 4,5:1 (WCAG) par rapport au pixel au 90e centile derrière elle. Les locuteurs diffèrent par la couleur du mot actif et, en mode hybride/cartes, une barre de 4 px dans la couleur du locuteur ; pas de bulles de parole, pas de plaques nominatives lourdes. Mouvement calme : fondus alpha uniquement, pas de pop d'échelle, pas de glissements verticaux.

QA (scripts/validate.py, également exécuté par demo) :

  • schéma : le manifeste par rapport à templates/manifest.schema.json ;
  • lignes ≤ 2 ;
  • placement : en mode fixed_bottom_center l'étalement de y-baseline et de x-centre sur toutes les unités est 0 (tolérance 0,5 px) et il n'y a pas de levées ; en mode baseline_lift chaque levée est sur la grille, a une raison, et l'unité entre effectivement en collision à la ligne de base ;
  • zéro chevauchement avec l'interface utilisateur de la plateforme sur chaque image ; boîtes interdites : 0 en baseline_lift, signalé comme overlap_accepted (info) en fixed_bottom_center ;
  • chaque mot exactement une fois, dans l'ordre, visible pendant qu'il est prononcé ;
  • contraste ≥ 4,5 et pas de rebond, tous deux mesurés par image par burn ;
  • polices résolues : fichier présent, famille et style Pillow, poids OS/2, fc-scan, et le fontselect de libass sélectionne le fichier et le poids déclarés ;
  • pixels : sur l'image gravée ET sur le rendu libass de l'ASS, la couleur active est présente, le bas du glyphe est à moins de 3 px de la mise en page, la largeur de hampe est à moins de 1 px d'un raster de référence du poids déclaré, et le contour est à moins de 1 px de outline_px.

Profils (profiles/*.json)

profilquoimeilleur pour
karaoke2–4 mots à la fois, mot parlé dans la couleur du locuteur, reste atténuéle plus stable ; couloirs fixes, vidéos explicatives
revealles mots apparaissent en fondu à la parole ; mise en page fixée à l'avance ; ≤ 2 lignesnarration calme
cardsune pensée par carte (≤ 8 mots, 2 lignes), panneau dense, mot-clé colorélignes lentes et citables ; lecture avant la parole
kineticun jeton à la fois, police plus grandepunchlines uniquement ; peu de contexte
hybrid (par défaut)≤ 3 mots, les mots apparaissent en fondu, soulignement sous le mot parlé, couleur d'emphase, barre de locuteurdialogue

Options des systèmes de R&D en tant que paramètres : tempo = chunk.max_dur_s: 1.2 + timing.min_show_s: 0.83 (activé dans hybride) ; stress = emphasis: "energy" (score z de sonie du mot par locuteur à partir de --audio), ou "keyword" avec une liste keywords.

Commandes

python -m subkit demo examples/fixture                     # 5 profils + hybrid_lift -> aperçus, 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                                          # avant un commit : pas de media/keys/chemins absolus
ELEVENLABS_API_KEY=... python -m subkit stt audio.wav -o words.json     # optionnel ; ou --provider groq avec GROQ_API_KEY

Pièges

  • libass ≠ taille de police Pillow. libass dimensionne par winAscent+winDescent OS/2 et place la ligne de base winAscent sous le sommet de la ligne ; l'export convertit les deux, sinon le texte ASS sort ~35 % plus petit, plus fin et plus bas.
  • Les polices variables dans ASS reviennent à l'instance par défaut (généralement Regular). Livrez une instance statique du poids que vous utilisez (ici Onest-Bold.ttf, 700) et passez fontsdir.
  • ffmpeg -ss avant -i réinitialise les timestamps : le filtre de sous-titres rend alors t=0. Cherchez après -i lors de la gravure ASS.
  • Le karaoké \k/\kf est en centisecondes et colore chaque mot parlé de façon permanente ; pour « seulement le mot courant », émettez des tranches de temps (comme le fait l'export).
  • SRT n'a pas de style : si une livraison nécessite du poids ou une surbrillance, livrez ASS/VTT ou la vidéo gravée.
  • Une ligne flottante (x par locuteur, hystérèse, levées « plus jolies ») est perçue comme une gigue dans le dialogue. Gardez le mode fixe par défaut ; en mode baseline_lift, le validateur revérifie que chaque levée entre effectivement en collision.
  • Le contraste est jugé sur la couleur éclairée par rapport aux 10 % les plus lumineux des pixels derrière elle, pas la moyenne : le feu, la neige et les hautes lumières échouent aux moyennes.
  • Ne supprimez pas les mots déjà dessinés à l'écran. Déplacez l'étiquette dessinée vers une couche d'annotation à la place.
  • Les zones de sécurité dans templates/safe_zones.json sont approximatives ; les interfaces utilisateur des plateformes changent, donc revérifiez-les avant publication.

Liste de vérification

  • words.json couvre toute la parole (STT cloud ou aligneur forcé) ; locuteurs étiquetés
  • forbid.json : visages, mains, feu/accessoires par plan (+ boîtes speakers pour align: speaker)
  • config : taille, fps, safe_zone, couleurs des locuteurs (teintes différentes, toutes ≥ 4,5:1 sur le fond)
  • plan → manifeste ; lisez overlap_accepted (fixe) ou collision_lifts (mode levée, chacune avec une vraie raison)
  • burn → regardez la feuille de QA (cyan = ligne de base, jaune = chevauchement accepté, orange = levée avec raison) et les images réelles
  • validate tout PASS ; livrez la vidéo gravée (+ ASS/VTT si un lecteur les rend ; SRT uniquement en texte brut)

Skills associés