Communitygithub.com

Sous-titrage de Démo Vidéo

Gravez des sous-titres élégants et bien placés dans une démo produit enregistrée ou une visite guidée UI, et produisez la copie de légende qui l'accompagne.

Qu'est-ce que Sous-titrage de Démo Vidéo ?

Cette compétence est conçue pour superposer des sous-titres stylisés sur des vidéos de démonstration de produits enregistrées, en s'attaquant aux problèmes courants : texte UI trop petit, langue incorrecte ou absence de narration. Elle gère l'ensemble du pipeline : analyse image par image de la vidéo, extraction textuelle précise des éléments d'interface, rédaction de légendes avec un budget de vitesse de lecture, un rendu visuel spécifique (texte blanc sur fond violet avec contour et halo), un placement intelligent qui ne masque jamais le panneau de démonstration, une incrustation via ffmpeg/libass, et une vérification image par image. Cette compétence prévient également les pièges fréquents qui cassent ce flux de travail, comme le problème du paramètre `-ss` avant `-i` qui neutralise le filtre `ass`, ou les coordonnées de recadrage erronées sur les images de prévisualisation réduites. Elle supporte les longues chaînes CJK (chinois, japonais, coréen) et les scripts latins, et fonctionne sur macOS, Windows et Linux.

Compatible avec✓Claude Code~Codex CLI~Cursor
npx skills add heavenchenggong/demo-video-subtitler

Demander à votre IA préférée

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

Documentation

Sous-titreur de Démo Vidéo

Problème

Les démos produits enregistrées échouent auprès de leur public pour trois raisons qui n'ont rien à voir avec le produit : le texte UI est dans la mauvaise langue, il est trop petit pour être lu, et la vidéo n'a souvent aucune narration (enregistrements silencieux). Le spectateur voit des clics se produire mais ne peut pas comprendre pourquoi. Les sous-titres résolvent cela — mais seulement s'ils sont stylisés pour être lisibles sur une UI chargée, placés là où ils ne couvrent pas l'élément démontré, et chronométrés pour ne jamais être coupés par une transition.

Cette compétence est le pipeline complet, y compris les modes de défaillance non évidents qui coûtent des heures lorsqu'ils sont découverts à dure.

Quand l'utiliser

  • "Ajoute des sous-titres à cette vidéo de démo" / "sous-titre cet enregistrement pour notre équipe coréenne"
  • Un enregistrement d'écran silencieux (.MP4, sans flux audio, ou audio qui n'est que de la musique de fond) où l'histoire doit être racontée par le texte de la légende lui-même
  • Une démo produit en anglais uniquement montrée à un public non anglophone
  • Des sous-titres existent déjà mais sont centrés en bas et couvrent le panneau de démo — nécessitant un placement qui suit le focus à la place
  • Générer un script de lecture pour un présentateur qui parlera sur la vidéo (voir "Utilisation complémentaire")

Ne pas utiliser cette compétence pour : le doublage avec voix synthétisée (c'est un pipeline TTS), ou extraire des légendes qui existent déjà sur une plateforme.

Installation (une commande)

scripts/bootstrap.sh --dry-run          # inspection uniquement : affiche exactement ce qui manque
scripts/bootstrap.sh                    # installe ce qui manque (demande une fois)
scripts/bootstrap.sh --yes --lang ja    # sans surveillance ; prépare aussi les polices japonaises

bootstrap.sh est idempotent — il ne fait rien sur une machine déjà prête. Il détecte la plateforme et choisit le bon installateur (Homebrew / apt / dnf / pacman / winget / scoop), et se rabat sur le placement d'un ffmpeg statique dans ~/.local/bin lorsqu'aucun gestionnaire de paquets n'est disponible. Exécutez-le comme première action de toute session qui va incruster des légendes ; les compétences n'ont pas de hook au moment de l'installation, donc ce script est l'installateur.

Prérequis

Exécutez d'abord la vérification intégrée — elle inspecte la machine et vous dit exactement ce qui manque :

scripts/doctor.sh "Hiragino Sans GB"     # passez la famille CJK que vous comptez utiliser

Ce qu'elle vérifie, et la commande d'installation quand quelque chose manque :

PrérequisPourquoiInstaller
ffmpeg compilé avec libassincruste les légendes .ass (-vf ass=...)macOS brew install ffmpeg ; Windows : build complet gyan.dev ; Debian apt install ffmpeg
--enable-fontconfig dans la compilation ffmpegrésout les polices par nom de famille ; sans cela vous obtenez un visage de secours silencieuxles mêmes builds ci-dessus l'incluent
une famille de police CJK que le système peut résoudrele texte de la légende lui-mêmemacOS : Hiragino Sans GB (pré-installée) · Windows : Microsoft YaHei (pré-installée) · Linux : apt install fonts-noto-cjk
python3 (optionnel)seulement export_cues.py ; bibliothèque standard, aucun pip installmacOS : xcode-select --install ; Linux : apt install python3
bash, zip (optionnel)scripts, et empaquetage pour distributionpré-installé partout

Pas de Node, pas d'ImageMagick, pas de paquets pip, pas de clés API. Un modèle capable de vision (l'agent lui-même) lit les images — ce n'est pas quelque chose que vous installez.

