Communitygithub.com

eternityspring/reelbench-skills

Reconstruye un video en un archivo limpio que solo contiene imagen y sonido, **sin ningún metadato del original**: GPS, modelo de dispositivo, ID de cuenta, marca de tiempo, capítulos, pistas de telemetría de GoPro… todo se queda donde estaba. Funciona con lista blanca en lugar de lista negra: no dice qué eliminar, solo qué conservar (un flujo de imagen, un flujo de sonido). La garantía de privacidad proviene de la estructura, no de una enumeración.

¿Qué es reelbench-skills?

El skill **video-scrub** de eternityspring/reelbench-skills es una herramienta de línea de comandos diseñada para eliminar por completo los metadatos de un archivo de video, reconstruyéndolo en un archivo limpio que solo contiene pistas de video y audio. A diferencia de enfoques tradicionales basados en listas negras (que intentan eliminar campos específicos de metadatos), esta herramienta utiliza una **lista blanca**: solo mapea y copia las pistas deseadas (una de video, una de audio), evitando así que cualquier metadato, incluso los invisibles para `ffprobe`, pueda filtrarse al archivo de salida. Es compatible con plataformas que usan Node.js y FFmpeg, sin dependencias externas ni API keys. Su funcionamiento se basa en tres capas de protección: primero, suprime metadatos de contenedor con `-map_metadata -1`; segundo, selecciona solo las pistas principales con `-map 0:v:0 -map 0:a:0?` para evitar pistas ocultas como telemetría; tercero, ataca cinco puntos ciegos donde se esconden identificadores de software: SEI en H.264, DSE en AAC, compressorname en avc1, y metadatos de capítulo y datos de usuario. El modo por defecto (`--mode copy`) copia el video byte a byte (verificado con `cmp`) y re-codifica solo el audio, logrando una velocidad de 1,3 segundos para un clip de 53 segundos. El modo `--mode encode` realiza una recodificación completa, eliminando adicionalmente marcas de agua en el flujo de bits y esteganografía frágil. La verificación es rigurosa y determinista: toma todas las cadenas de metadatos del video original como

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

Preguntar en tu IA favorita

Abre un nuevo chat con esta habilidad de agente ya precargada.

Documentación

video-scrub

Reconstruye un video en un archivo limpio: la imagen y el sonido se transfieren, pero no un solo byte de metadatos.

{baseDir} = directorio donde se encuentra este archivo. El script {baseDir}/scripts/video-scrub.mjs, sin dependencias, se ejecuta directamente con node.

Principio: lista blanca, no lista negra

Hay dos enfoques para limpiar metadatos, y la diferencia es de vida o muerte:

Cómo se haceProblema
Lista negraSe enumeran los campos a eliminar uno por uno (como con exiftool)No puedes eliminar lo que no sabes que existe — udta privado del fabricante, pistas de telemetría de GoPro, SEI en el flujo de bits; uno que se pase, uno que se pierde
Lista blancaNo se dice qué eliminar, sino qué llevar: un flujo de imagen, un flujo de sonido, nada más—

Con lista blanca, los metadatos del video original no tienen ningún conducto para entrar. No es que "se eliminen", es que nunca se transportan.

Pero ffmpeg traicionaría esta estructura por defecto dos veces, hay que sujetarlo:

  1. Por defecto copia las etiquetas del contenedor original a la salida → -map_metadata -1
  2. Por defecto selecciona flujos automáticamente, arrastrando consigo la pista de telemetría gpmd de GoPro → -map 0:v:0 -map 0:a:0?

El punto 2 es la única defensa para videos tipo GoPro/DJI: su GPS no está en las etiquetas, está en una pista independiente de metadatos temporales. Limpiar etiquetas no lo elimina; solo "seleccionar solo dos flujos" lo detiene.

El verdadero desafío: las capas invisibles

Con lo anterior controlado, la capa de etiquetas queda limpia, y ffprobe muestra silencio. Pero aún hay tres identificadores ocultos en el archivo:

Dónde se escondeContenido¿Lo ve ffprobe?
SEI de H.264Cadena completa de parámetros x264 cabac=1 ref=3 … crf=23.0✗
DSE del flujo AACLavc62.28.102✗
compressorname de avc1Lavc libx264✗

Los cinco escondites y cómo taparlos están en {baseDir}/references/residue-map.md — cada uno tiene registro de pruebas reales, incluyendo varias "trampas" que parecen funcionar pero no (-flags:v +bitexact y -x264-params info=0 no detienen el SEI).

Dos modos

ModoVideoAudioVelocidad¿Qué más elimina?
copy (por defecto)Copia byte a byteRecodifica53 s de video en 1.3 s—
encodeRecodificaRecodificaMucho más lentoMarcas de agua en flujo de bits, esteganografía frágil

El audio se recodifica en ambos modos — el número de versión de AAC está escrito dentro del DSE, que está en el flujo de bits; -c:a copy no puede eliminarlo. Recodificarlo es barato y deja la pista de audio limpia. En el modo copy, el video no se toca ni un bit (verificado con cmp, excepto por el SEI eliminado).

Sobre marcas de agua: encode elimina más marcas de agua en el flujo de bits y esteganografía frágil, que son raras en material real. Las marcas de agua de píxel robustas (SynthID, marcas forenses de video) están diseñadas para resistir la recodificación; ningún modo las elimina. Así que elige el modo por calidad y velocidad, no esperes que recodificar elimine las marcas de agua.

Cuatro perfiles

Después de limpiar, se escribe la capa de salida:

