Communitygithub.com

video-scrub

Baut ein Video zu einer sauberen Datei um – nur Bild und Ton, **kein einziges Byte Metadaten** wandert mit. GPS, Gerätemodell, Account-ID, Erstellungszeit, Kapitel, GoPro-Telemetriespuren – alles bleibt zurück. Arbeitet nach der Whitelist statt Blacklist. Läuft mit nur `node` und `ffmpeg`, null Abhängigkeiten.

Was ist video-scrub?

Dieses Skill (video-scrub) ist ein professionelles Tool zur Entfernung von Metadaten aus Videodateien. Es basiert auf einer „Whitelist“-Strategie: Statt zu definieren, welche Metadaten gelöscht werden sollen (was oft unvollständig ist), wird nur festgelegt, was mitgenommen wird – eine Videospur und eine Audiospur. So gelangen keine unerwünschten Daten wie GPS, Geräteinformationen, Kapitelmarken oder unsichtbare Identifikatoren in die Ausgabedatei. Das Skill unterstützt die Modi `copy` (für schnellstmögliche Verarbeitung ohne Neuencodierung des Videos) und `encode` (vollständige Neuencodierung für entfernung von Codec-Level-Fingerabdrücken). Es erkennt und eliminiert selbst versteckte Daten in H.264-SEI-Paketen, AAC-DSE-Strukturen und dem compressorname-Feld des avc1-Tracks – Werte, die mit `ffprobe` nicht sichtbar sind. Ein integrierter Verifizierungsmechanismus führt einen Byte-für-Byte-Scan des Ausgabe-Files durch, um sicherzustellen, dass keine der Metadaten-„Nadeln“ der Quelle mehr vorhanden ist. Das Tool ist plattformunabhängig, benötigt keine API-Keys und ist ideal für Datenschutz-, Forensik- und Datenbereinigungs-Workflows.

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

In Ihrer bevorzugten KI fragen

Öffnet einen neuen Chat, in dem dieser Agent-Skill bereits geladen ist.

Dokumentation

Was macht video-scrub?

Baut ein Video in eine saubere Datei um: Bild und Ton wandern mit, kein einziges Byte Metadaten.

{baseDir} = Verzeichnis dieser Datei. Skript {baseDir}/scripts/video-scrub.mjs, null Abhängigkeiten, direkt mit node ausführbar.

Prinzip: Whitelist, nicht Blacklist

Es gibt zwei Ansätze, Metadaten zu entfernen – der Unterschied ist überlebenswichtig:

WieProblem
BlacklistFelder einzeln auflisten und löschen (z. B. mit exiftool)Du kannst nicht löschen, was du nicht kennst – herstellerspezifische udta-Boxen, GoPro-Telemetriespuren, SEI im Bitstrom – eines übersehen und schon ist es weg
WhitelistNicht definieren, was gelöscht wird, sondern was mitgenommen wird: eine Videospur, eine Audiospur – alles andere ignorieren–

Mit der Whitelist hat die Metadaten der Quelle keinen Kanal, um in die Ausgabe zu gelangen. Sie werden nicht „gelöscht“, sondern nie transportiert.

Aber ffmpeg verrät dieses Prinzip zweimal standardmäßig – das muss unterbunden werden:

  1. Es überträgt standardmäßig die Container-Tags der Quelle auf die Ausgabe → -map_metadata -1
  2. Es wählt standardmäßig automatisch Spuren aus – dabei wandert z. B. die GoPro-gpmd-Telemetriespur mit → -map 0:v:0 -map 0:a:0?

Punkt 2 ist die einzige Verteidigung gegen GoPro-/DJI-Quellen: Deren GPS steckt nicht in Tags, sondern in einer eigenen zeitbasierten Metadatenspur. Tag-Löschung allein hilft nicht – nur die Beschränkung auf zwei Spuren schützt.

Die eigentliche Herausforderung: Die unsichtbaren Ebenen

Wenn die beiden obigen Punkte unterbunden sind, ist die Tag-Ebene sauber – ffprobe zeigt nichts mehr. Aber in der Datei lauern noch drei Identitäts-Quellen:

