📷 Metadata Stripper
Your photos know more than you do. Read it, then erase it.
"The photo was innocent. The GPS coordinates were not."
What is it?
Every file whispers: where it was taken, with what camera, by whom and at what exact moment. That whisper is metadata — and half the time it includes GPS coordinates. Metadata Stripper v2 reads the whisper, shows it to you in a beautiful Textual TUI, and then removes it for good.
JPEG / PNG / WebP / TIFF / BMP / GIF / ICO — EXIF, GPS, XMP, ICC, thumbnail
HEIC / HEIF / AVIF — ISOBMFF container, EXIF/XMP items
PDF — document info, XMP packet
DOCX / XLSX / PPTX / ODT / ODS / ODP — core/app/custom properties
ZIP — comments, timestamps, junk files
MP3 / FLAC / WAV / OGG / OPUS — ID3, vorbis, RIFF INFO
MP4 / MOV / MKV / WEBM / AVI — tags, covr, LIST INFO
DOC / XLS / PPT / MSG (OLE2) — read-only analysis
Run it
pip install "metadata-stripper[all]" # or: pip install -e .[all]
metastrip # 🚀 opens the TUI
metastrip with no arguments opens the Textual TUI — the main
interface. All subcommands below also work for scripting.
The TUI
Eight tabs, keyboard-first, dark theme with a fire-gradient dragon banner:
| Tab | What you can do |
|---|---|
| Explorar | type a path, scan (F5), filter GPS-only / high-risk, click a file |
| Detalle | every metadata field grouped (EXIF/GPS/XMP/ICC/doc/audio…) |
| GPS | decimal + DMS coordinates, geohash, plus code, altitude, nearest cities, map/OSM links, plausibility check |
| Riesgo | field-level privacy score: per-field 0-10 bar, category, verdict, per-category points |
| Limpiar | profile select (full/gps/dates/author/device/paper), keep/remove fields, dry-run, rename, output dir, live log |
| Batch | parallel mass-clean with N workers, progress bar, cancel |
| Reporte | export scan results as JSON / CSV / HTML |
| Renombrar | chameleon rename: random or {index} {name} {ext} {date} {time} {rand} |
Key bindings: F5 rescan · Ctrl+S clean selection · Ctrl+B clean all ·
R rename · E export · Q quit.
CLI (scripting)
metastrip scan ~/fotos -v # risk report per file
metastrip exif photo.jpg # full technical dump
metastrip gps show photo.jpg # coordinates + cities + map URL
metastrip gps remove photo.jpg # strip only GPS
metastrip strip photo.jpg --profile gps # selective by profile
metastrip strip ./export --keep make,model # keep camera info
metastrip batch ./export --workers 8 --report html
metastrip report ./dir --format html --out report.html
metastrip verify clean.jpg # exit 0 = clean
metastrip rename ./dir --mode random # no date hints
metastrip profiles # list cleaning profiles
# v0.2 additions
metastrip risk ~/fotos -v # field-level privacy scores
metastrip diff a.jpg b.jpg --verify # what changed; exit 1 on leaks
metastrip watch ~/inbox --out ~/clean # auto-strip new files (SQLite state)
metastrip serve --port 8710 --api-key s3cret # HTTP strip service (loopback)
metastrip forensics integrity ~/fotos # detect already-stripped/tampered
metastrip forensics timeline ~/fotos # movement timeline from GPS+time
metastrip forensics fingerprint ~/fotos # identify capture devices
metastrip policy list # built-in stripping policies
metastrip policy apply ~/fotos --builtin social --out ~/clean
Cleaning profiles
| Profile | What it removes |
|---|---|
full |
everything |
gps |
coordinates, latitude, longitude, altitude, geotag |
dates |
date/time/created/modified/datetime |
author |
author/creator/artist/owner/company/last |
device |
make/model/software/lens/camera/focal |
paper |
keeps only resolution/width/height/dpi/orientation/pixel |
New in v0.2
Risk engine (risk.py)
Every metadata field is scored 0-10 by sensitivity (GPS = 10, device
serial = 9, author/email = 8, camera make/model = 6, software = 4, dates = 3,
comments = 2, structure = 1). Files get a 0-100 score with a verdict that
names the dominant exposure; batches rank their worst offenders; and
risk_delta / strip_and_measure prove how much exposure a strip actually
removed.
Forensics (forensics.py)
Read the silence, not just the whisper:
- integrity — detect files that were already stripped or tampered with (camera JPEG with no EXIF, missing Make/Model, missing docProps, zeroed ZIP dates, editor-tool fingerprints in Software, EXIF-after-mtime, digitized- before-original date orderings).
- timeline — fuse EXIF timestamps + GPS from a photo batch into a movement log: events, legs with distance/speed, and stops where the photographer lingered.
- fingerprint — identify the capture device from EXIF patterns (make/model/software, maker-note vendor, tag set) against a built-in device-profile database (smartphone / DSLR / mirrorless / drone…).
Policies (policy.py)
Declarative keep/scrub rules per format, in JSON or TOML (stdlib only).
Validate, diff and apply them; four built-ins ship: paranoid, social,
photographer, archive.
Watcher (watch.py)
Polling filesystem watcher (no watchdog): auto-strips new/changed files in a folder with debounce and a SQLite state db, so restarts never double-strip.
HTTP service (server.py)
metastrip serve runs a loopback HTTP service on the stdlib http.server:
POST a file to /strip and get the stripped file back (JSON+b64 or raw
bytes) plus a risk before/after report; /risk scores without stripping.
Optional X-API-Key auth.
Plugins (plugins.py)
Drop a .py file into a plugin directory and it becomes a format handler:
declare PLUGIN = {name, version, extensions} plus read_metadata /
strip_metadata. Discovery, validation and sandboxed-ish execution included;
two sample plugins ship in sample_plugins/ (JSON documents, CSV logs).
Deep EXIF (exif_deep.py)
Recursive TIFF/IFD walker below Pillow's surface: sub-IFD traversal, maker-note vendor detection, embedded thumbnail extraction & stripping, and a minimal TIFF writer used to rebuild EXIF blobs.
Optional dependencies
Pillow + textual are required. Everything else is optional per format:
pip install "metadata-stripper[pdf]" # pypdf
pip install "metadata-stripper[audio]" # mutagen
pip install "metadata-stripper[ole]" # olefile
pip install "metadata-stripper[all]" # everything
Without an optional dep the format simply isn't registered (the TUI and CLI
keep working) and a clear pip install hint is shown if you try to use it.
Tests
pip install pytest
pytest
270 tests cover every format handler (including the HEIF container walker), GPS parsing, risk scoring, batch, reports, rename, redaction, deep EXIF, policies, diffs, the watcher, the HTTP service, plugins and forensics. All fixtures are built in code — no binary blobs in the repo.
License
MIT — clean your own files and let the protagonist's geotag be a plot point, not a leak.
Release files for metadata-stripper 0.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| metadata_stripper-0.2.0.tar.gz | 136.3 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| metadata_stripper-0.2.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 246.5 kB
Release files / metadata_stripper-0.2.0.tar.gz
| Download URL | metadata_stripper-0.2.0.tar.gz |
|---|---|
| Size | 136.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
33cdf840f4a994899a62984594b912a6e0e0c4b9bcd862a0787f9d62a5d3fc2a
|
|
BLAKE2b-256 checksum How to use checksums |
9c758d942709eeb38f63c5ec83310ddabf6e61487f5be4e0b1558b539aff2e3f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.14.3
|
Release files / metadata_stripper-0.2.0-py3-none-any.whl
| Download URL | metadata_stripper-0.2.0-py3-none-any.whl |
|---|---|
| Size | 110.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
5ab6824b4da52964113f33ecd9a59276b4835eedd21a240223328d360f8a535b
|
|
BLAKE2b-256 checksum How to use checksums |
0837a5a7b3c58687f19b0ee26ce4250d8d0767605313923a8d86c3ab59231e93
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.14.3
|