Created
August 24, 2024 02:31
-
-
Save shotasenga/18fd226bb53669858fa2141df0671ca6 to your computer and use it in GitHub Desktop.
Some of the photos exported from Google photos didn't have correct timestamp. This script adjust the date time info based on the exported JSON.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import re | |
import datetime | |
import piexif | |
import glob | |
import json | |
import os | |
from PIL import Image | |
import shutil | |
def iterate_images(directory): | |
for root, dirs, files in os.walk(directory): | |
for file in files: | |
if file.lower().endswith(('.jpg', '.jpeg', '.png', '.gif', '.webp')): | |
yield os.path.join(root, file) | |
def pick_one(fpath, matches): | |
if len(matches) == 0: | |
return None | |
elif len(matches) == 1: | |
return matches[0] | |
print('Multiple matches found for', fpath) | |
with Image.open(fpath) as img: | |
img.show() | |
for i, m in enumerate(matches): | |
print(i, m['photoTakenTime']['formatted']) | |
selected = input('Pick one and press enter:') | |
return matches[int(selected)] | |
def retrieve_captured_timestamp(fpath): | |
fname = re.sub(r'\s*\(\d+\)', '', os.path.basename(fpath)) | |
print(os.path.basename(fpath), fname) | |
matches = glob.glob(f'Extracted/**/{fname}.json', recursive=True) | |
meta = pick_one(fpath, [json.load(open(m)) for m in matches]) | |
if meta is None: | |
return None | |
return int(meta['photoTakenTime']['timestamp']) | |
def adjust_date(fpath, captured_at): | |
if fpath.lower().endswith(('.jpg', '.jpeg')): | |
exif_dict = piexif.load(fpath) | |
date_str = datetime.datetime.fromtimestamp(captured_at).strftime('%Y:%m:%d %H:%M:%S') | |
exif_dict['0th'][piexif.ImageIFD.DateTime] = date_str | |
exif_dict['Exif'][piexif.ExifIFD.DateTimeOriginal] = date_str | |
exif_dict['Exif'][piexif.ExifIFD.DateTimeDigitized] = date_str | |
exif_dict['Exif'][piexif.ExifIFD.OffsetTime] = "+00:00" | |
exif_dict['Exif'][piexif.ExifIFD.OffsetTimeDigitized] = "+00:00" | |
exif_dict['Exif'][piexif.ExifIFD.OffsetTimeOriginal] = "+00:00" | |
piexif.insert(piexif.dump(exif_dict), fpath) | |
os.utime(fpath, (captured_at, captured_at)) | |
def main(): | |
SOURCE_DIR = './No dates' | |
OUT_DIR = './New dates' | |
UNKNOWN_DIR = os.path.join(OUT_DIR, 'Unknown') | |
os.makedirs(OUT_DIR, exist_ok=True) | |
os.makedirs(UNKNOWN_DIR, exist_ok=True) | |
for fpath in iterate_images(SOURCE_DIR): | |
destination = os.path.join(OUT_DIR, os.path.basename(fpath)) | |
if os.path.exists(destination): | |
print('Already exists', destination) | |
continue | |
captured_at = retrieve_captured_timestamp(fpath) | |
if captured_at is None: | |
shutil.copy2(fpath, os.path.join(UNKNOWN_DIR, os.path.basename(fpath))) | |
else: | |
shutil.copy2(fpath, destination) | |
adjust_date(destination, captured_at) | |
if __name__ == '__main__': | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment