Skip to main content

📷 Metadata Stripper

Metadata Stripper

Your photos know more than you do. Read it, then erase it.

Python License PyPI Platform

"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)

Source distribution for metadata-stripper 0.2.0
File Size Uploaded
metadata_stripper-0.2.0.tar.gz 136.3 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for metadata-stripper 0.2.0
File Interpreter ABI Platform
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

Release history Release notifications | RSS feed

This release

0.2.0 This release

2 release files

0.1.1

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page