Perfil¿Qué escribe?
obs (por defecto)Apariencia de OBS Studio. OBS ya usa ffmpeg internamente, así que esto no es una falsificación, sino mantener la apariencia normal de una herramienta similar
quicktimeGrabación de pantalla de macOS QuickTime: Core Media Video / Core Media Audio
screencaptureGrabación de pantalla de ScreenCaptureKit
bareNo escribe nada. Máxima privacidad — no deja ninguna declaración que pueda ser verificada

Hay algo que no se puede hacer, y se menciona aquí para que no se intente: el campo encoder del contenedor es ocupado por el propio muxer, -metadata encoder=Lavf60.16.100 no lo sobrescribe (ni siquiera vaciarlo lo elimina, probado). Solo -fflags +bitexact lo hace desaparecer por completo. Así que el perfil obs no falsifica números de versión; simplemente deja que ffmpeg escriba su propia versión real.


Paso 0 — Examina primero qué trae el video original

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

Muestra todo en una pantalla: etiquetas del contenedor, etiquetas y side data de cada flujo, capítulos, y las cadenas de identidad dentro del flujo de bits que ffprobe no ve. Al final te dirá cuántas "agujas" se usarán en la verificación y si se tocará el SEI.

Mira esta pantalla antes de actuar. Presta especial atención a:

  • Si hay flujos data / timecode — si los hay, el video original puede tener pistas de telemetría (GoPro, DJI)
  • Si es HDR — en ese caso los SEI no se eliminan (explicado más abajo)

Paso 1 — Limpia

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

Por defecto --mode copy --profile obs. Parámetros comunes:

--mode encode          # Recodificación completa
--profile bare         # No escribe nada, máxima privacidad
--crf 18               # Solo tiene sentido en modo encode
--date 2026-01-01T00:00:00Z   # Especifica la marca de tiempo de creación; si no se da, se usa la actual

Paso 2 — Verificación ⛔ No se puede omitir

node {baseDir}/scripts/video-scrub.mjs verify <original> <salida>

O todo en uno (úsalo a diario):

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

El núcleo de la verificación es una frase: todo lo anterior es "he ajustado los parámetros correctos", que es una afirmación; este paso es "lo he escaneado, realmente no está", que es una prueba.

El método consiste en extraer todas las cadenas de metadatos del video original como "agujas" — claves y valores de etiquetas, nombres de handlers, títulos de capítulos — y escanear byte a byte el archivo de salida en busca de ellas. Si se clava una, se marca en rojo.

Dos detalles de implementación:

  • Las agujas deben tener ≥5 bytes. Cadenas más cortas pueden causar falsos positivos aleatorios en datos comprimidos; las agujas omitidas se listan en el mensaje.
  • Las cadenas genéricas no cuentan como agujas. VideoHandler está por todas partes, aporta cero información; Lavf58.45.100 fija una versión específica, es una huella. La línea divisoria es "¿puede esta cadena reducir el alcance a una persona, un dispositivo, una exportación específica?".

12 puertas

Puerta¿Qué bloquea?
Residuos a nivel de byteCadenas de metadatos del original encontradas en la salida
Residuos a nivel de etiquetasValores de etiquetas de salida que provienen del original
Información de ubicaciónCualquier campo GPS / location
Identificadores de dispositivo/softwareCampos make / model / software / artist / comment, etc.
Marca de tiempo del originalLa creation_time de salida es igual a la del original
Matriz de rotaciónEn modo encode, la rotación no se ha horneado en el video (modo copy se salta — eliminarla dejaría el video torcido)
Composición de flujosSe han infiltrado flujos data / timecode / attachment
CapítulosLos capítulos se han transferido
Cadenas de identidad en el flujo de bitsSEI / DSE / compressorname aún contienen el nombre del codificador
Coherencia del perfilLas etiquetas restantes no coinciden con el perfil declarado
DuraciónLa salida se ha truncado silenciosamente
DecodificableSe ha producido un archivo dañado que no se puede abrir

HDR y subtítulos incrustados: cuándo no se debe tocar el SEI

ffmpeg solo ofrece granularidad a nivel de NAL — filter_units elimina por tipo de NAL, h264_metadata tampoco tiene opción para "eliminar solo ciertos payloads de SEI". Así que remove_types=6 elimina todos los SEI juntos. Y dentro de los SEI, además de la basura de x264, viven:

  • Metadatos HDR estáticos (mastering display / content light level) — eliminarlos provoca pérdida de color, el tone mapping se rompe
  • Subtítulos incrustados CEA-608/708 — eliminarlos hace que desaparezcan los subtítulos

Por eso la estrategia del script es: solo toca el SEI si realmente se detecta una cadena de identidad; los videos HDR nunca se tocan (se indica claramente que no se ha tocado y por qué). Esta decisión la toma seiPlan(), no necesitas preocuparte, pero debes saber que existe.

Límites (lo que no hace)

No elimina marcas de agua (las marcas de agua de píxel robustas no se eliminan ni con recodificación, no esperes), no modifica el contenido del video (no recorta, no escala, no rota), no falsifica modelos de dispositivo ni GPS (el perfil solo escribe información de la herramienta, no inventa dispositivos ni ubicaciones de captura), no escanea directorios en lote (un video a la vez), no maneja contenedores que no sean mp4/mov.

Autocomprobación

node {baseDir}/scripts/selftest.mjs

117 aserciones, no toca ffmpeg, no toca archivos reales. Cada una de las 12 puertas tiene casos de prueba que la atraviesan — para demostrar que realmente bloquean. Ejecuta esto después de modificar el 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 relacionados