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, часто на годы позже.