Takeout JSON
RU

exiftool и даты Google Takeout: команда и где она ломается

Команда exiftool, которая копирует дату и GPS из JSON-файлов Takeout в фото, три случая, которые она молча пропускает, и когда лучше взять программу.

exiftool бесплатен, работает везде и умеет исправлять выгрузку Google Takeout. Большинство инструкций дают одну команду и на этом заканчиваются. Эта команда срабатывает примерно на четырех фото из пяти. Здесь рассказано, что происходит с пятым.

Установка

На Mac: brew install exiftool. В Debian или Ubuntu: sudo apt install libimage-exiftool-perl. В Fedora: sudo dnf install perl-Image-ExifTool. В Windows скачайте архив .zip с exiftool.org, распакуйте его и переименуйте exiftool(-k).exe в exiftool.exe. Затем откройте терминал в папке, где лежит распакованная папка Takeout.

В первый раз работайте с копией. exiftool записывает изменения прямо в ваши файлы.

Команда

Рядом с каждым фото лежит JSON-файл: IMG_2034.JPG.supplemental-metadata.json в выгрузках с 2024 года, IMG_2034.JPG.json в более старых. Поле photoTakenTime.timestamp в нем и есть настоящая дата, в секундах с 1970 года. Эта команда копирует ее в фото:

exiftool -r -d %s -tagsfromfile "%d/%F.supplemental-metadata.json" \
  "-DateTimeOriginal<PhotoTakenTimeTimestamp" \
  "-CreateDate<PhotoTakenTimeTimestamp" \
  "-FileModifyDate<PhotoTakenTimeTimestamp" \
  -ext jpg -ext jpeg -ext png -ext heic -ext gif \
  -overwrite_original -progress "Takeout/Google Photos"

По частям: -r обходит вложенные папки. -d %s сообщает exiftool, что читаемая дата задана числом секунд от эпохи. -tagsfromfile задает имя JSON-файла через %d (папка фото) и %F (имя файла с расширением). Три строки со знаком < копируют метку времени в дату EXIF, дату создания EXIF и дату изменения файла. -overwrite_original не дает exiftool оставлять копию _original для каждого файла.

Для старой выгрузки запустите команду еще раз с "%d/%F.json". Файлы, которые уже получили дату в первый раз, просто перезапишутся тем же значением.

GPS можно добавить в тот же проход еще четырьмя строками, если он вам нужен:

  "-GPSLatitude<GeoDataLatitude" "-GPSLatitudeRef<GeoDataLatitude" \
  "-GPSLongitude<GeoDataLongitude" "-GPSLongitudeRef<GeoDataLongitude" \

Для фото без места Google записывает 0.0, поэтому оберните это в -if '$GeoDataLatitude ne 0', иначе несколько сотен фото окажутся посреди Атлантики. Подробнее об этом в статье о восстановлении GPS из Takeout (на английском).

В конце exiftool пишет, сколько файлов обновил и сколько пропустил. На пропущенные и уходит весь вечер.

Где команда ломается

Обрезанные имена JSON-файлов. Google обрезает длинные имена JSON-файлов примерно до 46 символов. PXL_20230812_193044123.RAW-01.COVER.jpg получает JSON-файл с именем PXL_20230812_193044123.RAW-01.COVER.supplemen.json или еще короче. Шаблон %F с ним никогда не совпадет. Под это попадают длинные имена с камер, имена из WhatsApp и все, где в имени файла есть описание. Остатки придется вывести списком и сопоставить вручную или скриптом.

Дубликаты с (1). Если вы загружали одно и то же имя дважды, фото называется IMG_1234(1).jpg, а JSON-файл IMG_1234.jpg(1).json. Скобка переехала. Проход переименования JSON-файлов перед основной командой это исправляет:

for j in "Takeout/Google Photos"/*/*"("*").json"; do
  mv "$j" "$(echo "$j" | sed -E 's/\.([a-zA-Z0-9]+)\(([0-9]+)\)(\.supplemental-metadata)?\.json$/(\2).\1\3.json/')"
done

Отредактированные копии. У IMG_1234-edited.jpg нет своего JSON-файла, он общий с оригиналом. Когда оригиналы готовы, скопируйте данные из них:

for f in "Takeout/Google Photos"/*/*-edited.*; do
  exiftool -tagsfromfile "${f/-edited/}" -AllDates -GPS:all -overwrite_original "$f"
done

Видео. Файлы MP4 и MOV не используют EXIF. Они хранят даты в контейнере QuickTime, в других тегах, и эти теги читаются как UTC:

exiftool -r -d %s -api QuickTimeUTC -tagsfromfile "%d/%F.supplemental-metadata.json" \
  "-CreateDate<PhotoTakenTimeTimestamp" "-ModifyDate<PhotoTakenTimeTimestamp" \
  "-TrackCreateDate<PhotoTakenTimeTimestamp" "-MediaCreateDate<PhotoTakenTimeTimestamp" \
  "-FileModifyDate<PhotoTakenTimeTimestamp" \
  -ext mp4 -ext mov -overwrite_original "Takeout/Google Photos"

У роликов Live Photo JSON-файла нет вообще, поэтому после этого они остаются без даты. Для них есть цикл в статье о парах Live Photo (на английском).

Ловушка часовых поясов

Метка времени в JSON-файле задает точный момент по UTC. DateTimeOriginal в EXIF хранит время на часах без пояса. exiftool связывает одно с другим через текущий часовой пояс вашего компьютера. Снимок, сделанный в 11:05 в Париже, получит 11:05, если на компьютере стоит парижское время, и 05:05, если стоит время Нью-Йорка.

Перед запуском задайте пояс того места, где снята большая часть фото:

TZ=Europe/Paris exiftool ...

Отпускные фото из других поясов все равно сдвинутся на несколько часов. Только по JSON-файлу это не обойти: единственная запись местного времени это собственный EXIF камеры, если он сохранился. Не "исправляйте" фото, у которых уже есть правильная дата в EXIF. Добавьте -if 'not $DateTimeOriginal', чтобы их не трогать.

Стоит ли это вечера

Для чистой выгрузки из нескольких тысяч снимков с камеры exiftool займет час, включая чтение этой статьи. Для настоящей медиатеки с картинками из WhatsApp, скриншотами, дубликатами (1), HEIC, видео и Live Photo заложите вечер и приготовьтесь несколько раз запускать exiftool -r -if 'not $DateTimeOriginal' -filename, чтобы увидеть, что еще осталось без даты.

Если вы хотите еще и переименовать файлы по дате, разложить их по папкам по годам и месяцам, превратить HEIC в JPG и сохранить пары Live Photo вместе, это еще четыре команды и цикл. Если вам это нравится, все в порядке. GooglePhotosTakeoutHelper это бесплатный скрипт, который сам справляется с особенностями имен. Это хороший промежуточный вариант, если вы уверенно работаете в терминале, но не хотите писать sed сами.

Приложение

Takeout JSON Metadata Fixer делает весь этот список за один запуск, с предварительным пробным прогоном, на macOS, Windows и Linux.

Программа находит JSON-файл для каждого фото, включая обрезанные имена, дубликаты (1) и копии -edited, и записывает в файл photoTakenTime и GPS. Видео получают теги QuickTime. Ролики Live Photo наследуют дату снимка. Фото без JSON-файла сохраняют дату из EXIF, а фото без того и другого получают дату файла, так что без даты ничего не остается.

Шаблоны имен и папок необязательны: "Keep names" и "Keep my folders" исправляют только даты. Перед началом программа проверяет место на диске и работает полностью офлайн. Первые 1000 фото бесплатно. Без ограничений стоит 19,99 $ один раз, это меньше, чем стоит ваш вечер.

Частые вопросы

exiftool пишет "Warning: No writable tags set from" для JSON-файла. Что это значит?

JSON-файл есть, но в нем нет photoTakenTime, или шаблон имени совпал не с тем файлом. Откройте .json и проверьте. Файлы альбомов metadata.json тоже вызывают это предупреждение, его можно игнорировать.

Можно запустить команду прямо на архивах, без распаковки?

Нет. exiftool нужны файлы на диске. Сначала распакуйте все части в одну папку, как описано в статье Google Takeout: несколько архивов .zip.

Меняет ли запись EXIF само изображение?

Нет. exiftool переписывает блок метаданных, а данные изображения не трогает. Файл может вырасти на несколько сотен байт.

Какую дату копировать, photoTakenTime или creationTime?

photoTakenTime. creationTime это время загрузки файла в Google, часто на годы позже.