Last active
June 27, 2021 18:20
-
-
Save crearo/fa38892631ad3384f1bfd1f478925606 to your computer and use it in GitHub Desktop.
Add Exif Tags to Google Photo via Drive Takeout
This file contains 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
#!/usr/bin/python | |
""" | |
[ ] List out if there are images which have been missed. (ie, image was there, but no json file) | |
[ ] Put the images in various directories. | |
[ ] Add EXIF tags to each of the photos. | |
""" | |
import json | |
import os | |
import sys | |
import shutil | |
from collections import Counter | |
from pathlib import Path | |
from subprocess import PIPE, run | |
input_dir = os.getcwd() + '\\Photos' | |
output_dir = os.getcwd() + '\\out' | |
def log_warning(str): | |
print("------------------WARNING------------------") | |
print(str) | |
print("-------------------------------------------") | |
def _delete_files(dir: Path): | |
shutil.rmtree(dir) | |
def _setup_output_folder(output_path: Path): | |
if len(list(output_path.glob("**/*"))) > 0: | |
_delete_files(output_path) | |
print("Deleted all files in output directory") | |
if not output_path.exists(): | |
output_path.mkdir() | |
print("Created output directory") | |
def cmd(command): | |
# result = run(command, stdout=PIPE, stderr=PIPE, universal_newlines=True, shell=True) | |
# print(result.stderr, result.stdout) | |
# return result.stdout | |
os.system(command) | |
def add_media_metadata(json_file: Path, media_file: Path): | |
command = f'exiftool.exe -d %s -tagsfromfile {json_file.absolute()} "-GPSAltitude<GeoDataAltitude" "-GPSLatitude<GeoDataLatitude" "-GPSLatitudeRef<GeoDataLatitude" "-GPSLongitude<GeoDataLongitude" "-GPSLongitudeRef<GeoDataLongitude" "-Keywords<Tags" "-Subject<Tags" "-Caption-Abstract<Description" "-ImageDescription<Description" "-CreateDate<PhotoTakenTimeTimestamp" "-PreviewDateTime<PhotoTakenTimeTimestamp" "-DateTimeOriginal<PhotoTakenTimeTimestamp" "-ModifyDate<PhotoTakenTimeTimestamp" -ext "*" -overwrite_original {media_file.absolute()}' | |
cmd(command) | |
# pass | |
def main(): | |
output_path = Path(output_dir) | |
_setup_output_folder(output_path) | |
internal_dirs = len([f for f in os.listdir(input_dir) if Path(Path(output_path).joinpath(f)).is_dir()]) | |
if internal_dirs != 0: | |
log_warning("You have directories within the main folder. This program doesn't look inside these directories " | |
"currently, but I can add it if you would like.") | |
all_files = list(Path(input_dir).glob('**/*')) | |
json_files = list(Path(input_dir).glob('**/*.json')) | |
media_files = {} # media files with corresponding json files. | |
# using json files as the source of truth, instead of finding images and then searching for their corresponding json | |
for json_file in json_files: | |
with open(json_file.absolute(), 'r') as _file: | |
try: | |
media_name = json.load(_file)['title'] | |
except KeyError: | |
log_warning(f"No Key For Image: {json_file}") | |
sys.exit() | |
corresponding_media_path = json_file.parent.joinpath(media_name) | |
media_files[json_file] = corresponding_media_path | |
media_files_without_json = list((Counter(all_files) - Counter(json_files)).elements()) | |
if len(media_files_without_json) != 0: | |
log_warning(f"There are media files without a corresponding json! In total:{len(media_files_without_json)}" | |
"\nThese will be stored in out/unsorted.") | |
json_files_without_media = [f for f in media_files.values() if not Path(f).exists()] | |
if len(json_files_without_media) != 0: | |
log_warning(f"There are json files without corresponding media! In total:{len(json_files_without_media)}") | |
# read all json files, create the output dir, move the corresponding media to the out folder. | |
for json_file, media_file in media_files.items(): | |
if not media_file.exists(): | |
output_path_for_json_without_media = output_path.joinpath('json-without-media') | |
if not output_path_for_json_without_media.exists(): | |
output_path_for_json_without_media.mkdir() | |
shutil.copyfile(json_file, output_path_for_json_without_media.joinpath(json_file.name)) | |
print("No corresponding media file for json file:", json_file.name, "Placed json file in:", | |
output_path_for_json_without_media.name) | |
continue | |
with open(json_file.absolute(), 'r') as _file: | |
# Exif tool | |
add_media_metadata(json_file, media_file) | |
# Move the file to its corresponding folder | |
try: | |
corresponding_out_dir_name = json.load(_file)['googlePhotosOrigin']['mobileUpload']['deviceFolder'][ | |
'localFolderName'] | |
except KeyError: | |
log_warning('Key Error for ' + str(json_file.name)) | |
if corresponding_out_dir_name is None or corresponding_out_dir_name == '': | |
corresponding_out_dir_name = 'unspecified-folder' | |
corresponding_media_path = media_file.absolute() | |
corresponding_media_output_dir = output_path.joinpath(corresponding_out_dir_name) | |
if not corresponding_media_output_dir.exists(): | |
corresponding_media_output_dir.mkdir() | |
shutil.copyfile(corresponding_media_path, | |
corresponding_media_output_dir.joinpath(corresponding_media_path.name)) | |
if __name__ == '__main__': | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment