AUTOPILOTE - que faire quand l'utilisateur tape /short-form-video-editor
L'utilisateur télécharge une vidéo (et optionnellement un logo) et tape la commande. Fait TOUT le travail sans poser de questions ; ne t'arrête que si quelque chose te bloque vraiment (pas de vidéo trouvée, pas de ffmpeg). Le texte supplémentaire après la commande = notes de style/marque, applique-les.
- Trouver les entrées : la vidéo la plus récente dans les téléversements de la session (
ls -t /root/.claude/uploads/*/*.{mp4,mov,webm}ou le chemin donné dans le message) ; logo = une image jointe si présente (pas de logo -> pas de watermark/héro/outro-logo, utilise une outro propre sans texte). Langue = détection automatique avecasr_chunks.py ... auto. - Localiser la boîte à outils : le dossier de cette compétence (le répertoire contenant ce SKILL.md) contient
template/. Travaille dans un nouveau dossier de projet : à l'intérieur du clone du dépôt Video-editing en tant queprojects/<slug>-<YYYYMMDD>/quand ce dépôt est dans la session, sinon dans le scratchpad.cp -r template/. <project>/. - Vérifier/installer les dépendances (une fois) :
ffmpeg, Node + Playwright Chromium (/opt/pw-browsers, ne jamais exécuterplaywright install),pip install numpy pillow sherpa-onnx,scripts/fetch_asr_model.sh. Pas de réseau pour huggingface ; les releases GitHub + npm + PyPI fonctionnent. - Exécuter le Workflow ci-dessous de bout en bout, en prenant toi-même les décisions de conception (couleur de marque à partir du logo ; sinon palette tech corail/teal ; scènes choisies dans
reference/scene-patterns.mden fonction de ce qui est réellement dit). - Livrer :
SendUserFilele mp4 (afficher le rendu), puis un court résumé : spécifications, les graphismes ajoutés, les passages de transcription à FAIBLE CONFIANCE à vérifier à l'oreille, la limitation de résolution de la vidéo source, "audio non écouté". Sauvegarde le projet (sanswork/) et le mp4 final dans le dépôt et pousse vers la branche désignée de la session (oumainsi aucune n'a été donnée). Ne crée jamais de PR sauf demande explicite. - Prévois ~15-25 min pour une vidéo de 60 s (transcription ~3 min, rendu ~4 min, encodage ~2 min). Informe l'utilisateur en une ligne au début, puis travaille en silence jusqu'à la fin.
MODE DOUBLAGE - traduction + nouvelle voix + synchronisation labiale (testé une fois sur le clip Glow : arabe -> anglais)
À utiliser quand l'utilisateur veut la vidéo dans une autre langue "avec les lèvres qui suivent les mots".
- Script : transcris + vérifie l'original comme en mode Édition, puis écris toi-même la traduction dans
data/phrases.<lang>.json=[[win_start, win_end, "texte avec *mots clés et remplacements DISPLAY|parlés (NBSP à l'intérieur des tokens multi-mots)"], ...]. Garde les FENÊTRES de phrase ORIGINALES pour que les repères de caméra/graphismes survivent ; tu peux fusionner les fenêtres voisines. Prépare environ 2,5-3 mots/s (anglais) par fenêtre - condense plutôt que de se précipiter ; garde le sens, pas de nouvelles affirmations. Exemple de remplacement parlé :*COSRX|Cosrex,Axis-Y|Axis\u00a0Y. - Modèles :
scripts/fetch_dub_models.sh(+fetch_asr_model.shpour la vérification). - Voix (par défaut = CLONÉE à celle du locuteur) : construis
work/ref.wav= 4-5 extraits propres délimités par des pauses (environ 15 s) du discours original du locuteur (16k mono, concaténés).python3 -I scripts/tts_clone.py data/phrases.en.json work/ref.wav 8 <total_seconds>->work/voice_en.wav+data/transcript.source.jsonavec les PLAGES DE PAROLE réelles. YourTTS (coqui,fetch_dub_models.shle télécharge) clone le timbre sans apprentissage préalable ; le script génère N prises par ligne, garde la prise dont la relecture Whisper correspond le mieux au script, puis étire temporellement (0,72-1,25x, avec préservation de la hauteur) pour remplir ~90% de la fenêtre originale. Chiffres prouvés sur le clip Glow : hauteur médiane 144 Hz contre 154 Hz pour le locuteur (voix Kokoro standard 123 Hz), similarité du spectre à long terme 0,96 contre 0,90, chaque ligne se relit correctement. Épelle les noms de marques phonétiquement viaDISPLAY|spoken(et exclus-les du score - automatique). Solution de replitts_phrases.py= voix Kokoro standard (claire mais pas celle du locuteur). Accent : le modèle transfère le timbre/la hauteur, PAS un accent natif - ne promets pas un anglais accentué égyptien ; dis ce qui a été réalisé. - Synchronisation labiale sur les images RÉSOLUTION ORIGINALE :
ffmpeg -t <end> -i src -vf fps=30 work/raw/%05d.png;ffmpeg -i work/voice_en.wav -ac 1 -ar 16000 work/en16k.wav;MODELS=work/models python3 -I scripts/lipsync2.py work/raw work/en16k.wav work/ls --model wav2lip_gan_96.onnx --enhance 0.7(teste d'abord avec--range a b; exécution complète ~25 min pour 1700 images sur 4 CPU : boîtes Haar, repères 2dfan4 sur chaque image, puis Wav2Lip + GFPGAN par image). lipsync2 = recadrage FFHQ-512 aligné sur les repères, repères lissés, Wav2Lip sur la boîte du visage repérée, restauration GFPGAN fusionnée à 0.7, masque en forme de bouche (ne touche jamais le nez), collage en retour. Puisscripts/01b_upscale_lipsynced.sh. N'utilise PAS l'ancienlipsync.py(recadrage Haar grossier : lèvres étalées, artefacts ressemblant à une langue - l'utilisateur l'a qualifié de "très mauvais"). EDTalk-256 a été essayé et est pire (bouche fantomatique). Modèles : 2dfan4, gfpgan_1.4, wav2lip_gan_96 de la release GitHub facefusion-assets. - Preuve de synchronisation visuelle : planche contact des recadrages de bouche avec l'enveloppe audio sous chaque tuile - la bouche doit être fermée dans les silences et ouverte/variée quand la barre est haute. Les métriques de corrélation pixel par pixel sont trop bruitées ici ; ne t'y fie pas.
- Graphismes :
python3 -I scripts/build_transcript.py work/en16k.wav(règlemax_chars~22 dans le json source pour l'anglais), régénèrecomposition/transcript.js, puis réécris la copie descene.jset les mots déclencheurs dans la nouvelle langue (LTR : direction.row/.chunkltr, police Inter, origine du trait à gauche, flèches de flux gauche->droite). Les tokens multi-mots sont des mots séparés dans la transcription : cherche les mots individuels (ts('skin',0,43.5)), jamaists('your skin'). - Rendu comme d'habitude (
LAST_SRC_FRAME=<n> scripts/02_render_all.sh, repères, sfx) et mixage avecVOICE=work/voice_en.wav OUT=out/nom-en.mp4 scripts/03_mix_and_encode.sh. - Sois honnête dans le résumé : Wav2Lip sur des visages d'environ 100px donne une bouche douce et légèrement lissée (visible à l'inspection rapprochée), les mouvements de tête/de mains sont toujours ceux du locuteur original, la voix est synthétique, le locuteur devrait approuver une version doublée de son image, envisage de la qualifier de doublage IA. Nomme toute phrase traduite dont la source était de faible confiance.
Éditeur vidéo court (sous-titres + graphismes animés + SFX)
Deux modes : Édition (garde la langue, ajoute sous-titres/graphismes/SFX) et Doublage (traduit dans une autre langue, nouvelle voix, lèvres réanimées sur le nouvel audio - déclenché quand l'utilisateur dit anglais/traduire/doubler/synchronisation labiale). Pipeline testé sur une promo pharmaceutique égyptienne-arabe de 57 s (Glow Cosmetics). Sortie : 1080x1920 @30, H.264/AAC, -14 LUFS.
Exemple de projet travaillé : projects/glow-kbeauty-reel/ dans https://github.com/MuhammadToffaha/Video-editing
Règles qui définissent "professionnel, pas un modèle"
- Garde le locuteur, la voix et le sens intacts. N'invente jamais d'affirmations (chiffres, "100%", prix). Si un graphisme a besoin de texte qui n'a pas été prononcé, garde-le générique (noms de catégories, étiquettes de sourcils).
- Chaque visuel soutient une phrase prononcée et est déclenché SUR le mot (
ts('mot')), pas au début d'une phrase. Les cartes sortent avant que la suivante n'entre ; pas de fantômes qui se chevauchent. - Un seul langage visuel : couleur de marque (échantillonne-la du logo) + blanc + panneaux verre foncé, Inter pour les étiquettes latines, Cairo (arabe) pour les titres/sous-titres. Menthe uniquement pour les états de succès.
- Zones de disposition (1080x1920, visage du locuteur
y 790-1110) : cartes y 235-735 (jamais au-dessus du visage, même après un zoom 1.15x), sous-titres y1545, garde les 220 px du bas et 130 px du haut libres de contenu clé (UI de la plateforme). Vérifie à nouveau la position du visage pour chaque source. - Dispositifs de rétention, avec modération : zoom instantané (1.12-1.15x) au début des phrases avec des dérives lentes entre les deux, un graphisme toutes les 2-4 s, surlignage actif des mots des sous-titres, micro-animations de barré / coche / compte à rebours, moment héro du logo quand la marque est dite, carte de fin de marque.
- SFX : whoosh à l'entrée de la carte, pop/tick sur les éléments, carillon sur le succès, thud sur le logo. Doux (environ -10 dB sous la voix). Pas de musique sauf si l'utilisateur la fournit.
Workflow
- Inspecter :
ffprobe, planche contact (ffmpeg -vf "fps=1/6,scale=239:-1,tile=5x2"), trouve les cartes de fin (cadre noir WhatsApp/CapCut) et note la position du locuteur. La source est généralement de basse résolution (téléphone/WhatsApp) : upscale avec lanczos, informe l'utilisateur que la vidéo sera douce mais que les graphismes resteront nets. - Projet :
cp -r <skill>/template/. mon-projet/ && cd mon-projet;cp <logo> assets/;python3 -I scripts/extract_logo.py assets/logo_original.png assets/logo_white.png(affiche le hex de la marque). Mets à jour--coral/--branddanscomposition/style.csset#wm/#outro. - Préparer :
END=<secondes de coupe> scripts/01_prepare.sh input.mp4(images, wav 16k, voix). - Transcrire (pas d'accès HF dans le sandbox ; les releases GitHub fonctionnent) :
scripts/fetch_asr_model.sh; ensuitepython3 -I scripts/asr_chunks.py work/asr16k.wav <lang|auto> 3.2 a, répète avec 2.4 et 4.5. Whisper tronque les fins de segments ; fusionne toi-même les 3 hypothèses et corrige avec ta propre connaissance de la langue.- Mots incertains / noms de marques :
python3 -I scripts/asr_probe.py work/asr16k.wav ar,en 14.0-16.8 14.5-16.6 ...(le passageenépelle les noms de marques latins). Ne fais confiance qu'à ce qui est cohérent entre les fenêtres qui se chevauchent ; liste le reste comme FAIBLE CONFIANCE dans le message final et le README. Ne construis pas de graphismes sur des noms devinés.
- Timing :
python3 -I scripts/pauses.py work/asr16k.wav-> les pauses sont les limites fiables des phrases. Écrisdata/transcript.source.jsoncomme[début, fin, "texte"]de phrases (voirdata/transcript.source.example.json;*mot= pilule d'accent ; utilise NBSP à l'intérieur des noms latins multi-mots ; marques latines dans le texte RTL : évite de coller le préfixe arabeوà eux, écrisو Brand). Puispython3 -I scripts/build_transcript.py work/asr16k.wavetecho "window.TRANSCRIPT=$(cat data/transcript.json);" > composition/transcript.js. Il distribue les temps des mots par poids syllabique à l'intérieur de chaque phrase et les snap aux vallées d'énergie, puis fabrique des morceaux de sous-titres équilibrés de 2-4 mots. Vérifie de bon sens : aucun début de mot à l'intérieur du silence. - Concevoir les scènes dans
composition/scene.js(voirreference/scene-patterns.md). Utilisecard(t0,t1,{y,h,html}),pop,fade,draw,cue,ts. Prends les exemples de scènes comme modèles : carte d'accroche, chips de marque (emplacements squelettes qui se remplissent quand chaque nom est dit), tuiles de catégorie, diagramme A->B avec point voyageur + coche, chips barrés, nœuds de flux, notation par étoiles, lignes de checklist, feuille de route, graphique à barres, anneaux de confiance, révélation héro du logo, outro de marque. Règlefinish(<secondes>)= fin de la voix + ~1,7 s d'outro. - Rendu + QA : rends d'abord quelques images de test (
node scripts/render.mjs <f> <f+1> work/test), construis des planches contact avec Pillow, REGARDE-les. Puisscripts/02_render_all.sh(4 workers, ~8 fps au total),node scripts/render.mjs cues work/cues.json && python3 -I scripts/sfx.py work/cues.json work/sfx.wav,OUT=out/nom.mp4 scripts/03_mix_and_encode.sh. Si la source se termine par une carte de fin, passeLAST_SRC_FRAME=<n>pour figer la dernière image propre. - Livrer : envoie le mp4 (SendUserFile), indique les spécifications, les passages de transcription de faible confiance, la limite de résolution de la vidéo, et que l'audio n'a pas été écouté à l'oreille (tu ne peux pas l'entendre) - seulement vérifié avec des mesures de volume sonore et des tests d'attaque vs énergie.
Pièges déjà résolus (ne les réapprends pas)
str.split()de Python divise sur NBSP - utilisesplit(' ')(déjà dans build_transcript.py).- Animer un
<rect>SVG avec CSSscaleYle retourne/décale dans Chromium headless - animeattr:{y,height}à la place. Dessin de trait :draw(path,t,d). - Les éléments qui commencent cachés ont besoin de CSS
opacity:0(classehid) et de tweens avecimmediateRender:false; chaque tween doit pouvoir être cherché (les workers sautent directement à leur image de départ). - Les cartes avec
backdrop-filterbrouillent seulement si l'image vidéo est dans la même page (#bgimg) - c'est pourquoi les images sont composées dans la page. - Les cartes floues vides ont l'air cassées : soit commence la carte sur son mot déclencheur, soit affiche des rangées/emplacements de placeholder sombres qui s'allument quand ils sont prononcés.
- L'arabe a besoin d'un vrai shaping : Chromium + Cairo woff2 (split unicode-range arabe/latin) fonctionne ; Pillow/ffmpeg drawtext ne fonctionne pas.
- Whisper dans sherpa-onnx n'a pas de timestamps de mots et tronque les fins ; d'où les petites fenêtres + ancrage sur les pauses.
- opencv 5.x n'a pas
CascadeClassifier(épingler 4.10.0.84) ; les boîtes de visage Haar donnent de faux positifs sur les mains/chemises et manquent ~30% des images -> rejet des valeurs aberrantes par boîte médiane + deuxième passage ROI + interpolation (déjà dans lipsync.py). - La TTS de style Kokoro/Whisper lit
COSRXcomme des lettres : utilise les remplacementsDISPLAY|spoken. - MCP GitHub
create_repositorypeut être 403 - demande à l'utilisateur de créer le dépôt, puisadd_repo+ clone (clone vers un répertoire frais si le chemin existe).