Communitygithub.com

eternityspring/reelbench-skills

Reconstruit une vidéo en un fichier propre composé uniquement de l'image et du son – **toutes les métadonnées de la source sont abandonnées** : GPS, modèle d'appareil, ID de compte, date de création, chapitres, pistes télémétriques GoPro, tout reste sur place. Utilise une liste blanche plutôt qu'une liste noire : on ne dit pas ce qu'il faut supprimer, on indique seulement ce qu'il faut emporter (un flux image, un flux audio). La garantie de confidentialité vient de la structure, pas de l'énumération. La difficulté ne réside pas dans les tags du conteneur, mais dans **les couches invisibles** : x264 inscrit les paramètres d'encodage complets dans le SEI du flux, AAC écrit le numéro de version dans le DSE, avc1 en contient une copie dans `compressorName` – `ffprobe` ne voit rien de tout cela. Cinq cachettes sont colmatées une par une, chacune avec une vérification pratique. Par défaut `--mode copy` : **l'image est copiée octet par octet** (vérifié avec `cmp`), seul l'audio est ré-encodé, une vidéo de 53 secondes est traitée en 1,3 seconde ; `--mode encode` effectue un ré-encodage complet, éliminant davantage de données au niveau du flux. La validation ne repose pas sur des affirmations : toutes les chaînes de métadonnées de la vidéo source sont utilisées comme « aiguilles » pour une **recherche au niveau octet** dans le fichier de sortie – la moindre piqûre fait échouer le test. Les 12 vérifications sont toutes effectuées de manière déterministe par le script. Zéro dépendance, zéro clé API, seulement Node et ffmpeg. À utiliser quand on demande : nettoyer les métadonnées, supprimer les métadonnées, effacer les informations de la vidéo, confidentialité de la vidéo, supprimer les filigranes d'information, supprimer les métadonnées d'une vidéo, retirer les EXIF d'une vidéo, nettoyer une vidéo.

Qu'est-ce que reelbench-skills ?

Cette compétence fournit un script Node.js (`video-scrub.mjs`) qui reconstruit une vidéo en un fichier propre contenant uniquement les flux vidéo et audio, en éliminant **toutes** les métadonnées de la source. Contrairement aux approches par liste noire (qui suppriment des champs connus mais peuvent en oublier), elle utilise une **liste blanche** : seuls les deux flux principaux sont conservés, excluant ainsi de manière structurelle toute métadonnée de conteneur, piste de données (GPS, télémétrie), chapitres, etc. La difficulté principale réside dans les métadonnées incrustées dans les flux encodés eux-mêmes, invisibles pour `ffprobe` : les paramètres x264 dans le SEI du H.264, le numéro de version du codec dans le DSE de l'AAC, et le `compressorName` de la boîte avc1. Le script les supprime ou les ré-encode pour éliminer ces traces. Il supporte deux modes : `copy` (par défaut) qui recopie l'image octet par octet et ne ré-encode que l'audio pour une vitesse maximale, et `encode` qui ré-encode complètement pour éliminer davantage de données (filigranes fragiles, données de flux). Un système de validation intégré effectue une **recherche au niveau octet** de toutes les chaînes de métadonnées de la source dans le fichier de sortie, garantissant qu'aucune information résiduelle n'est présente. La compétence est compatible avec les vidéos MP4/MOV, fonctionne sans dépendances externes (sauf Node.js et ffmpeg), et peut être utilisée via une commande unique (`run`), une inspection préalable (`inspect`), une opération de nettoyage (`scrub`), ou une vérification post-nettoyage (`verify`).

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

Demander à votre IA préférée

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

Documentation

video-scrub

Reconstruit une vidéo en un fichier propre : l'image et le son sont conservés, pas un seul octet de métadonnées n'est transféré.

{baseDir} = répertoire contenant ce fichier. Script {baseDir}/scripts/video-scrub.mjs, zéro dépendance, s'exécute directement avec node.

Principe : liste blanche, pas liste noire

Il existe deux approches pour nettoyer les métadonnées, la différence est cruciale :

Comment ça marcheProblème
Liste noireOn liste les champs à supprimer un par un (comme avec exiftool)On ne peut pas supprimer ce qu'on ignore – les udta propriétaires, les pistes télémétriques GoPro, les SEI dans le flux, un oubli et c'est une fuite
Liste blancheOn ne dit pas ce qu'il faut supprimer, on indique ce qu'il faut emporter : un flux image, un flux audio, rien d'autre—

Avec la liste blanche, les métadonnées de la source n'ont aucun canal pour entrer. Elles ne sont pas « supprimées », elles n'ont jamais été transportées.

Mais ffmpeg trahit par défaut cette structure à deux reprises, il faut le brider :

  1. Il copie par défaut les tags du conteneur source vers la sortie → -map_metadata -1
  2. Il sélectionne automatiquement les flux, et emportera la piste télémétrique gpmd de GoPro → -map 0:v:0 -map 0:a:0?

Le point 2 est la seule défense pour les sources comme GoPro / DJI : leur GPS n'est pas dans les tags, mais dans une piste de métadonnées temporelles indépendante. Supprimer les tags ne suffit pas, seul le fait de « ne sélectionner que deux flux » bloque cela.

La vraie difficulté : les couches invisibles

En bridant les deux points ci-dessus, la couche des tags est propre, ffprobe est silencieux. Mais le fichier contient encore trois chaînes d'identité :

Caché dansContenuVisible par ffprobe
SEI du H.264Chaîne de paramètres complète de x264 cabac=1 ref=3 … crf=23.0✗
DSE du flux AACLavc62.28.102✗
compressorName d'avc1Lavc libx264✗

Les cinq cachettes et leurs méthodes de colmatage respectives sont détaillées dans {baseDir}/references/residue-map.md – chaque méthode est accompagnée d'un enregistrement de test pratique, y compris les pièges « qui semblaient devoir fonctionner mais n'ont pas marché » (ni -flags:v +bitexact ni -x264-params info=0 ne bloquent le SEI).

Deux modes

ModeImageAudioVitesseÉlimine en plus
copy (par défaut)Copie octet par octetRé-encodé53 secondes de vidéo traitées en 1,3 s—
encodeRé-encodéRé-encodéBeaucoup plus lentFiligranes de flux, stéganographie fragile

L'audio est ré-encodé dans les deux modes – le numéro de version de l'AAC est écrit dans le DSE, qui se trouve à l'intérieur du flux, -c:a copy ne peut pas l'effacer. Le ré-encodage est peu coûteux et garantit une piste audio propre. En mode copy, l'image n'est vraiment pas modifiée d'un seul bit (vérifié avec cmp, hormis le SEI retiré).

À propos des filigranes : Le mode encode élimine en plus les filigranes de flux et la stéganographie fragile, rares dans les contenus réels. Les filigranes pixel robustes vraiment dangereux (SynthID, filigranes médico-légaux) sont conçus pour résister au ré-encodage, les deux modes sont impuissants. Choisissez donc le mode en fonction de la qualité et de la vitesse, n'espérez pas qu'un ré-encodage efface un filigrane.

Quatre profils

La couche qui est réécrite après le nettoyage :

ProfilCe qui est écrit
obs (par défaut)Aspect d'OBS Studio. OBS utilise déjà ffmpeg pour l'encapsulation, donc ce profil n'est pas une falsification, mais le maintien d'une apparence normale d'outil similaire
quicktimeCapture d'écran macOS QuickTime : Core Media Video / Core Media Audio
screencaptureCapture d'écran ScreenCaptureKit
bareRien n'est écrit. Confidentialité maximale – aucune déclaration falsifiable laissée

Une chose impossible à faire, mentionnée ici pour éviter d'autres essais : le champ encoder du conteneur est occupé par le muxer lui-même, -metadata encoder=Lavf60.16.100 ne peut pas le remplacer, même le vider est impossible (testé). Seul -fflags +bitexact peut le faire disparaître complètement. Donc le profil obs ne falsifie pas le numéro de version, il laisse ffmpeg écrire sa propre version vraie.


Étape 0 — Voir d'abord ce que la source contient

node {baseDir}/scripts/video-scrub.mjs inspect <video>

Affiche en un écran tout le contenu : tags du conteneur, tags de chaque flux et side data, chapitres, ainsi que les chaînes d'identité dans le flux invisibles pour ffprobe. À la fin, il indique combien d'« aiguilles » seront utilisées pour la validation et si la coupure du SEI sera effectuée.

Regardez cet écran avant d'agir. Portez une attention particulière à deux choses :

  • Y a-t-il des flux data / timecode – si oui, la source peut contenir une piste télémétrique (GoPro, DJI)
  • Est-ce du HDR – si oui, les SEI ne sont jamais supprimés (explication ci-dessous)

Étape 1 — Nettoyage

node {baseDir}/scripts/video-scrub.mjs scrub <video> -o out.mp4

Par défaut --mode copy --profile obs. Paramètres courants :

--mode encode          # Ré-encodage complet
--profile bare         # Rien n'est écrit, confidentialité maximale
--crf 18               # Significatif seulement en mode encode
--date 2026-01-01T00:00:00Z   # Spécifie la date de création, par défaut l'heure actuelle

Étape 2 — Validation ⛔ Ne pas sauter

node {baseDir}/scripts/video-scrub.mjs verify <source> <sortie>

Ou en une seule commande (à utiliser au quotidien) :

node {baseDir}/scripts/video-scrub.mjs run <video> -o out.mp4

Le cœur de la validation est simple : tout ce qui précède est « j'ai réglé les bons paramètres », c'est une affirmation ; cette étape est « j'ai scanné, c'est vraiment parti », c'est une preuve.

La méthode consiste à extraire toutes les chaînes de métadonnées de la vidéo source comme des « aiguilles » – les clés et valeurs des tags, les noms de handlers, les titres de chapitres – et à effectuer une recherche au niveau octet dans le fichier de sortie. La moindre piqûre fait échouer le test.

Deux considérations d'implémentation :

  • Les aiguilles doivent avoir ≥5 octets. Des chaînes plus courtes pourraient générer des faux positifs aléatoires dans les données compressées. Les aiguilles ignorées sont listées dans les messages.
  • Les chaînes génériques ne sont pas des aiguilles. VideoHandler existe partout, son information est nulle ; Lavf58.45.100 pointe vers une version spécifique, c'est une empreinte. La frontière est « peut-on réduire la portée à une personne, un appareil, une exportation spécifique ? ».

Les 12 vérifications

VérificationCe qui est bloqué
Résidu au niveau octetUne chaîne de métadonnées de la source se retrouve dans la sortie
Résidu au niveau des tagsLa valeur d'un tag de la sortie provient de la source
Information de localisationTout champ GPS / location
Identifiants d'appareil et logicielmake / model / software / artist / comment, etc.
Horodatage de la sourceLe creation_time de la sortie est égal à celui de la source
Matrice de rotationEn mode encode, la rotation n'est pas cuite dans l'image (ignoré en mode copy – la supprimer rendrait l'image de travers)
Composition des fluxUn flux data / timecode / pièce jointe s'est glissé dedans
ChapitresLes chapitres ont été transportés
Chaîne d'identité du fluxSEI / DSE / compressorName contient encore le nom du codec
Conformité au profilLes tags restants ne correspondent pas au profil déclaré
DuréeLa sortie a été tronquée sans le dire
DécodabilitéLe fichier produit est corrompu et ne peut pas être ouvert

HDR et sous-titres incrustés : quand ne pas toucher au SEI

ffmpeg ne permet qu'une granularité au niveau NAL – filter_units supprime par type NAL, h264_metadata n'a pas d'option pour « supprimer seulement un certain payload SEI ». Donc remove_types=6 supprime tous les SEI en une fois. Or, dans les SEI, en plus de la chaîne de paramètres inutile de x264, on trouve :

  • Les métadonnées statiques HDR (mastering display / content light level) – les supprimer entraîne une perte de couleur, le tone mapping est cassé
  • Les sous-titres incrustés CEA-608/708 – les supprimer fait disparaître les sous-titres

La stratégie du script est donc : ne supprimer les SEI que si une chaîne d'identité est effectivement détectée, et jamais sur une source HDR (le script indique clairement qu'il n'a pas agi et pourquoi). Cette décision est prise par seiPlan(), vous n'avez pas à vous en soucier, mais vous devez savoir qu'elle existe.

Limites (ce qui n'est pas fait)

Ne supprime pas les filigranes (les filigranes pixel robustes résistent au ré-encodage, n'y comptez pas), ne modifie pas le contenu de l'image (pas de recadrage, pas de redimensionnement, pas de rotation), ne falsifie pas le modèle d'appareil ni le GPS (les profils n'écrivent que des informations d'outil, pas d'appareil de prise de vue ni de localisation), ne fait pas de scan de répertoire (une vidéo à la fois), ne traite pas les conteneurs autres que mp4/mov.

Auto-test

node {baseDir}/scripts/selftest.mjs

117 assertions, ne touche pas à ffmpeg, ne touche pas à de vrais fichiers. Chacune des 12 vérifications a un cas de test de contournement – prouvant qu'elle bloque réellement. Exécutez ce test après toute modification du script.

Individual skills in this repo

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

eternityspring/reelbench-skills

拉片:把一条成片拆成逐镜头的分析表——每个镜头的时长、景别、类别、运镜、画面。 分工刻在骨子里:**能量的都由代码量**(切点来自 ffmpeg 场景检测,时长是切点相减, 运动量是逐帧差分的中位数),模型只判断它真正该判断的那几件事(景别 / 类别 / 运镜 / 画面 / 节奏), 然后每一条判断都被代码当场对账——**声称推拉摇移却实测几乎不动,门直接拦**。 看片走联系表(每镜起手帧 + 收尾帧各拼一张大图,一屏二十几个镜头,a/b 对照就是运镜), 不是一张张翻。检测漏刀多刀用 recut 补刀并刀,自动重编号重算时长,手改边界过不了门。 产出 shots.json + Markdown 镜头表 + **单页交互式拉片报告**:内嵌播放器(播放时同步高亮镜头、 点镜头跳转)、镜头节奏带、可搜索可筛选可排序的镜头表(列表 / 卡片两种视图、首尾关键帧并排、 点图开大图)、景别类别运镜分布、出场人物、质量门、导出 JSON。单文件零依赖,离线双击能开。 15 道质量门全部由脚本确定性检查。 零依赖、零 API key,只要 node 和 ffmpeg。 Use when asked to 拉片、拆镜头、分析视频镜头、镜头时长、景别、运镜、镜头表、 video shot breakdown、shot list from video。

eternityspring/reelbench-skills

把拉片数据和原片合成一条**能直接看的视频**:一边是画面,一边是这一镜的分镜信息 (镜号、起止、时长、景别、类别、运镜、画面描述、台词),**镜头切了信息跟着切**, 镜头表自动滚动并高亮当前这一镜。 版式只看原片的宽高比:**横版 / 方版 → 画面在上、信息在下;竖版 → 画面在左、信息在右**, 画面永远原样缩放,不裁不拉。 镜头表**随播放滚动**:切点处滚一小段把当前镜头带到锚点、随后停住,高亮条同步滑过去。 实现上只截三张图(底板 + 铺开的长图 ×2),滚动与高亮由 ffmpeg 按时间裁窗, **布局全在 scripts/panel.css 里,改它就能改版式**,不用碰脚本。 吃 video-shots 产出的 shots.json(有 frames/ 就把关键帧当缩略图用)。 零 npm 依赖,用 ffmpeg 合成、无头浏览器渲面板。 Use when asked to 导出视频、合成视频、分镜视频、带分镜信息的视频、解说版视频、 video with shot info、annotated shot video。

Skills associés