Versteckt inInhaltffprobe sichtbar?
SEI von H.264x264-Parameterkette cabac=1 ref=3 … crf=23.0✗
DSE im AAC-BitstromLavc62.28.102✗
compressorname von avc1Lavc libx264✗

Die fünf Verstecke und ihre Abdichtung stehen in {baseDir}/references/residue-map.md – jedes mit Messprotokoll, inklusive mehrerer Fallstricke (-flags:v +bitexact und -x264-params info=0 stoppen SEI nicht).

Zwei Modi

ModusVideoAudioGeschwindigkeitWas zusätzlich entfernt wird
copy (Standard)Byte-genau kopiertneu codiert53‑Sekunden-Clip in 1,3 s–
encodeneu codiertneu codiertviel langsamerBitstrom-Wasserzeichen, fragile Steganografie

Audio wird in beiden Modi neu codiert – AAC-Versionsnummern stecken im DSE, der innerhalb des Bitstroms liegt. -c:a copy kann sie nicht entfernen. Eine Neucodierung ist günstig und bringt eine saubere Tonspur. Video dagegen wird im copy-Modus kein einziges Bit verändert (bestätigt mit cmp, abgesehen von den entfernten SEI).

Zu Wasserzeichen: encode entfernt zusätzlich Bitstrom-Wasserzeichen und fragile Steganografie – diese sind in echtem Material selten. Die wirklich gefährlichen robusten Pixel-Wasserzeichen (SynthID, forensische Film-Wasserzeichen) sind dafür gebaut, selbst eine Neucodierung zu überstehen – beide Modi entfernen sie nicht. Wähle den Modus also nach Qualität und Geschwindigkeit, nicht in der Hoffnung, Wasserzeichen wegzucodieren.

Vier Profile

Nach der Reinigung wird eine Schicht zurückgeschrieben:

ProfilSchreibt
obs (Standard)Wie OBS Studio. OBS verwendet selbst ffmpeg, daher ist dies keine Fälschung, sondern die normale Erscheinung eines ähnlichen Tools
quicktimemacOS QuickTime-Bildschirmaufnahme: Core Media Video / Core Media Audio
screencaptureScreenCaptureKit-Bildschirmaufnahme
bareNichts. Höchster Datenschutz – keine nachweisbare Behauptung

Eine Sache ist nicht möglich, um weitere Versuche zu ersparen: Das encoder-Attribut des Containers wird vom Muxer selbst gesetzt. -metadata encoder=Lavf60.16.100 überschreibt es nicht (bestätigt). Leeren geht auch nicht. Nur -fflags +bitexact lässt es ganz verschwinden. Deshalb fälscht das obs-Profil keine Versionen – es lässt ffmpeg seine eigene echte Version schreiben.


Schritt 0 – Erst prüfen, was die Quelle mitbringt

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

Ein Bildschirm voll: Container-Tags, Tags und Side Data jeder Spur, Kapitel – sowie die Identitätsstrings im Bitstrom, die ffprobe nicht sieht. Am Ende erfährst du, wie viele Nadeln zum Scannen verwendet werden und ob der SEI-Eingriff erfolgt.

Sieh dir diesen Bildschirm an, bevor du handelst. Achte besonders auf:

  • data / timecode-Spuren – deutet auf Telemetriespuren (GoPro, DJI) hin
  • HDR – dann werden SEI niemals entfernt (Begründung weiter unten)

Schritt 1 – Bereinigen

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

Standard: --mode copy --profile obs. Häufige Parameter:

--mode encode          # vollständige Neucodierung
--profile bare         # nichts schreiben, höchster Datenschutz
--crf 18               # nur im encode-Modus sinnvoll
--date 2026-01-01T00:00:00Z   # Erstellungszeit setzen, sonst aktuelle Zeit

Schritt 2 – Verifikation ⛔ nicht überspringen

node {baseDir}/scripts/video-scrub.mjs verify <quelle> <ausgabe>

Oder alles in einem Befehl (täglicher Gebrauch):

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

Das Kernprinzip der Verifikation: Bis hierhin war alles „ich habe die richtigen Parameter gesetzt“ – eine Behauptung. Dieser Schritt ist „ich habe gescannt, es ist weg“ – ein Beweis.

Dafür werden alle Metadaten-Strings der Quelle als „Nadeln“ extrahiert – Schlüssel und Werte von Tags, Handler-Namen, Kapiteltitel – und in der Ausgabedatei byte-genau gescannt. Wird eine gefunden, wird sie rot markiert.

Zwei Implementierungsdetails:

  • Nadeln müssen ≥5 Byte sein. Kürzere Strings würden im komprimierten Datenstrom zu falschen Positiven führen. Übersprungene Nadeln werden in der Ausgabe aufgelistet.
  • Generische Strings sind keine Nadeln. VideoHandler ist überall zu finden – Informationswert null. Lavf58.45.100 dagegen identifiziert eine bestimmte Version – das ist ein Fingerabdruck. Die Trennlinie ist: „Kann der String den Kreis auf eine Person, ein Gerät oder einen Export eingrenzen?“

12 Prüfungen

PrüfungBlockiert
Byte-Ebene-RückständeMetadaten-Strings der Quelle tauchen in der Ausgabe auf
Tag-Ebene-RückständeAusgabe-Tags stammen von der Quelle
OrtsinformationenJedes GPS / location-Feld
Geräte- und Software-Identifikationmake / model / software / artist / comment etc.
Quell-Zeitstempelcreation_time der Ausgabe entspricht dem der Quelle
Rotationsmatrixbei encode-Modus wurde Rotation nicht ins Bild eingebrannt (copy-Modus überspringt – Löschen würde Bild verdrehen)
Spurenzusammensetzungdata / timecode / Anhang-Spuren wurden übernommen
KapitelKapitel wurden übernommen
Bitstrom-IdentitätsstringsSEI / DSE / compressorname enthalten noch Encoder-Namen
Profil-Konformitätverbleibende Tags stimmen nicht mit dem deklarierten Profil überein
DauerAusgabe wurde unbemerkt abgeschnitten
DekodierbarkeitAusgabe ist eine unspielbare Datei

HDR und eingebettete Untertitel: Wann SEI nicht angerührt werden dürfen

ffmpeg bietet nur NAL-Ebene-Granularität – filter_units löscht nach NAL-Typ, h264_metadata hat keine Option „nur bestimmte SEI-Payloads löschen“. Daher löscht remove_types=6 alle SEI auf einmal. Doch in SEI stecken neben dem x264-Müll auch:

  • HDR-Statische-Metadaten (mastering display / content light level) – deren Entfernung würde Farben zerstören, Tone Mapping bricht
  • CEA-608/708-Eingebettete Untertitel – deren Entfernung würde Untertitel löschen

Daher lautet die Strategie des Skripts: Nur dann eingreifen, wenn tatsächlich Identitätsstrings gefunden wurden; HDR-Quellen werden nie angetastet (es wird explizit angezeigt, dass und warum kein Eingriff erfolgte). Diese Entscheidung trifft seiPlan(), ohne dass du dich darum kümmern musst – aber du solltest wissen, dass es existiert.

Grenzen (was nicht gemacht wird)

Kein Entfernen von Wasserzeichen (robuste Pixel-Wasserzeichen überleben selbst Neucodierung – keine Hoffnung machen), keine Bildinhaltsänderung (kein Zuschneiden, Skalieren, Drehen), keine Fälschung von Gerätemodell oder GPS (Profile schreiben nur Tool-Informationen, keine erfundenen Aufnahmegeräte oder -orte), kein Batch-Modus (eine Datei pro Aufruf), keine Container außer mp4/mov.

Selbsttest

node {baseDir}/scripts/selftest.mjs

117 Assertions, ohne ffmpeg, ohne echte Dateien. Jede der 12 Prüfungen hat einen Durchbruchs-Testfall – beweist, dass sie wirklich blockt. Führe diesen Test nach jeder Änderung am Skript aus.

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。

Verwandte Skills