Communitygithub.com

kylefritz/video-skill

Créer, éditer, vérifier et comprendre des vidéos depuis un terminal, pour un agent qui ne peut pas les regarder.

Qu'est-ce que video-skill ?

Cette compétence permet à un agent IA de manipuler des vidéos de bout en bout sans pouvoir les visionner directement. Chaque étape transforme le contenu vidéo en éléments lisibles : trames à observer et nombres à vérifier. La création d'un film suit le format OpenFilm (HTML) avec des commandes pour ouvrir, inspecter (`look`) et rendre (`render`) des productions. Les métadonnées audio et vidéo sont analysées via `footage.py` (probe, proxy, frames, check, share) qui détecte et corrige les problèmes courants : HDR, rotation, trames variables, dimensions impaires. Le résumé audio est réalisé avec `parakeet-mlx` pour des transcriptions horodatées. La livraison inclut une vérification de conformité (codec, loudness, faststart) et une optimisation (WebP plutôt que GIF). La compétence est spécifiquement conçue pour fonctionner sur macOS et utilise OpenFilm comme outil principal, avec des alternatives mentionnées (HyperFrames, Remotion, Manim, MoviePy).

Compatible avec✓Claude Code~Codex CLI~Cursor
npx skills add https://github.com/kylefritz/video-skill/tree/HEAD

Demander à votre IA préférée

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

Documentation

Que fait video-skill ?

Les scripts se trouvent dans scripts/ à côté de ce fichier ; écrivez-le comme $S ci-dessous, mais indiquez le chemin absolu dans chaque commande, car les variables shell ne sont pas conservées entre les appels Bash.

Vous ne pouvez pas regarder de vidéo, donc chaque étape la transforme en quelque chose de lisible : des trames que vous regardez et des nombres que vous vérifiez. Réaliser un film, c'est écrire → rendre les trames → lire la planche-contact → corriger. Comprendre des images, c'est sonder → proxy → planches horodatées + transcription → juger. Livrer, c'est un script qui vérifie le codec, la fréquence d'images, faststart et la sonie, pour que « ça marche partout » soit un fait.

Première fois sur Mac

Exécutez sh $S/setup.sh vous-même. Il vérifie mise et ffmpeg (installe ffmpeg via Homebrew si nécessaire), installe OpenFilm dans sa version épinglée lors de la première utilisation et lance openfilm doctor. Corrigez chaque TODO qu'il affiche, ou expliquez-le clairement à la personne. Sans danger de le relancer.

Réaliser un film : OpenFilm

OpenFilm (MIT) est le format d'édition : un film est un dossier contenant un film.html qui assemble des pages HTML, des images, des vidéos et du son, chaque page dessinant tout moment t demandé. Studio est son éditeur local dans le navigateur.

  1. Imprimez le manuel et lisez-le en entier (il est court et change entre les versions) : $S/openfilm. Suivez-le, ce fichier prévalant en cas de divergence.
  2. $S/openfilm est la seule façon d'exécuter OpenFilm ici : il épingle la version, l'installe via mise à la première utilisation, et empêche Studio d'ouvrir une fenêtre de navigateur. N'utilisez pas npx openfilm (contourne l'épingle) ni npx skills add openfilm/openfilm (une compétence en amont qui entre en concurrence avec celle-ci).
  3. FILM est toujours un chemin absolu, $HOME/Movies/OpenFilm/Projects/<nom-du-film>. Un nom relatif atterrit dans le dossier courant, par exemple un dépôt git. Les films ne sont jamais dans un dépôt.
ÉtapeCommande
Créer ou ouvrir$S/openfilm open FILM
Vérifier tout le film$S/openfilm -C FILM look
Vérifier un instant, une plage, un fichier… look 4.5, look 3-6, look assets/bed.wav
Vérifier une entrée rapide… look 0-2 --count 24
Rendre le MP4$S/openfilm -C FILM render
Quels services média sont connectés$S/openfilm get
Diagnostiquer l'installation$S/openfilm doctor

look et render s'exécutent sans Studio ouvert et impriment les chemins relatifs à FILM. L'adresse de Studio contient une clé d'accès : ne la donnez qu'à la personne avec laquelle vous travaillez (c'est 127.0.0.1, Chrome ou Edge, pas depuis un téléphone).

Lisez chaque planche look avant de montrer quoi que ce soit, et corrigez ce que vous voyez. Ses vérifications (le même t dessine la même image, polices chargées, texte rogné, sonie) ne disent rien sur le goût ou la disposition. Un ! concernant un texte coupé pendant une entrée est acceptable ; coupé au repos est un bug. Regardez ensuite quelques moments en pleine taille (look 12.5) : une vignette de 300 px cache les petits détails, y compris les personnes.

Comment un film est construit, appris en en réalisant un :

  • Une page par plan ; ce que les pages partagent (un mouvement Ken Burns, polices, couleurs) va dans un module qu'elles importent. Chaque valeur est une fonction de t uniquement : pas de minuteurs, pas de boucle d'animation, pas d'état.
  • Les pages se fondent elles-mêmes (opacité depuis t) ; un fondu au noir entre deux plans sur une même piste est la transition la plus simple. Un fondu enchaîné nécessite que le plan entrant soit sur une piste au-dessus du plan sortant.
  • Le texte sur des photos a besoin d'une ombre derrière lui (un dégradé en bas), sinon il disparaît dans les scènes chargées.
  • Les polices vont dans FILM/fonts/ avec @font-face, attendues via window.film.ready (document.fonts.ready). Pour les photos, ready: img.decode().
  • Rognez avec #t=debut,fin, jamais en découpant de nouveaux fichiers ; un son plus long que le film étend le film.

Son

$S/openfilm get montre ce qui est connecté ; la voix générée, la musique et les effets nécessitent les propres clés du fournisseur de la personne (Studio : logo OpenFilm → Paramètres → Fournisseurs). Sans elles, créez du son avec ffmpeg, sauvegardez-le sous FILM/assets/, gardez la commande dans FILM/sound-notes.txt pour pouvoir la reconstruire, et dites quel service ferait mieux. Vous ne pouvez pas l'entendre, donc jugez par les nombres :

  • -16 LUFS intégré pour un mix final (podcasts, la plupart des vidéos web) ; les plateformes de streaming normalisent autour de -14. Gardez le pic vrai à ou sous -1 dBTP. Un fond sonore sous une voix se situe ~ -22.
  • Mesure : ffmpeg -i X -af ebur128=peak=true -f null -. Normalisez un fond généré avec loudnorm=I=-16:TP=-1.5:LRA=7 ; pour un master final, exécutez loudnorm deux fois (mesurez, puis renvoyez les valeurs measured_*) pour que le gain soit linéaire.
  • look imprime la sonie du mix, et footage.py check signale un rendu qui manque la cible.

Images qu'on vous a confiées : réception

Les vidéos de téléphone sont rarement ce qu'elles semblent être. Exécutez ceci sur tout fichier avant de le regarder ou de le couper :

uv run --no-project --with pillow python -I $S/footage.py probe CLIP.mov ...
uv run --no-project --with pillow python -I $S/footage.py proxy CLIP.mov <scratch>/clip.mp4

probe avertit pour chacun de ces points, et proxy les corrige tous en un seul passage (SDR BT.709, rotation intégrée, 30 ips constant, taille paire, H.264 yuv420p, AAC 48 kHz, faststart) :

  • HDR (les iPhones enregistrent en HLG par défaut : color_transfer=arib-std-b67, BT.2020). Lu en SDR, cela semble délavé et décale les couleurs : le vermillon devient orange. proxy mappe les tons avec avconvert intégré à macOS, sinon ffmpeg zscale (manquant dans de nombreuses versions Homebrew), sinon avertit.
  • Rotation : les téléphones stockent les vidéos en portrait de travers avec un indicateur de rotation ; certains outils l'ignorent.
  • Fréquence d'images variable : nominale 24, moyenne 23,997. Les temps des trames dérivent et l'audio se désynchronise après les coupes. probe signale tout écart supérieur à 0,001 ips.
  • Dimensions impaires : H.264 yuv420p nécessite une largeur et une hauteur paires.

Regardez et coupez à partir du proxy ; conservez l'original pour tout ce qui doit rester en pleine qualité.

Comprendre des images

uv run --no-project --with pillow python -I $S/footage.py frames <scratch>/clip.mp4 <scratch>/sheets --every 4

Cela prend une trame à chaque changement de scène (--scene 0.3) et au moins une toutes les --every secondes, plafonnée à --max 60, sur des planches de 5×6 vignettes étiquetées 0007 · 1:23.4, avec manifest.json mappant chaque ID à son temps. Lisez les planches et répondez avec les ID et les temps. Pratique, pas doctrine :

  • Films créés : échantillonnage uniforme à la longueur du plan. Images trouvées : changements de scène plus un minimum d'une trame toutes les 2-5 s. Action rapide : un court segment à --every 0,5.
  • Gardez une planche sous ~2 500 px sur son côté long et lisez-en quelques-unes à la fois ; pour de longues images, déléguez à des sous-agents sur les planches, chacun écrivant un verdict JSONL par ID de vignette, puis python3 -I $S/check_verdicts.py manifest.json out/*.jsonl (les ID manquants, en double ou inventés échouent ; une planche où chaque vignette a reçu la même réponse a probablement été survolée).
  • La majeure partie d'une vidéo parlée est dans son audio. Transcrivez sur l'appareil avec les temps des mots : uvx parakeet-mlx AUDIO.wav --output-format json --output-dir <scratch> (extrayez d'abord l'audio : ffmpeg -i clip.mp4 -ar 16000 -ac 1 audio.wav). Joignez les mots aux trames par le temps. Les noms propres sont souvent mal retranscrits (« torii » → « Torrey »), donc vérifiez les noms par rapport à ce que vous voyez avant de les citer.

Livraison

uv run --no-project --with pillow python -I $S/footage.py check FILM/<nom>.mp4 --duration 30
uv run --no-project --with pillow python -I $S/footage.py share FILM/<nom>.mp4 <scratch>/<nom>-share.mp4
uv run --no-project --with pillow python -I $S/footage.py gif FILM/<nom>.mp4 <scratch>/loop.webp --start 2.5 --duration 6 --width 480
  • check doit réussir avant de remettre un fichier : H.264, yuv420p, taille paire, fréquence d'images constante, moov avant mdat (faststart : lecture pendant le téléchargement), durée, sonie ±1,5 LU, pic vrai.
  • Rendez quand la personne demande le fichier ou ne peut pas être sur ce Mac ; exécutez look sur le MP4 lui-même comme dernière vérification. share crée une petite copie (1280 de large, CRF 28 : un rendu 1080p de 30 s est passé de 34 Mo à 3,8 Mo) pour une page web ou un chat ; le MP4 du film reste intact.
  • Boucles : WebP animé plutôt que GIF. Une boucle photo de 5,6 s avec un zoom lent faisait 6,7 Mo en GIF et 1,6 Mo en WebP : le GIF ne peut pas réutiliser les pixels quand chaque pixel bouge. GIF uniquement lorsque la cible ne peut pas afficher WebP ; alors footage.py gif X.gif utilise une palette générée (palettegen/paletteuse).
  • Supprimez ce qui ne doit pas voyager : les vidéos iPhone transportent le GPS, le modèle du téléphone et l'heure dans leurs métadonnées. proxy et share les suppriment toutes (-map_metadata -1). Le fichier original, ou une copie faite par AirDrop ou une exportation Photos, les conserve. Vérifiez avec exiftool -GPSCoordinates FICHIER.

Photos dans un film

Utilisez la compétence photo-skill (github.com/kylefritz/photo-skill ; si elle n'est pas installée, proposez de la cloner dans ~/.claude/skills/photo-skill) pour les trouver (recherchez la bibliothèque par description, puis jugez les planches-contact) et pour télécharger les originaux en pleine résolution : les aperçus sont trop petits pour du 1080p. Copiez ce que le film utilise dans FILM/assets/, ré-enregistré sans EXIF. Avant qu'un film avec les photos de quelqu'un aille quelque part, regardez chaque plan en pleine taille : la détection de visages rate les foules, et une vignette de 300 px cache des personnes qu'une trame 1080p montre.

Autres outils

OpenFilm est ce qui convient le mieux à un agent car un film est du HTML pur et look renvoie exactement ce que vous devez voir. Alternatives, à fin 2026 :

OutilÉditionNotes
HyperFrames (HeyGen, Apache-2.0)HTML/CSS + GSAP, rendu par headless Chromel'analogue le plus proche ; vérifications check --json lint/layout/contraste, instantanés, un plugin Claude Code
RemotionComposants Reactmature, compétences officielles d'agent ; licence source-disponible, les entreprises au-dessus d'une petite taille en ont besoin d'une
Manim (MIT)Scènes Pythonexplications mathématiques et de diagrammes
MoviePy (MIT) / ffmpeg brutPython / filtergraphscoupes et superpositions simples ; ffmpeg est préférable comme couche d'E/S

Mise à niveau d'OpenFilm

L'amont publie souvent. Lisez les notes de version et exécutez d'abord npm view openfilm scripts (une entrée preinstall ou postinstall mérite un coup d'œil), puis changez OPENFILM_VERSION dans scripts/openfilm et relancez setup.sh. Ensuite ls ~/Library/Caches/ms-playwright : le premier lancement d'OpenFilm supprime les navigateurs Playwright qui n'ont pas de marqueur de lien ; setup.sh signale ceux qu'il a supprimés, et npx playwright install chromium dans le projet qui en a besoin le remet.

Erreurs courantes

ErreurCorrectif
npx openfilm …$S/openfilm … : npx contourne l'épingle
look ou render dans le mauvais dossier$S/openfilm -C FILM …
Faire confiance aux vérifications de look sans ouvrir son PNGLisez la planche ; une vérification réussie ne dit rien sur la disposition
Couper ou regarder directement des vidéos iPhoneprobe, puis proxy : HDR, rotation et VFR posent tous problème
Envoyer le MP4 maître dans un chatshare-le, puis check la copie
Un GIF pour un README ou une pageWebP, ~4× plus petit
Studio périmé après une mise à niveauArrêtez les anciens processus studio/server/main.mjs (pgrep -fl openfilm) ; un Studio, c'est deux processus

Skills associés