Skip to content

Pix13/face2exif

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

3 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

re-exif

Restore the metadata Facebook strips from media in its data dumps.

What it does

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.

Why

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.

Requirements

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

Install

git clone <this repo> re-exif
cd re-exif
python3 -m pip install --user -r requirements.txt

Preparing the source dump

Facebook 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"; done

After 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.

Usage

# 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.

How the dates are chosen

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

Tests

python3 -m pytest -q

Should print 35 passed.

Known limitations

  • 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/.

About

Re-exif a facebook takeout, based on the dates found in the html parts

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages