web ·
How To Correctly Export And Migrate Away From Google Photos

Downloading your pictures from Google Photos via Takeout Export messes up the EXIF data of images. If re-importing elsewhere, like in Apple Photos' iCloud, it's important to fix that data in order to have a successful import that stays in chronological order and with the correct GPS coordinates.
Use the free automatic fixer tool
Upload your Google Photos export and restore its metadata automatically.
Fix my photos
Manual method
Downloading All Files From Google Photos
- Navigate to Google Takeout
- Deselect all, and select the Photos archive
- Download and unzip your archive. If the export comes as several zips, unzip them all into the same folder, since photos and their metadata files often end up split across zips.
Each picture and video comes with a JSON file holding its metadata, and many files get stripped of their own dates and location in the process. To import your library anywhere else in the correct order, that metadata needs to be merged back into the files.
Fixing Image and Video Metadata
- Install ExifTool, the free tool that will copy the metadata from the JSON files back into your photos and videos.
- Open Terminal (on Windows, PowerShell) and run the commands below in order. Replace
<DirToProcess>with the path of your unzipped folder (on Mac, drag the folder into Terminal to fill it in). Subfolders are included automatically.
Step 1: Rename the JSON files
Takeout names its metadata files inconsistently (photo.jpg.json, photo.jpg.supplemental-metadata.json, truncated and numbered variants…). This renames them all to one predictable form:
exiftool -r -ext json '-FileName<${FileName;s/^(.+?\.\w{2,4})\.[^.()]+((?:\(\d+\))?)\.json$/$1$2.json/i; s/^(.+?)\.(\w{2,4})\((\d+)\)\.json$/$1($3).$2.json/i}' <DirToProcess>
exiftool -r -ext json -charset filename=utf8 '-FileName<${FileName;s/^(.+?\.\w{2,4})\.[^.()]+((?:\(\d+\))?)\.json$/$1$2.json/i; s/^(.+?)\.(\w{2,4})\((\d+)\)\.json$/$1($3).$2.json/i}' <DirToProcess>
Step 2: Fix wrong file extensions
Google often converts photos and videos without renaming them (a .HEIC file that's really a JPEG, a .MOV that's really an MP4), which blocks the metadata from being written. This renames mismatched files (and their JSON) to the correct extension (a few renames like .heic → .heif are just the container's preferred spelling — harmless):
exiftool -r -q -if '$FileTypeExtension and $FileName !~ m{\.(\Q$FileTypeExtension\E|jpeg)$}i' -p '$Directory/$FileName|$FileTypeExtension' -ext '*' --ext json --ext html <DirToProcess> | while IFS='|' read -r f ext; do mv "$f" "${f%.*}.$ext"; [ -e "$f.json" ] && mv "$f.json" "${f%.*}.$ext.json"; done
exiftool -r -q -if '$FileTypeExtension and $FileName !~ m{\.(\Q$FileTypeExtension\E|jpeg)$}i' -p '$Directory/$FileName|$FileTypeExtension' -ext '*' --ext json --ext html -charset filename=utf8 <DirToProcess> | ForEach-Object { $f, $ext = $_ -split '\|'; $new = $f -replace '\.[^.]+$', ".$ext"; Move-Item -LiteralPath $f -Destination $new; if (Test-Path -LiteralPath "$f.json") { Move-Item -LiteralPath "$f.json" -Destination "$new.json" } }
Step 3: Copy the metadata into your files
This is the main step. It can take a while on a large library, and ExifTool shows its progress as it goes:
exiftool -r -d %s -tagsfromfile '%d/%F.json' \
'-AllDates<CreationTimeTimestamp' \
'-AllDates<PhotoTakenTimeTimestamp' \
'-FileModifyDate<CreationTimeTimestamp' \
'-FileModifyDate<PhotoTakenTimeTimestamp' \
'-FileCreateDate<CreationTimeTimestamp' \
'-FileCreateDate<PhotoTakenTimeTimestamp' \
'-QuickTime:TrackCreateDate<CreationTimeTimestamp' \
'-QuickTime:TrackCreateDate<PhotoTakenTimeTimestamp' \
'-QuickTime:TrackModifyDate<CreationTimeTimestamp' \
'-QuickTime:TrackModifyDate<PhotoTakenTimeTimestamp' \
'-QuickTime:MediaCreateDate<CreationTimeTimestamp' \
'-QuickTime:MediaCreateDate<PhotoTakenTimeTimestamp' \
'-QuickTime:MediaModifyDate<CreationTimeTimestamp' \
'-QuickTime:MediaModifyDate<PhotoTakenTimeTimestamp' \
'-GPSLatitude<${GeoDataLatitude;$_ = undef if $_ == 0 and $self->GetValue("GeoDataLongitude") == 0}' \
'-GPSLatitudeRef<${GeoDataLatitude;$_ = undef if $_ == 0 and $self->GetValue("GeoDataLongitude") == 0}' \
'-GPSLongitude<${GeoDataLongitude;$_ = undef if $_ == 0 and $self->GetValue("GeoDataLatitude") == 0}' \
'-GPSLongitudeRef<${GeoDataLongitude;$_ = undef if $_ == 0 and $self->GetValue("GeoDataLatitude") == 0}' \
'-GPSAltitude<${GeoDataAltitude;$_ = undef if $self->GetValue("GeoDataLatitude") == 0 and $self->GetValue("GeoDataLongitude") == 0}' \
'-Keys:GPSCoordinates<${GeoDataLatitude;$_ = undef if $_ == 0 and $self->GetValue("GeoDataLongitude") == 0}, $GeoDataLongitude, $GeoDataAltitude' \
'-Keywords<Tags' \
'-Subject<Tags' \
'-Caption-Abstract<Description' \
'-ImageDescription<Description' \
'-XMP:Description<Description' \
-api QuickTimeUTC -api LargeFileSupport=1 -m \
-ext '*' --ext json --ext html \
-overwrite_original -progress <DirToProcess>
exiftool -r -d %s -tagsfromfile '%d/%F.json' '-AllDates<CreationTimeTimestamp' '-AllDates<PhotoTakenTimeTimestamp' '-FileModifyDate<CreationTimeTimestamp' '-FileModifyDate<PhotoTakenTimeTimestamp' '-FileCreateDate<CreationTimeTimestamp' '-FileCreateDate<PhotoTakenTimeTimestamp' '-QuickTime:TrackCreateDate<CreationTimeTimestamp' '-QuickTime:TrackCreateDate<PhotoTakenTimeTimestamp' '-QuickTime:TrackModifyDate<CreationTimeTimestamp' '-QuickTime:TrackModifyDate<PhotoTakenTimeTimestamp' '-QuickTime:MediaCreateDate<CreationTimeTimestamp' '-QuickTime:MediaCreateDate<PhotoTakenTimeTimestamp' '-QuickTime:MediaModifyDate<CreationTimeTimestamp' '-QuickTime:MediaModifyDate<PhotoTakenTimeTimestamp' '-GPSLatitude<${GeoDataLatitude;$_ = undef if $_ == 0 and $self->GetValue("GeoDataLongitude") == 0}' '-GPSLatitudeRef<${GeoDataLatitude;$_ = undef if $_ == 0 and $self->GetValue("GeoDataLongitude") == 0}' '-GPSLongitude<${GeoDataLongitude;$_ = undef if $_ == 0 and $self->GetValue("GeoDataLatitude") == 0}' '-GPSLongitudeRef<${GeoDataLongitude;$_ = undef if $_ == 0 and $self->GetValue("GeoDataLatitude") == 0}' '-GPSAltitude<${GeoDataAltitude;$_ = undef if $self->GetValue("GeoDataLatitude") == 0 and $self->GetValue("GeoDataLongitude") == 0}' '-Keys:GPSCoordinates<${GeoDataLatitude;$_ = undef if $_ == 0 and $self->GetValue("GeoDataLongitude") == 0}, $GeoDataLongitude, $GeoDataAltitude' '-Keywords<Tags' '-Subject<Tags' '-Caption-Abstract<Description' '-ImageDescription<Description' '-XMP:Description<Description' -api QuickTimeUTC -api LargeFileSupport=1 -m -ext '*' --ext json --ext html -charset filename=utf8 -overwrite_original -progress <DirToProcess>
When it finishes, ExifTool prints a summary like 209 image files updated. Warnings about missing .json files along the way are normal: those are the Live Photo videos and edited copies covered in the notes below.
This fills in every photo and video with its date and time taken, GPS location, captions, and tags. It handles:
- Photos and videos (video dates and location are stored differently)
- Photos with no location data, which are skipped instead of being geotagged to a wrong spot
- Photos older than 1970
- Photos whose metadata has no "taken" date, which fall back to their Google Photos upload date instead of getting no date at all
- File creation and modification dates, so everything also sorts correctly in Finder
Step 4: Copy the people and face tags
Google Photos stores the names of the people you tagged in each picture's metadata file, and the previous step doesn't carry them over. This writes the names as keywords (so they're searchable in Apple Photos and elsewhere) and into the standard "person shown" tag:
exiftool -r -tagsfromfile '%d/%F.json' \
-if 'not $PersonInImage' \
'-IPTC:Keywords+<PeopleName' \
'-XMP-dc:Subject+<PeopleName' \
'-XMP-iptcExt:PersonInImage+<PeopleName' \
-api LargeFileSupport=1 -m \
-ext '*' --ext json --ext html \
-overwrite_original -progress <DirToProcess>
exiftool -r -tagsfromfile '%d/%F.json' -if 'not $PersonInImage' '-IPTC:Keywords+<PeopleName' '-XMP-dc:Subject+<PeopleName' '-XMP-iptcExt:PersonInImage+<PeopleName' -api LargeFileSupport=1 -m -ext '*' --ext json --ext html -charset filename=utf8 -overwrite_original -progress <DirToProcess>
Warning: No writable tags set on pictures with no tagged people is expected and harmless.
Step 5 (optional): Date the leftover files from their filenames
A few files have no metadata JSON at all (Live Photo videos, "-edited" copies, strays), but many of them encode the capture date right in their name, like IMG_20190919_123456.jpg. This fills in dates from filenames for files that still have none, and leaves everything else alone:
exiftool -r -if 'not $DateTimeOriginal' '-AllDates<Filename' \
-m -ext '*' --ext json --ext html \
-overwrite_original -progress <DirToProcess>
exiftool -r -if 'not $DateTimeOriginal' '-AllDates<Filename' -m -ext '*' --ext json --ext html -charset filename=utf8 -overwrite_original -progress <DirToProcess>
Filenames whose digits aren't a real date (like P1050123.jpg) are rejected with a warning and left untouched. To see what's still missing a date afterwards:
exiftool -r -if 'not $DateTimeOriginal' -p '$Directory/$FileName' -ext '*' --ext json --ext html <DirToProcess>
Notes
- This overwrites files in place, so keep the original zip as a backup.
- The commands are safe to re-run if anything gets interrupted. If ExifTool then complains about a temporary file, delete any file ending in
_exiftool_tmpand run it again. - Times are written in your computer's timezone, so photos taken while traveling can be off by a few hours.
- Some files legitimately have no JSON: the video halves of Live Photos (they pair back up with their photo when imported into Apple Photos) and "-edited" copies (duplicates of the originals, safe to delete). Likewise,
.MP/.MVfiles from Pixel phones are the video halves of Motion Photos and need no fixing.
Moving Library to iCloud
- Use Photos.app on Mac to upload your pictures. Make sure to use the File > Import instead of dragging everything in the app, it works much better for large libraries.
- Everything should import well. If there are some pictures that show up at the wrong date, manually correct them with Image > Adjust Date and Time.
Taking Care of Your Existing Google Photos Library
- If you are keeping Google Photos as a secondary backup, there’s a couple of things to do. The Google Photos phone app will recognize all newly added pictures in your iCloud as new pictures and will start uploading them again as duplicates. I couldn’t find a way to prevent that so I let the Google Photos app upload the whole library again.
- Once all pictures are uploaded to Google Photos, navigate to https://photos.google.com/search/_tra_ where pictures will be sorted by Upload Time. Select all newly added pictures and delete them. Google Photos will not reupload the deleted duplicates. It will continue uploading new pictures you take on your phone in the future as usual.
Comments