Communitygithub.com

video-cut

Transforme une longue vidéo non montée en un premier montage compact et regardable, avec des décisions éditoriales humaines et une exécution mécanique précise.

Qu'est-ce que video-cut ?

Cette compétence permet à un agent IA de transformer une vidéo brute (URL YouTube ou fichier local) en un premier montage resserré et regardable. L'agent prend les décisions éditoriales (quoi garder, quoi couper) en écrivant manuellement un plan de coupe (`edit_coarse.json`), tandis que des scripts automatisés gèrent la précision mécanique : alignement des coupes sur les silences, suppression des blancs, normalisation du rythme de parole avec changement de vitesse variable, et rendu mémoire-sécurisé. Le workflow comprend six étapes : téléchargement, transcription (faster-whisper), analyse des silences et des répétitions, décision éditoriale humaine, expansion et rendu, puis auto-vérification par retranscription et analyse des images. La compétence est conçue pour les agents Claude Code, Cursor ou Codex, et nécessite les dépendances `yt-dlp`, `ffmpeg`/`ffprobe` et Python avec `faster-whisper`. Elle produit un fichier `first_cut.mp4` et un rapport `first_cut_report.md`. L'auto-vérification est obligatoire : elle valide l'absence d'images noires, la synchronisation audio/vidéo, l'absence de silence résiduel et l'intégrité du contenu.

Compatible avec~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/WhiteTowerAI/cut-as-code/tree/main/skills/video-cut

Demander à votre IA préférée

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

Documentation

Video Cut

Transformez une longue vidéo non montée en un premier montage compact et regardable — décisif, conscient du contenu et auto-vérifié. L'humain (vous, le modèle) prend la décision éditoriale de CE qui est conservé ; les scripts fournis assurent la précision mécanique (alignement des limites, récupération du silence, rendu mémoire-sécurisé, auto-vérification).

Quand l'utiliser

  • Une vidéo brute/non montée (URL YouTube ou fichier local) doit être resserrée.
  • La demande est « réduis-la de moitié », « coupe les parties ennuyeuses », « fais un premier montage/ébauche ».
  • PAS pour : la micro-chirurgie des mots de remplissage, l'assemblage multi-caméras avec rushes, le polissage couleur/audio — ce sont des passes de polissage P1/P2 ultérieures. Cette compétence est P0 : valider le diagnostic + produire un montage solide.

Dépendances

/video-understand est un prérequis. Exécutez-le d'abord et utilisez ses médias validés, sa transcription, son analyse et sa timeline au lieu de les recréer dans cette compétence. Avant de commencer, vérifiez qu'il est installé. Si ce n'est pas le cas, prévenez l'utilisateur que ce prérequis manque et arrêtez-vous avant de traiter les médias.

