Skip to content

Instantly share code, notes, and snippets.

@jnwarp
Created September 20, 2019 13:47
Show Gist options
  • Save jnwarp/6f4248ca046846f4096f75e125ad6f37 to your computer and use it in GitHub Desktop.
Save jnwarp/6f4248ca046846f4096f75e125ad6f37 to your computer and use it in GitHub Desktop.
Parse Google Photos from Takeout and update metadata for iCloud
from PIL import Image, ExifTags
import glob, os, shutil
import re, json
import piexif
from datetime import datetime
source = "GooglePhotos"
dest = "Processed"
debug = False
def get_field(exif, field):
for tag,value in exif.items():
decodedTag = ExifTags.TAGS.get(tag, tag)
if decodedTag == field:
return value
def read_json(f, field):
if os.path.exists(f + ".json"):
f = f + ".json"
elif os.path.exists(f[:-5] + ".json"):
f = f[:-5] + ".json"
elif os.path.exists(f[:-4] + ".json"):
f = f[:-4] + ".json"
else:
return None
with open(f) as json_file:
data = json.load(json_file)
return data[field]
def get_json_file(f):
if os.path.exists(f + ".json"):
f = f + ".json"
elif os.path.exists(f[:-5] + ".json"):
f = f[:-5] + ".json"
elif os.path.exists(f[:-4] + ".json"):
f = f[:-4] + ".json"
else:
return None
return f
def set_exif_date(f, ts):
exif_dict = piexif.load(f)
exif_dict['Exif'][piexif.ExifIFD.DateTimeOriginal] = datetime.utcfromtimestamp(ts).strftime("%Y:%m:%d %H:%M:%S")
#exif_dict = {'Exif': { piexif.ExifIFD.DateTimeOriginal: datetime.utcfromtimestamp(ts).strftime("%Y:%m:%d %H:%M:%S") }}
exif_bytes = piexif.dump(exif_dict)
piexif.insert(exif_bytes, f)
def get_exif_date(f):
exif_dict = piexif.load(f)
return exif_dict['Exif'][piexif.ExifIFD.DateTimeOriginal]
def processHeic(f):
# (1).heic files are bad
badfile = re.match("(.*)\([0-9]+\)(\.[hH][eE][iI][cC])$", f)
if badfile:
#print("Skipping", f)
return
metadata = read_json(f"{f}", "photoTakenTime")
# skip files without metadata
if metadata == None:
print("Skipping", f)
print()
return
# must convert timestamp to est
ts = int(metadata['timestamp']) - 18000
timestamp = datetime.utcfromtimestamp(ts).strftime('%Y-%m-%d %H:%M:%S')
print(f)
print(" DT", "None (HEIC)")
print(" TS", timestamp)
newName = datetime.utcfromtimestamp(ts).strftime('%Y-%m-%d %H%M%S') + ".heic"
print(" COPY -> HEIC/" + newName)
shutil.copy2(f, f"{dest}/HEIC/{newName}")
shutil.copy2(get_json_file(f), f"{dest}/HEIC/{newName}"[:-5] + ".json")
print()
def processJpeg(f):
try:
img = Image.open(f)
except:
#print(f, "failed.\n")
return
metadata = read_json(f"{f}", "photoTakenTime")
# skip files without metadata
if metadata == None:
print("Skipping", f)
print()
return
exif = img._getexif()
date = get_field(exif,'DateTime')
# must convert timestamp to est
ts = int(metadata['timestamp']) - 18000
timestamp = datetime.utcfromtimestamp(ts).strftime('%Y-%m-%d %H:%M:%S')
print(f)
print(" DT", date)
print(" TS", timestamp)
newName = datetime.utcfromtimestamp(ts).strftime('%Y-%m-%d %H%M%S') + ".jpg"
print(" COPY ->", newName)
shutil.copy2(f, f"{dest}/{newName}")
if date == None:
print(" EXIF ->", newName)
set_exif_date(f"{dest}/{newName}", ts)
#shutil.copy2(f, f"{dest}/_{newName}")
print()
def processPng(f):
try:
img = Image.open(f)
except:
print(f, "failed.\n")
return
metadata = read_json(f"{f}", "photoTakenTime")
# skip files without metadata
if metadata == None:
print("Skipping", f)
print()
return
# must convert timestamp to est
ts = int(metadata['timestamp']) - 18000
timestamp = datetime.utcfromtimestamp(ts).strftime('%Y-%m-%d %H:%M:%S')
print(f)
print(" DT", "None (PNG)")
print(" TS", timestamp)
newName = datetime.utcfromtimestamp(ts).strftime('%Y-%m-%d %H%M%S') + ".png"
print(" COPY ->", newName)
shutil.copy2(f, f"{dest}/{newName}")
print()
if __name__ == "__main__":
# create missing directory
if not os.path.exists(f"{dest}/HEIC"):
os.makedirs(f"{dest}/HEIC")
''' Uncomment to process the matched HEIC files
# process converted HEIC files
for f in glob.glob(dest + "/HEIC/**", recursive=True):
jpeg = re.match("(.*)(\.[jJ][pP][eEgG]+)$", f)
png = re.match("(.*)(\.[pP][nN][gG])$", f)
if jpeg:
processJpeg(f)
if png:
processPng(f)
'''
# process all inputs
for f in glob.glob(source + "/**", recursive=True):
heic = re.match("(.*)(\.[hH][eE][iI][cC])$", f)
jpeg = re.match("(.*)(\.[jJ][pP][eEgG]+)$", f)
png = re.match("(.*)(\.[pP][nN][gG])$", f)
if heic:
processHeic(f)
if jpeg:
processJpeg(f)
if png:
processPng(f)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment