Restore the metadata Facebook strips from media in its data dumps.
When you request a Facebook data dump, the JPEGs, PNGs, GIFs, and MP4s you get back have had their EXIF tables wiped. Only the surrounding HTML files still contain the context — capture dates, album and conversation titles, message captions, source IPs. This tool parses those HTML files and writes that context back into each media file's metadata (DateTimeOriginal, ImageDescription, UserComment, Artist, plus equivalents for QuickTime/XMP).
The output is a triaged tree organized by capture date:
/opt/backup-facebook/exifed/
├── 2014/09/13/10152549062849873.jpg
├── 2017/09/17/10155873784814873.jpg
├── 2019/01/14/2141323906121044.jpg
├── unknown/<files with no resolvable date>
├── _manifest.jsonl # one JSON record per processed file
├── _collisions.log # media referenced by more than one HTML
└── _errors.log # per-file exiftool failures (should be empty)
Source files are never modified — the tool reads the dump and writes copies to a separate directory.
Facebook dumps are useless for long-term archival without the dates back in the files. Photo libraries (Apple Photos, Google Photos, digiKam, Immich…) all sort by EXIF, not by filename. re-exif makes 10–20 years of stripped media re-indexable in one pass.
- Linux / macOS
- Python ≥ 3.11
exiftool≥ 12 (dnf install perl-Image-ExifTool/apt install libimage-exiftool-perl/brew install exiftool)- Python deps:
beautifulsoup4,lxml,pytest(tests only)
git clone <this repo> re-exif
cd re-exif
python3 -m pip install --user -r requirements.txtFacebook delivers the dump as one or several ZIPs (facebook-<user>-<date>-1of3.zip, …). Unpack all of them into the same directory; they share a common layout and merge cleanly:
mkdir -p /opt/backup-facebook/consolidated
cd /opt/backup-facebook/consolidated
for z in ~/Downloads/facebook-*-of-*.zip; do unzip -n "$z"; doneAfter unpacking you should see this layout at the root of the consolidated directory:
consolidated/
├── posts/{album/*.html, media/<Album>_<id>/*.jpg}
├── messages/{inbox,filtered_threads,archived_threads,message_requests}/<thread>/
│ ├── message_1.html
│ ├── photos/, gifs/, videos/
├── messages/{photos,stickers_used}/
├── live_videos/
└── saved_items_and_collections/
If your dump includes the top-level your_facebook_activity/ prefix directory, move its contents up one level so the tree above sits at the consolidated root — this is what the HTML href paths resolve against.
# full run
python3 reexif.py --root /opt/backup-facebook/consolidated --out /opt/backup-facebook/exifed
# scan only — prints the manifest summary, writes nothing
python3 reexif.py --dry-run
# process only the first N files (useful for smoke-testing)
python3 reexif.py --limit 50 --out /tmp/exifed_smoke -v| Flag | Default | Purpose |
|---|---|---|
--root |
/opt/backup-facebook/consolidated |
Consolidated dump root |
--out |
/opt/backup-facebook/exifed |
Output tree (YYYY/MM/DD/…) |
--dry-run |
off | Scan + report only |
--limit N |
0 (no limit) | Process at most N media files |
-v, --verbose |
off | Debug logging |
A full run on ~25 k files typically takes 15–25 minutes and roughly doubles the dump's on-disk size (copies, not in-place). Do not background the run with nohup … & — if you need it detached, use tmux or screen; two concurrent runs over the same --out will create <basename>__<parent> disambig twins for every record.
| Source HTML | Date written as DateTimeOriginal |
|---|---|
posts/album/*.html |
The section's "Date de création" (true capture time when FB preserved it) |
messages/**/message_1.html |
The section's footer timestamp (message send time) |
| everything else / no match | file goes to unknown/, no EXIF date written |
Album title or conversation thread name → ImageDescription / XMP:Title.
Message body → UserComment / XMP:Description.
Message sender → Artist.
Album source IP → appended verbatim to UserComment (no geolocation).
python3 -m pytest -qShould print 35 passed.
- Dates are parsed as naïve local time (no timezone inference). They represent what the dump shows, without UTC conversion.
- Source IPs are stored verbatim; no reverse-geolocation.
- GIF metadata support depends on your exiftool version; very old versions may not write XMP into GIFs.
- This tool cannot recover data Facebook didn't export. If a photo's HTML section has no date, the file goes to
unknown/.