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.
- 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. $S/openfilmest 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 pasnpx openfilm(contourne l'épingle) ninpx skills add openfilm/openfilm(une compétence en amont qui entre en concurrence avec celle-ci).FILMest 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.
| Étape | Commande |
|---|---|
| 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
tuniquement : 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 viawindow.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é avecloudnorm=I=-16:TP=-1.5:LRA=7; pour un master final, exécutezloudnormdeux fois (mesurez, puis renvoyez les valeursmeasured_*) pour que le gain soit linéaire. lookimprime la sonie du mix, etfootage.py checksignale 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.proxymappe les tons avecavconvertintégré à macOS, sinon ffmpegzscale(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.
probesignale 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
checkdoit réussir avant de remettre un fichier : H.264, yuv420p, taille paire, fréquence d'images constante,moovavantmdat(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
looksur le MP4 lui-même comme dernière vérification.sharecré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.gifutilise 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.
proxyetshareles suppriment toutes (-map_metadata -1). Le fichier original, ou une copie faite par AirDrop ou une exportation Photos, les conserve. Vérifiez avecexiftool -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 | Édition | Notes |
|---|---|---|
| HyperFrames (HeyGen, Apache-2.0) | HTML/CSS + GSAP, rendu par headless Chrome | l'analogue le plus proche ; vérifications check --json lint/layout/contraste, instantanés, un plugin Claude Code |
| Remotion | Composants React | mature, 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 Python | explications mathématiques et de diagrammes |
| MoviePy (MIT) / ffmpeg brut | Python / filtergraphs | coupes 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
| Erreur | Correctif |
|---|---|
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 PNG | Lisez la planche ; une vérification réussie ne dit rien sur la disposition |
| Couper ou regarder directement des vidéos iPhone | probe, puis proxy : HDR, rotation et VFR posent tous problème |
| Envoyer le MP4 maître dans un chat | share-le, puis check la copie |
| Un GIF pour un README ou une page | WebP, ~4× plus petit |
| Studio périmé après une mise à niveau | Arrêtez les anciens processus studio/server/main.mjs (pgrep -fl openfilm) ; un Studio, c'est deux processus |