Communitygithub.com

skailabinfo/claude-video-harness

Montage d'une nouvelle vidéo à partir d'une prise brute : détection des doublons et faux départs, découpage des silences, correction de la transcription, extraction du masque pour le poster, beat-list de cartes et stickers, images à valider, master, miniature, archivage. Lance avec « monte takes/1.MOV », « nouvelle vidéo », « voici la prise ».

Qu'est-ce que claude-video-harness ?

Ce skill automatise le flux de production vidéo complet, depuis une prise brute jusqu'au master final, pour les créateurs et monteurs travaillant avec des assistants IA comme Claude Code. Il est conçu pour gérer les tâches répétitives et techniques du montage : détection et suppression des doublons et faux départs, découpage précis des silences, correction manuelle des sous-titres générés par Whisper, création de cartes, stickers et images clés, génération de la miniature et archivage structuré. Le skill utilise des scripts Python (cut.py, matte.py, cover-sticker.py) et FFmpeg pour toutes les opérations lourdes, et intègre un système de configuration basé sur un fichier `harness.config.json` ainsi que des profils utilisateur (brand-kit.md, editing-taste.md) pour respecter le style et les préférences du créateur. Il fonctionne sur n'importe quel terminal Unix avec Node.js, FFmpeg compilé avec libvidstab et les dépendances Remotion. L'agent exécute les étapes dans un ordre précis : inspection de la prise, découpage, masque en arrière-plan, correction des sous-titres, écriture du script et du code de composition, rendu des images de validation, master final, et enfin miniature et archivage.

Compatible avec✓Claude Code~Codex CLI~Cursor
npx skills add https://github.com/skailabinfo/claude-video-harness/tree/HEAD/.claude/skills/new-video

Demander à votre IA préférée

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

Documentation

Nouvelle vidéo : de la prise au master

Avant de commencer : harness.config.json → configured: true (sinon, skill setup), bash video/scripts/doctor.sh sans MISSING. Lis brand-kit.md, editing-taste.md, le « Profil de goût » dans exemplars.md, anti-patterns.md, delivery-specs.md. Le style de la vidéo provient uniquement du profil utilisateur : y a-t-il un poster, des cartes, des stickers, du b-roll, du son, combien de graphismes, quel rythme. Ce qui n'est pas décrit dans le profil, demande-le, ne décide pas toi-même. Le point de départ du code est video/src/ actuel.

Si video/public/footage.mp4 existe déjà de la vidéo précédente et n'est pas archivé dans edits/, archive-le d'abord (étape 11), puis commence.

1. Inspection de la prise

ffprobe -v error -show_entries format=duration:stream=width,height,r_frame_rate:stream_side_data=rotation -of default=nw=1 takes/1.MOV

Regarde l'orientation via rotation, pas via width (l'iPhone écrit la verticale comme 1920x1080 + flag). Regarde 3–4 images (ffmpeg -ss N -i … -frames:v 1 out/peek-N.jpg). Ne stabilise pas si filmé sur trépied — vidstab ajoutera du tremblement. Seulement si c'est clairement à main levée : stabilise la SOURCE (vidstabdetect=shakiness=6:accuracy=15 → vidstabtransform=smoothing=25:optzoom=1:interpol=bicubic, -c:a copy ; nécessite ffmpeg avec libvidstab) et travaille ensuite avec celle-ci.

2. Doublons et faux départs — AVANT le découpage

cd video && python3 scripts/chunks.py ../takes/1.MOV

La transcription continue cache les répétitions, mais le spectateur les repère immédiatement. Trouve : les segments voisins avec la même phrase (doublon — garde le DERNIER), les segments de 1–2 mots que le segment suivant recommence (faux départ). Montre à l'utilisateur la liste de ce que tu vas couper, avec les timecodes, et obtiens un « oui ». Les limites se situent dans les silences.

3. Découpage

cp public/captions.json out/captions.backup.json 2>/dev/null   # cut.py écrase la transcription
DROP="12.40-15.80" python3 scripts/cut.py ../takes/1.MOV

Le rythme provient du paramètre cut dans le profil (ajusté selon les références). Le script affiche DURATION, les images des coupes et la vérification « coupe sur parole ». Trop peu de coupes pour le rythme souhaité par l'utilisateur ? Passe des arguments en dessous du profil, par exemple … 0.4 0.2 (seuil ~0.35 / 0.2) ; trop de coupes ? Passe des arguments au-dessus. Aucune coupe du tout — pièce bruyante : QUIET=-46. Locuteur rapide / rythme lent — SPEED=1.1. Coupe sur parole : décale DROP ou les paramètres, ne laisse pas.

4. Masque — lance IMMÉDIATEMENT (si style.poster.enabled)

TITLE_OUT = première coupe dans la sortie de cut.py (le poster part sur celle-ci), généralement 4–7 s. Le masquage est la partie la plus longue (~8 s/image sur CPU, 2 workers ≈ 6 images/min), lance-le en arrière-plan et écris le code pendant qu'il tourne :

python3 scripts/matte.py <TITLE_OUT> 2 > out/matte.log 2>&1      # en arrière-plan (run_in_background)

Ne rends pas d'images fixes pendant qu'il tourne — cela le ralentit. Attends la fin via ps -axo command | grep "[m]atte.py", pas via pgrep. Le script ignore les images déjà prêtes — après un redémarrage, relance-le simplement.

5. Transcription

Ouvre public/captions.json (w, s, e en secondes, em) :

  • corrige les erreurs d'oreille : noms, marques, termes, anglicismes ; acronymes tels qu'ils s'écrivent (AI, ROI) ; Whisper aime ajouter « Merci d'avoir regardé » / « Thank you. » dans le silence de la queue — supprime-les ;
  • em: 2 (si style.captions.highlight — size ou both) et "accent": 1 (si color ou both) — selon le SENS, dans la quantité et sur les mots décrits dans le profil (brand-kit.md §2, §4). Non décrit ? Demande quels mots mettre en évidence.

6. Beat-list

Toi-même ou via le sous-agent script-beats (pour une vidéo longue), écris video/SCRIPT.md :

  • poster — uniquement si style.poster.enabled : lignes avec les mots et le ton de l'utilisateur, accent et kicker comme dans le profil ;
  • cartes — si style.cards n'est pas none, avec la densité de editing-taste.md §3 (nombre → count ; étape → num + head ; énumération → head + lines ou row avec des icônes sur les mots) ; from = premier mot de la phrase + 4 images ;
  • stickers FLOATS — uniquement si style.stickers : légende en un mot, pictogramme compact (ART_NAMES) ;
  • carte pain: true — uniquement si l'utilisateur veut « pas comme ça ».

Avant le code, montre brièvement à l'utilisateur la beat-list (quoi et où cela apparaîtra) — c'est sa vidéo, ses décisions.

7. Code

  • src/Composition.tsx : DURATION, TITLE_OUT, KICKER, TITLE, BEATS, FLOATS ; Captions from = début de la deuxième phrase si poster présent, sinon 0.
  • src/ui/Footage.tsx : tableau BEATS strictement selon les coupes de cut.py, à pleine puissance (gros plan 1.16–1.21×, normal 1.06–1.09×) — style.motion.punch l'atténuera ou le désactivera. Où placer le gros plan ? Selon editing-taste.md §4 ; après un gros plan, reviens plus large, sinon le plan large « colle » ; si poster, sur la première coupe.
  • Nouveau composant — uniquement si ni carte, ni sticker, ni Photo/Clip/Tag ne peuvent l'exprimer (dans ce cas, scene-builder).
  • B-roll depuis Higgsfield — si connecté et souhaité par le profil (brand-kit.md §5) : affiche via <Clip> (recadre et coupe le son à l'avance : ffmpeg -an -t <sec>), vérifie le texte/visages/mains sur les images générées avant insertion. Demande d'abord à l'utilisateur et indique le prix.
  • Son — uniquement si style.sfx : rôles manquants (card, count, end) — via le sous-agent audio-agent selon la description du profil.
  • npm run lint — propre.

8. Images à valider (après le masque)

Rends une image fixe pour chaque carte et sticker au moment où ils sont entièrement composés, le poster au milieu de sa durée (la dernière ligne est lue au-dessus des cheveux), plusieurs phrases de sous-titres, la DERNIÈRE image :

npx remotion still MyComp out/key/f<N>.png --frame=<N> --scale=0.5 --timeout=120000

Assemble-les en une planche de 4 par ligne avec des légendes numérotées (PIL ; police avec cyrillique, par exemple /System/Library/Fonts/Supplemental/Arial Bold.ttf), montre à l'utilisateur (SendUserFile). Le master vient seulement après son « ok ». La planche prend quelques minutes, le master 10–20 minutes.

Vérifie toi-même avant de montrer : le texte clé en dehors des 15% inférieurs / 8% supérieurs ; rien ne se chevauche ; le sticker ne croise pas la carte ; la dernière image n'est ni vide ni coupée ; tout correspond au profil utilisateur et rien de plus.

9. Master

npm run master        # en arrière-plan ; attends le processus, pas l'apparition du fichier

Le script vérifie lui-même les coupes, compte les images et produit out/preview.mp4. Vérifie le volume : ffmpeg -i out/final-master.mp4 -af ebur128 -f null - 2>&1 | grep -A3 Summary ≈ cible de audio.lufs. Envoie la prévisualisation à l'utilisateur.

Correction après le master (« coupe cette phrase ») — coupe le master lui-même (ffmpeg trim+concat sur silence), ne recoupe pas la prise et ne rerend pas tout ; annonce l'ETA à l'avance. Vérifie la jointure : coupe 6 s autour et exécute whisper — les deux phrases voulues doivent correspondre exactement.

10. Miniature

Demande quelle miniature est nécessaire si le profil ne le précise pas : image avec titre (Cover), poster de la vidéo (PosterCover) ou personnalisée. Choisis l'image avec l'utilisateur : propose 3–4 images nettes avec les yeux ouverts (ffmpeg -ss <sec> -i takes/1.MOV -frames:v 1 -q:v 2 out/cover-N.jpg).

  1. Image choisie → public/cover-bg.jpg.
  2. rembg i -m birefnet-portrait public/cover-bg.jpg out/cover-matte.png
  3. python3 scripts/cover-sticker.py public/cover-bg.jpg out/cover-matte.png public/cover-cutout.png 20
  4. Dans Root.tsx → Cover defaultProps.lines : titre avec les mots de l'utilisateur, accent / em comme il le souhaite. Les tailles s'ajustent automatiquement — ne modifie pas les corps.
  5. npx remotion still Cover out/cover.png --timeout=120000 — montre-la.

Alternative : PosterCover — le poster de la vidéo comme miniature (image + son masque dans poster-bg.jpg / poster-cutout.png).

11. Archivage

edits/<N>/ (à la racine, pas dans video/) : Composition.tsx, Footage.tsx, captions.json, SCRIPT.md, cover.png, final-master.mp4, preview.mp4. Si l'utilisateur le souhaite, passe ensuite au skill publish.

Individual skills in this repo

This repo contains 2 individual skills — each has its own dedicated page.

Skills associés