Deux notes d'environnement qui piquent :

  • Mettez ffmpeg dans le PATH, ou exportez son emplacement pour les scripts : FFMPEG=/chemin/vers/ffmpeg scripts/burn.sh ....
  • Windows : libass résout les polices via fontconfig/DirectWrite selon la compilation. Si les légendes s'affichent avec la mauvaise police, installez fontconfig (scoop install fontconfig ou MSYS2) et revérifiez avec fc-match "Microsoft YaHei:bold".

Pipeline

Travaillez de haut en bas. Ne sautez pas la Phase 2 — chaque phase ultérieure dépend de la connaissance de la géométrie UI.

Phase 1 — Inventaire (2 minutes)

scripts/probe.sh INPUT.mp4

Enregistrez : durée, résolution, fps, débit binaire, si un flux audio existe. Exécutez la vérification audio dans le script (volume moyen + carte de silence).

Décision à partir de la vérification audio :

Résultat audioType de sous-titre
Aucun flux audio du toutSous-titres de narration — vous rédigez l'histoire ("ce qui se passe / pourquoi c'est important")
Audio présent mais pas de parole (musique uniquement)Identique ci-dessus. Confirmez en transcrivant une tranche de 10s : un outil ASR qui renvoie vide ou presque vide sur de l'audio fort signifie musique, pas voix
Parole présenteSous-titres de dialogue — transcrivez d'abord avec horodatages, puis nettoyez ; n'inventez pas la copie

Phase 2 — Analyse visuelle et géométrie (la phase que les gens sautent)

Extrayez les images à 2 ips (un clip de 60s → ~120 images ; révisez d'abord toutes les 4 images pour l'arc, puis ez-vez sur le texte) :

scripts/split_frames.sh INPUT.mp4 frames/ 2

Construisez trois artefacts avant d'écrire une seule légende :

  1. Feuille de rythme — | heure | ce qui a changé à l'écran | texte à l'écran (textuel) |. Un "rythme" est tout changement : une notification apparaît, un panneau glisse, un bouton est cliqué, un défilement, une permutation de page, une transition. Notez les types de transition (flash blanc, coupure, noir) — les légendes ne doivent pas les chevaucher.

  2. Texte UI, textuel, lu à la résolution source. Les images de prévisualisation à échelle réduite brouillent les petits textes UI et vous le transcrirez mal. Recadrez à partir du fichier original : ffmpeg -i IN.mp4 -ss 12.5 -frames:v 1 -vf "crop=W:H:X:Y,scale=2*iw:-1" out.jpg (-ss après -i pour une extraction image précise ; voir Pièges.)

  3. Carte de géométrie — les limites en pixels du panneau "actif" (la chose démontrée) en pixels source. Mesurez-le, ne le devinez pas :

    scripts/find_edge.sh INPUT.mp4 30 2880 300     # t=30s, bande à partir de x=2880, 300px de large
    

    Lisez ensuite l'image de bande renvoyée et notez où le bord gauche du panneau se trouve à l'intérieur de la bande ; ajoutez ce décalage au x de la bande pour obtenir le bord absolu. Répétez pour d'autres scènes si la disposition change (par exemple, un panneau de chat s'ouvre plus tard).

Règle qui découle de la carte de géométrie : la légende vit dans la région libre adjacente au panneau de démo, jamais par-dessus. Voir Phase 5.

Phase 3 — Rédigez la copie de légende

Deux règles gouvernent tout :

  • Une idée par réplique. Maximum 2–3 lignes courtes. Une réplique est une phrase, pas un paragraphe.
  • Budget de vitesse de lecture. CJK : 3,5–5,5 caractères par seconde est confortable ; au-dessus de 6 c'est une bousculade ; en dessous de 3 ça semble mort. Latin : 12–18 caractères par seconde. Calculez-le, ne devinez pas. scripts/export_cues.py affiche la densité par réplique une fois que vous avez un fichier .ass — itérez par rapport à ce nombre.

Règles de synchronisation des répliques :

  • Commencez la réplique au moment où le rythme visuel arrive, pas avant. Si une notification glisse à 1,0s, la réplique commence à 1,0s.
  • Terminez les répliques avant une transition (flash blanc / coupure), en laissant au moins 0,8s de silence à travers elle. Une légende qui chevauche une transition donne l'impression d'un glitch.
  • Laissez de vrais écarts. Trois à cinq secondes vides par minute garde la vidéo respirable ; des légendes de bout en bout épuisent le spectateur.
  • Pour les sous-titres de narration, mettez la raison dans la légende, pas la mécanique : "l'agent a contacté d lui-même" bat "une notification est apparue dans le coin supérieur droit".

Arrêtez-vous ici et faites approuver la copie. Texte approuvé → stylisation. Texte rejeté après stylisation coûte un deuxième rendu pour rien.

Phase 3b — Choix de la langue de la légende

Le pipeline est indépendant de la langue, mais trois choses doivent être décidées par langue avant la stylisation.

1. Famille de police — choisissez-en une que la plateforme peut résoudre (vérifiez avec scripts/doctor.sh <lang>):

Langue de la légendemacOSWindowsLinux
Chinois (zh)Hiragino Sans GB / PingFang SCMicrosoft YaHeiNoto Sans CJK SC
Japonais (ja)Hiragino SansYu Gothic / MeiryoNoto Sans CJK JP
Coréen (ko)Apple SD Gothic NeoMalgun GothicNoto Sans CJK KR
Latin (en/fr/de/es…)Helvetica NeueSegoe UIInter / DejaVu Sans

2. Vitesse de lecture — le budget dépend du script ; scripts/export_cues.py sélectionne le bon automatiquement (--lang remplace) et signale les répliques qui le dépassent :

ScriptConfortableSignaler comme trop rapide
Han / Kana (zh, ja)3,5–5,5 caractères/s> 6,0
Hangul (ko)4,0–6,0 caractères/s> 6,5
Latin12–18 caractères/s> 19

3. Césure des lignes — Le chinois et le japonais n'ont pas d'espaces entre les mots : coupez à la ponctuation (U+FF0C, U+3001, U+3002, U+FF1A) et ne coupez jamais un nom de produit. Le coréen et le latin coupent aux espaces ; gardez une particule coréenne attachée à son nom. Si une ligne ne tient pas, refaites la mise en page avant de réduire la police — une police plus petite perd plus de lisibilité qu'une ligne supplémentaire.

4. Largeur de ligne par script — la largeur des glyphes n'est pas la même selon les scripts, donc la même Fontsize donne des longueurs de ligne différentes. Budgetez les lignes comme floor(disponible_px / (fontsize * k)), où k est approximativement 1,0 pour Han et 1,05–1,1 pour Hangul et Kana (ils sont plus larges), et 0,5 pour Latin en moyenne (cela varie avec le mot).

Mesuré en 4K avec Fontsize 165 : 13 caractères Hangul ≈ 2 150 px (sûr dans un créneau de 2 450 px), tandis que 18 caractères ≈ 2 970 px — cela dépasse et pousse la ligne hors du bord gauche du cadre. Lorsqu'une ligne ne tient pas, refaites la mise en page ; ne réduisez pas la police pour la faire tenir.

Légendes bilingues (original + traduction) : seulement lorsque les deux lignes sont courtes. Deux lignes de langue doublent la charge de lecture et, dans une démo, rivalisent avec l'interface. Préférez une langue par version du fichier sauf si le client demande autrement ; quand vous devez, empilez l'original en haut et la traduction en dessous, et budgetez l'ensemble du bloc par rapport à la vitesse de lecture de la traduction.

Scripts de droite à gauche (arabe, hébreu) : libass prend en charge le RTL, mais la qualité du façonnage dépend de la compilation de harfbuzz. Rendez une image de prévisualisation avant de vous engager — vérifiez que les lettres se connectent correctement et que l'ancrage d'alignement a toujours du sens.

Le poids de la police diffère selon le script. Les familles japonaises et coréennes rendent généralement visuellement plus grandes qu'une famille chinoise à la même Fontsize ; vérifiez avec une image de prévisualisation et ajustez à l'œil, pas par règle.

Phase 4 — Stylisation (la lueur à deux couches)

Un seul style ASS ne peut pas produire "texte blanc avec un contour coloré épais et un halo coloré doux" — Outline ne dessine qu'un seul anneau. Utilisez deux événements par réplique sur deux couches :