Activez tout environnement contenant ces éléments (ne supposez PAS de nom d'environnement conda spécifique) :

  • yt-dlp (téléchargement), ffmpeg/ffprobe (coupe/rendu/analyse) dans le PATH
  • Python avec faster-whisper (le CPU fonctionne : device=cpu, compute_type=int8) Vérifiez d'abord : yt-dlp --version, ffmpeg -version, python -c "import faster_whisper".

Workflow du protocole de projet

Utilisez ces fichiers de projet durables :

work/cut/edit-plan.json          # décisions manuelles de conservation/suppression uniquement
work/timeline.json               # plages de précision générées + mappage source/programme
review/01-cut/cut-summary.md
review/01-cut/timeline-map.png
review/01-cut/boundary-review.mp4
review/01-cut/full-proxy.mp4  # optionnel, pour réviser le rythme global

Chaque décision canonique a un id stable, une action (keep ou drop), start_s, end_s, reason et des evidence_refs sémantiques/transcription. Les décisions keep sont chronologiques ; la V1 ne supporte pas le réordonnancement ou la duplication des clips sources.

Réutilisez la couche de preuves partagée plutôt que de retranscrire ou diagnostiquer à nouveau :

python skills/video-understand/scripts/transcribe.py work/cache/audio16k.wav work/understand/transcript
python skills/video-understand/scripts/analyze.py work/understand/transcript.json work/understand/analysis.json

Les scripts de précision existants acceptent le plan canonique. Étendez les limites en respectant les mots, attribuez éventuellement une vitesse variable linéaire, puis générez la timeline partagée :

python skills/video-cut/scripts/build_edit.py work/cut/edit-plan.json work/understand/transcript.json work/cache/edit-final.json
python skills/video-cut/scripts/assign_speed.py work/cache/edit-final.json work/understand/transcript.json
python skills/video-understand/scripts/build_timeline.py work/cache/edit-final.json work/timeline.json --fps-num SOURCE_FPS_NUM --fps-den SOURCE_FPS_DEN

Utilisez le FPS rationnel exact de work/understand/media.json ; n'arrondissez jamais 30000/1001 à 30/1. Examinez timeline-map.png et le court boundary-review.mp4. Ne générez full-proxy.mp4 que lorsque le rythme global doit être revu. Écrivez le résultat dans cut-summary.md et pointez le rapport de vérification de l'opération vers ce fichier.

Enregistrez la contribution de la timeline avec son entrée source requise, mettez à jour l'entier de révision revision et définissez based_on sur la révision exacte de compréhension consommée :

"render": {
  "kind": "timeline-transform",
  "input": "../input/original-video.mp4",
  "plan": "timeline.json"
}

Le rendu de livraison partagé encode la séquence active après la réussite de toutes les vérifications de révision.

Workflow de compatibilité autonome

Les fichiers et commandes hérités ci-dessous restent supportés pendant la migration. build_edit.py accepte à la fois edit_coarse.json et les decisions[] canoniques ; cut_render.py accepte à la fois edit_final.json et timeline.json ; transcribe.py de cette compétence est un wrapper de compatibilité autour de ../video-understand/scripts/transcribe.py.

Conservez tous les fichiers intermédiaires sous work/, livrez à la racine du projet :

work/source.mp4            # original téléchargé
work/audio16k.wav          # audio mono 16k pour whisper
work/transcript.json/.srt  # horodatages au niveau mot
work/analysis.json         # diagnostic (silences/tics de langage/runs)
work/edit_coarse.json      # *** VOTRE décision éditoriale (écrite à la main) ***
work/edit_final.json       # segments de rendu serrés étendus (générés ; portent la vitesse par segment)
work/selfcheck/            # retranscription du montage + images de jointure
first_cut.mp4              # LA LIVRAISON
first_cut_report.md        # rapport d'édition + auto-vérification

Le pipeline (6 étapes)

1. Téléchargement

Qualité la plus élevée accessible + audio. Le client lecteur par défaut échoue souvent (non disponible / DRM / connexion). La solution de repli fiable est le client android :

yt-dlp --extractor-args "youtube:player_client=android" \
       -o work/source.mp4 "<URL>"

Si seul un 360p multiplexé (format 18) est accessible sans connexion, acceptez-le et documentez la contrainte — le HD DASH est souvent verrouillé par DRM/connexion. Extrayez ensuite l'audio :

ffmpeg -y -i work/source.mp4 -ac 1 -ar 16000 work/audio16k.wav

2. Transcription (au niveau mot)

python scripts/transcribe.py work/audio16k.wav work/transcript [base.en]

Produit work/transcript.json (segments + début/fin par mot) et .srt. faster-whisper, CPU/int8, VAD activé, word_timestamps=True. Non-anglais : passez un modèle multilingue + --lang, par ex. python scripts/transcribe.py work/audio16k.wav work/transcript medium --lang zh (les modèles .en sont réservés à l'anglais). Vérifiez d'abord la langue sur un court échantillon. Remarque : Whisper supprime la plupart des « euh » vocalisés — ils ne sont PAS mesurables dans la transcription, donc la détection des tics de langage doit combiner la recherche lexicale ci-dessous avec l'analyse des silences, et non se fier uniquement au texte de la transcription.

3. Diagnostic (« voir » + « lire »)

python scripts/analyze.py work/transcript.json work/analysis.json

Révèle : silences de début/fin, silences entre mots ≥0,8s (avec le mot qu'ils suivent), nombre de tics de langage, mots/min, rapport de parole, répétitions quasi identiques, monologues >40s (candidats au découpage).

Pour voir l'image, échantillonnez une image toutes les ~2s dans une planche contact et lisez-la vous-même ; confirmez la structure des scènes / recherchez les images noires ou défectueuses :

ffmpeg -y -i work/source.mp4 -vf "fps=1/2,scale=240:-1,tile=10x10" -frames:v 1 work/contact.png
ffmpeg -i work/source.mp4 -vf "blackdetect=d=0.1:pic_th=0.98" -an -f null -

Pour un plan fixe d'une personne parlant, il n'y a pas de coupures de scène — attendez-vous à cela et notez que les jump-cuts ultérieurs sont normaux, pas des défauts.

4. Décider du montage — écrivez edit_coarse.json À LA MAIN

C'est le cœur éditorial. Lisez la transcription + l'analyse, puis écrivez un plan grossier de blocs à conserver et plages à supprimer qui couvrent toute la timeline sans trou ni chevauchement, chacun avec in, out et une reason d'une ligne. Compressez le long préambule en quelques pépites de valeur ; conservez le contenu substantiel en grands blocs ; supprimez les tangentes/divagations claires. Respectez l'ordre chronologique ; la timeline canonique V1 rejette le réordonnancement. Visez à réduire environ de moitié la durée.

  • Spécification du format + un exemple concret réel : reference/edit_coarse.schema.md.
  • Alignez chaque limite de bloc sur une fin de phrase/clause — ne commencez/arrêtez jamais au milieu d'une phrase ou sur un bégaiement. Utilisez :
    python scripts/inspect_bounds.py work/edit_coarse.json work/transcript.json
    
    Il accepte à la fois les plans keep[] hérités et decisions[] canoniques et imprime les mots chevauchant chaque limite afin que vous puissiez les déplacer sur des bords propres.

5. Expansion → vitesse automatique → rendu

Transformez les blocs grossiers en segments de rendu serrés sans silence, normalisez le rythme de parole, puis effectuez le rendu :

python scripts/build_edit.py   work/edit_coarse.json work/transcript.json work/edit_final.json 1.5
python scripts/assign_speed.py work/edit_final.json   work/transcript.json   # ajoute la vitesse par segment
python scripts/cut_render.py    work/edit_final.json   work/source.mp4 first_cut.mp4
  • build_edit.py divise chaque bloc aux pauses internes > SPLIT_GAP (4e argument, ex. 1,5s) et coupe chaque limite DANS le silence (LEAD 0,10s / TRAIL 0,30s) pour qu'aucun mot ne soit coupé et que le silence soit récupéré. Plus SPLIT_GAP est bas = plus serré mais plus saccadé ; pour un locuteur lent et délibéré, préférez ~1,5, pas 0,6 (évite les micro-coups saccadés).
  • assign_speed.py (vitesse variable automatique) — groupe les segments de rendu par decision_ref, mesure le rythme de parole de chaque décision de conservation écrite à la main, et écrit un facteur de speed partagé plus la out_dur résultante de chaque segment dans edit_final.json. Les décisions adjacentes diffèrent d'au plus 0,08x pour éviter les jonctions « pompeuses ». Il ne modifie que le rythme — jamais le contenu conservé. Lisez le tableau imprimé avant de rendre :
    • Métrique : mots/min (anglais) — bascule automatiquement vers caractères/min pour le CJK (zh/ja/ko).
    • Les valeurs par défaut sont des points de départ pour la première exécution, pas des constantes validées : zone neutre 145–185 mots/min et vitesse limitée à 1,0–1,3. Il s'agit d'une accélération uniquement : les blocs de décision lents peuvent être resserrés, tandis que les débits naturels rapides restent à 1,0. Les blocs de décision courts (<3,5s / <6 mots) sont trop bruités pour être mesurés et héritent du facteur global.
    • Le taux est mesuré après récupération du silence, donc il est plus élevé que le WPM brut et plus proche du taux d'articulation. L'examen du tableau imprimé est essentiel : examinez-le et ajustez deadband au locuteur avant le rendu, ne faites pas confiance aux valeurs par défaut aveuglément.
    • Réglages (k=v) : mode=segment|global|off (défaut segment ; global=un facteur pour toute la vidéo, off=désactivé), deadband=lo,hi, min_speed=/max_speed=, min_seg=/min_words=, lang=auto|en|cjk. Pour ignorer complètement la fonction, ne lancez simplement pas cette étape — cut_render.py traite une speed manquante comme 1.0.
  • cut_render.py utilise N entrées seeked séparées + filtre concat, un seul ré-encodage. C'est obligatoire : couper N fois sur UNE entrée décodée fait que ffmpeg met en mémoire tampon tout le flux décodé (dizaines de Go pour une longue vidéo). Voir reference/pitfalls.md. Lorsque les segments portent une speed, il re-temporise chaque entrée (setpts vidéo / atempo audio) et normalise fps/base de temps/taux d'échantillonnage par entrée avant concat pour que l'A/V reste verrouillé ; sans vitesse, il revient au graphe original exact.
  • Comparaison finale : video-edit-compare lit la timeline canonique plus la livraison finale réelle. Il projette les pixels finaux vers le temps source, étire les clips à vitesse variable sur leur durée source et montre les plages supprimées en noir.

6. Auto-vérification — ne déclarez pas terminé avant qu'elle ne réussisse

# vérification de régression de précision du protocole (exécutée depuis la racine du dépôt)
python skills/video-cut/scripts/check_project_protocol.py
# retranscrire le montage fini
ffmpeg -y -i first_cut.mp4 -ac 1 -ar 16000 work/selfcheck/cut_audio16k.wav
python scripts/transcribe.py work/selfcheck/cut_audio16k.wav work/selfcheck/cut_transcript
# vérification du contenu + tics de langage + limites
python scripts/verify_cut.py work/edit_final.json work/transcript.json work/selfcheck/cut_transcript.json
# vérification des images / noires / jonctions (les temps de jonction utilisent out_dur après vitesse, donc ils restent précis)
python scripts/selfcheck_frames.py work/edit_final.json first_cut.mp4 work/selfcheck
# synchronisation A/V + absence de silence résiduel
ffprobe -v error -select_streams v:0 -show_entries stream=duration -of csv=p=0 first_cut.mp4
ffprobe -v error -select_streams a:0 -show_entries stream=duration -of csv=p=0 first_cut.mp4
ffmpeg -i first_cut.mp4 -af silencedetect=noise=-32dB:d=1.5 -f null -

Seuils de réussite :

VérificationCondition de réussite
Images noiresaucune
Images de jonctionpropres — seulement des sauts de tête naturels, pas d'image noire/figée/déchirée
Sync A/Vdurée vidéo vs audio à ~1 image près (≈30 ms) ; total ≈ final_duration_s_after_speed si vitesse variable activée
Silence ≥1,5 s0 (le rapport de parole doit augmenter nettement)
Segment supprimé/dupliquéaucun — tout le contenu clé présent (difflib ~0,90 est OK ; un ratio plus faible est généralement du bruit de transcription de seconde passe sur un discours marmonné, pas une perte — vérifiez par présence de mots-clés avant de blâmer le montage)
Demi-phrases coupéesaucune — les limites tombent dans le silence / sur des fins de phrase

Avertissement honnête sur les tics de langage : le découpage au niveau paragraphe n'atteindra PAS « presque pas de tics de langage ». La densité par minute peut même augmenter car la substance conservée est conversationnelle. Signalez-le honnêtement ; l'excision des tics de langage au niveau mot appartient à P1/P2 (et rendrait un plan fixe d'une personne parlant visiblement saccadé).

Enfin

Écrivez first_cut_report.md : résumé de la source, ce qui a été coupé et pourquoi, le tableau d'auto-vérification, et les éventuels avertissements honnêtes (plafond de qualité dû à une source 360p, densité de tics de langage, etc.). Si une vitesse variable a été appliquée, divulguez-la : combien de segments ont été re-temporisés et la plage de vitesse (à partir du tableau de assign_speed.py / speed_params), afin que le lecteur sache que le montage n'est pas en temps réel 1:1. Notez également que le chiffre de tics de langage par minute de verify_cut.py est légèrement plus élevé après accélération (mêmes mots, minutes plus courtes) — un artefact de rapport, pas de nouveaux tics.

Principes clés (durement acquis)

  • Vous décidez du contenu ; les scripts décident des images. Écrivez edit_coarse.json à la main ; laissez build_edit.py faire la précision et assign_speed.py faire le rythme.
  • La vitesse touche le rythme, jamais le contenu. La vitesse variable automatique donne à tous les segments de rendu d'un même decision_ref la même vitesse, déplace le rythme hors zone uniquement vers le bord de la zone neutre le plus proche, limite les décisions adjacentes à un delta de 0,08x et limite la sortie à 1,0–1,3. atempo préserve la hauteur tonale. Examinez le tableau ; mode=off ou sautez l'étape pour désactiver.
  • Ne coupez jamais un mot. Les limites tombent toujours dans le silence.
  • Rendu mémoire-sécurisé uniquement (N entrées seeked + concat). Ne faites jamais plusieurs coupes sur une seule entrée.
  • Soyez décisif. Prenez des décisions raisonnables et avancez ; ne demandez pas trop souvent.
  • L'auto-vérification est non négociable. Retranscrivez + vérifiez les images avant de déclarer terminé.
  • Voir reference/pitfalls.md pour la liste complète des pièges et correctifs.

Individual skills in this repo

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

Skills associés