Communitygithub.com

eternityspring/reelbench-skills

動画を「映像と音声だけ」のクリーンなファイルに再構築します。GPS、デバイスモデル、アカウントID、作成時刻、チャプター、GoProのテレメトリトラックなど、すべてのメタデータを残しません。ゼロ依存、Node.jsとffmpegのみで動作します。

reelbench-skills とは?

このスキルは、`eternityspring/reelbench-skills` リポジトリに含まれる `video-scrub` ツールを提供します。これは、動画ファイルからプライベートなメタデータを徹底的に除去するためのものです。ホワイトリスト方式を採用し、映像ストリームと音声ストリームのみを抽出・再構築することで、GPS、デバイスモデル、作成時刻、ソフトウェアバージョンなどの情報を確実に排除します。ffprobeでは検出できない、H.264のSEI(x264のエンコードパラメータ)、AACのDSE(エンコーダバージョン)、avc1のcompressornameといったコードストリーム内部に埋め込まれた識別子にも対応しており、5つの隠れ場所をすべて塞ぎます。デフォルトモード(`--mode copy`)では映像はバイトレベルでコピー(`cmp`で検証済み)、音声のみ再エンコードするため高速で、53秒の動画を約1.3秒で処理します。`--mode encode` では完全に再エンコードし、コードストリーム領域の透かしも除去します。検証プロセスはバイトレベルのスキャンにより行われ、ソースファイルのメタデータ文字列を「針」として出力ファイルをチェックし、漏れがないことを保証します。12のチェック項目(ドア)があり、それぞれ自動化されたテストで検証されています。このスキルは、動画のメタデータ除去、プライバシー保護、透かし情報の除去、EXIF情報の削除などのタスクに最適で、ゼロ依存、APIキー不要で、Node.jsとffmpegのみで動作します。

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

お気に入りのAIに質問する

このエージェントスキルを事前に読み込んだ状態で新しいチャットを開きます。

ドキュメント

video-scrub

動画をクリーンなファイルに再構築します:映像と音声だけを持ち越し、メタデータは1バイトも残しません。

{baseDir} = このファイルがあるディレクトリ。スクリプト {baseDir}/scripts/video-scrub.mjs、ゼロ依存、node で直接実行可能。

原理:ホワイトリスト方式、ブラックリスト方式ではない

メタデータ除去には2つのアプローチがあり、その違いは死活問題です:

方法問題点
ブラックリスト方式削除するフィールドを列挙して一つずつ削除(exiftoolのような)知らないものは削除できない——ベンダー固有のudta、GoProのテレメトリトラック、コードストリーム内のSEI、一つ漏れるとアウト
ホワイトリスト方式削除するものを指定せず、持ち越すものだけを指定する:映像1ストリーム、音声1ストリーム、それ以外は一切不要——

ホワイトリスト方式では、ソース動画のメタデータが入り込む経路がありません。それは「削除された」のではなく、「そもそも運ばれなかった」のです。

しかし、ffmpegのデフォルト動作はこの構造を2度裏切るため、しっかり抑え込む必要があります:

  1. デフォルトでソースのコンテナタグを出力にコピーする → -map_metadata -1
  2. デフォルトで自動的にストリームを選択するため、GoProの gpmd テレメトリトラックも一緒に連れて行かれる → -map 0:v:0 -map 0:a:0?

2番目はGoPro / DJIなどの動画に対する唯一の防御線です:それらのGPSはタグにあるのではなく、独立したタイムコードメタデータトラックに存在します。タグを消しても除去できず、「2つのストリームだけを選択する」ことでしか防げません。

本当の難所:見えない層たち

上記2つを抑えればタグレベルはクリーンになり、ffprobe は何も表示しなくなります。しかし、ファイル内にはまだ3つの識別子が潜んでいます:

隠れ場所内容ffprobeで見えるか
H.264 の SEIx264の完全なパラメータ文字列 cabac=1 ref=3 … crf=23.0✗
AAC コードストリームの DSELavc62.28.102✗
avc1 の compressornameLavc libx264✗

5つの隠れ場所とそれぞれの対策方法は {baseDir}/references/residue-map.md にあります——それぞれに実測記録があり、「効きそうで効かなかった」いくつかの落とし穴を含みます(-flags:v +bitexact や -x264-params info=0 ではSEIを止められない)。

2つのモード

モード映像音声速度さらに何を除去するか
copy(デフォルト)バイトレベルでそのままコピー再エンコード53秒の動画が1.3秒——
encode再エンコード再エンコード非常に遅いコードストリーム領域の透かし、脆弱なステガノグラフィー

音声は両方のモードで再エンコードされます——AACのバージョン番号はDSEに書き込まれており、それはコードストリーム内部のため -c:a copy では除去できません。再エンコードはコストが低く、音声トラックをクリーンにします。映像はコピーモードでは本当に1ビットも変更されていません(cmp で検証済み、除去されたSEIを除く)。

透かしについて:encode モードが copy モードより多く除去するのは、コードストリーム領域の透かしと脆弱なステガノグラフィーであり、これらは実際の素材ではまれです。本当に厄介なロバストピクセル透かし(SynthID、フォレンジック透かし)は、再エンコードに耐えるように設計されており、どちらのモードでも除去できません。そのため、モードの選択は画質と速度に基づいて行い、再エンコードで透かしが除去できると期待してはいけません。

4つのプロファイル

拭き取った後に書き戻す層:

プロファイル書き込む内容
obs(デフォルト)OBS Studio の外観。OBS自体がffmpegでラッピングされているため、これは偽造ではなく、同種のツールの正常な外観を保持します
quicktimemacOS QuickTime 画面収録:Core Media Video / Core Media Audio
screencaptureScreenCaptureKit 画面収録
bare何も書き込まない。最もプライバシー強度が高い——検証可能な宣言を一切残さない

一つだけできないことがあるので、試す手間を省くために書いておきます:コンテナの encoder フィールドはmuxer自身が占有しており、-metadata encoder=Lavf60.16.100 では上書きできず、空にしようとしても消せません(実測済み)。-fflags +bitexact だけがそれを完全に消去できます。そのため obs プロファイルはバージョン番号を偽造せず、ffmpegに実際のバージョン番号を書き込ませます。


Step 0 — まずソース動画が何を持っているか確認する

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

一画面にすべてを表示:コンテナタグ、各ストリームのタグとサイドデータ、チャプター、そして ffprobe では見えないコードストリーム内のいくつかの識別子。最後に、検証時に何本の針でスキャンする必要があるか、そしてSEIを除去するかどうかが通知されます。

この表示を見てから作業を始めてください。 特に以下の2点に注意:

  • data / timecode ストリームがあるか——あればソース動画がテレメトリトラック(GoPro、DJI)を持っている可能性がある
  • HDR かどうか——HDRの場合はSEIは一切削除しない(理由は後述)

Step 1 — 除去

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

デフォルトは --mode copy --profile obs。よく使うパラメータ:

--mode encode          # 完全再エンコード
--profile bare         # 何も書き込まない、最もプライバシー強度が高い
--crf 18               # encode モードでのみ意味がある
--date 2026-01-01T00:00:00Z   # 作成時刻を指定、指定しない場合は現在時刻を使用

Step 2 — 検証 ⛔ 省略禁止

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

または一発で実行(日常的にはこれを使う):

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

検証の核心は一言で言えば:ここまではすべて「正しいパラメータを設定しました」という主張に過ぎません。このステップは「スキャンして、本当に存在しないことを証明する」ものです。

方法は、ソース動画のすべてのメタデータ文字列を「針」として抽出します——タグのキーと値、ハンドラー名、チャプタータイトル——そして出力ファイル内をバイトレベルでスキャンし、一つでも一致すれば赤で表示します。

実装上の2つのこだわり:

  • 針は5バイト以上でなければならない。より短い文字列は圧縮データ内でランダムに偽陽性を引き起こす可能性があります。スキップされた針はプロンプトにリストされます。
  • 汎用的な文字列は針にしない。VideoHandler はどこにでも存在し、情報量はゼロです。Lavf58.45.100 は特定のバージョンに固定されており、それは指紋です。線引きは「範囲を特定の人物、特定のデバイス、特定のエクスポートに絞り込めるかどうか」です。

12のドア

ドア防ぐもの
バイトレベルの残骸ソース動画のメタデータ文字列が出力にヒットする
タグレベルの残骸出力のタグ値がソース動画由来である
位置情報GPS / location フィールド
デバイス・ソフトウェア識別子make / model / software / artist / comment など
ソース動画のタイムスタンプ出力の creation_time がソース動画と同じ
回転行列encode モードで回転が映像に焼き込まれていない(copy モードはスキップ——削除すると映像が歪む)
ストリーム構成data / timecode / 添付ストリームが混入している
チャプターチャプターが引き継がれている
コードストリームの識別子SEI / DSE / compressorname にまだエンコーダ名が残っている
プロファイルの一致残ったタグが宣言されたプロファイルと一致しない
再生時間出力が密かにカットされている
デコード可能性開けない壊れたファイルが生成されている

HDR と埋め込み字幕:SEI を絶対に削除してはいけない場合

ffmpeg は NAL レベル の粒度しか提供しません——filter_units は NAL タイプで削除し、h264_metadata にも「特定のSEIペイロードのみ削除」するオプションはありません。そのため、remove_types=6 は すべての SEI をまとめて削除 します。そして、SEI には x264 のゴミ以外にも以下のものが含まれています:

  • HDR 静的メタデータ(mastering display / content light level)——削除すると色が変わり、トーンマッピングが崩れる
  • CEA-608/708 埋め込み字幕——削除すると字幕が消える

そのため、スクリプトの戦略は次の通りです:実際に識別子が見つかった場合のみSEIを削除し、HDRソース動画は決して削除しない(削除しなかったこととその理由を明示します)。この判断は seiPlan() が行うため、ユーザーが気にする必要はありませんが、その仕組みが存在することは知っておくべきです。

境界(やらないこと)

透かしの除去はしない(ロバストピクセル透かしは再エンコードでも除去できないので期待しない)、映像コンテンツの変更はしない(切り抜き、リサイズ、回転はしない)、デバイスモデルやGPSの偽造はしない(プロファイルはツール情報のみを書き込み、撮影デバイスや位置は偽造しない)、バッチディレクトリスキャンはしない(一度に1ファイルずつ処理)、mp4/mov 以外のコンテナは処理しない。

セルフテスト

node {baseDir}/scripts/selftest.mjs

117 項目のアサーション。ffmpeg や実際のファイルには依存しません。12のドアすべてに突破テストがあります——それが実際にブロックすることを証明します。スクリプトを変更したら、まずこれを実行してください。

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。

関連スキル