CoucheRemplissageContourFlouObjectif
0transparent (&HFF000000)large, violet semi-transparent\blur 20–30le halo
1blancplus étroit, violet solideaucun / \blur1le texte lisible

Point de départ (voir assets/caption-template.ass). Les couleurs sont &HAABBGGRR — alpha 00 est opaque, FF est totalement transparent, et l'ordre des octets est BGR :

purple RGB(124,46,240)  →  &H00F02E7C
halo, 35% opaque        →  &H55F02E7C

Mettez à l'échelle avec PlayRes, pas au goût : un rendu 4K utilisait fontsize 180 / outline 16 / halo 28 / blur 24 ; le même rendu en 1080p est 80 / 7 / 12 / 12. Définissez PlayResX/Y sur la résolution de sortie et conservez ScaledBorderAndShadow: yes.

Phase 5 — Placement qui suit le focus

Centré en bas est mauvais pour les démos : le bas est généralement l'UI, et le panneau de démo est la seule chose qui doit rester visible. Anchez chaque réplique à la région libre adjacente à ce qui est montré :

Focus de cette répliqueAncragePourquoi
Un panneau latéral / panneau de chat est ouvert\an6\pos(<gauche_panneau - 40>, <milieu_vertical_panneau>)le bord droit épouse le panneau, le bloc croît vers la gauche, les changements de largeur ne nécessitent pas de recalcul
Un toast/notification en haut à droite\an3\pos(<droite_toast>, <bas_toast + 80>)l'ancrage en bas à droite place le bloc juste sous le toast
Page pleine largeur, sans panneau\an5\pos(<centre_visuel_x>, <y entre deux blocs de contenu>)centré, mais placé dans un écart mesuré — pas au centre géométrique
Carte titre avec grand type intégré\an5\pos(center_x, EN DESSOUS du titre)le centre géométrique entre en collision avec le propre titre de la vidéo

Calculez la largeur maximale de ligne avant d'écrire : max_line_px ≈ fontsize × caractères. Si une ligne dépasse la région libre, refaites la mise en page (ne réduisez jamais la police juste pour une réplique) — coupez à la ponctuation et gardez les noms de produits non coupés.

Pour les blocs ancrés à droite, un dépassement de quelques dizaines de pixels sur un fond plat est acceptable et semble intentionnel ; le dépassement sur le panneau de démo ne l'est pas.

Gardez l'ancrage hors du bord du cadre. Le contour et le halo s'étendent au-delà de la boîte de glyphe d'environ outline + blur pixels (en 4K avec outline 15 / blur 22, c'est ~40 px, plus l'anti-aliasing). Un ancrage \an3 stationné à 50 px du bord droit voit donc son halo coupé et se lit comme un mot cassé. Stationnez les ancrages à au moins 150 px à l'intérieur du cadre, et vérifiez le résultat dans une image de prévisualisation — le découpage est invisible dans le fichier .ass et évident à l'écran.

Phase 6 — Incrustation

scripts/burn.sh INPUT.mp4 subs.ass OUT.mp4 [crf]

Ceci est -i IN -vf "ass=subs.ass" -c:v libx264 -crf 17~18 -pix_fmt yuv420p -movflags +faststart, conservant la piste audio originale. Les sous-titres durs sont la bonne valeur par défaut pour les démos — ils survivent à chaque lecteur, téléchargement et partage d'écran ; expédiez un fichier .ass/.srt en accompagnement uniquement si demandé.

Phase 7 — Vérification

Rendez d'abord des images fixes bon marché, puis vérifiez le fichier encodé :

scripts/preview_frame.sh INPUT.mp4 subs.ass 16.0 /tmp/check1.jpg

Vérifiez chaque image pour : texte sur le panneau de démo, texte hors cadre, retour à la ligne inattendu, une réplique qui devrait être terminée, halo devenu bouillie sur un fond lumineux. Extrayez ensuite 3 images du fichier terminé (les sous-titres sont cuits, la qualité est finale) et confirmez à nouveau.

Pièges (chacun a coûté du temps réel à trouver)

  1. -ss avant -i neutralise le filtre ass. La recherche d'entrée réinitialise les horodatages à zéro, donc le filtre rend l'état du sous-titre à t≈0 pour chaque image. Résultat : la vidéo brûle sans sous-titres du tout, et ffmpeg n'affiche aucune erreur. Mettez toujours -ss après -i (recherche de sortie), ou ajoutez -copyts. Les scripts fournis le font déjà.
  2. Les coordonnées des images de prévisualisation mentent. Une image mise à l'échelle en 1280×720 mais affichée pour vous à ...

`}

Skills associés