Created
February 22, 2020 18:15
-
-
Save Tojaj/1352245c1a2293400a91d00dd2487d58 to your computer and use it in GitHub Desktop.
Rename jpeg files in a directory based on the date &time in EXIF metadata
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/env python3 | |
import os | |
import os.path | |
import datetime | |
import argparse | |
from PIL import Image | |
EXIF_TAG_DATETIME = 306 # When taken or last edited | |
EXIF_TAG_DATETIMEORIGINAL = 36867 # When taken | |
#EXIF_TAG_DATETIMEDIGITIZED = 36868 # When stored as digital data | |
EXIF_TAG_SUBSECTIMEORIGINAL = 37521 # Franctions of secs for DateTimeOriginal | |
JPEG_SUFFIX = ".jpg" | |
def get_exif(filename): | |
"""Get dict with EXIF data""" | |
image = Image.open(filename) | |
image.verify() | |
return image._getexif() | |
def get_datetime(exif): | |
"""Return datetime python object for date time data from EXIF""" | |
# EXIF data time format example: "2019:12:22 16:52:40" | |
if EXIF_TAG_DATETIMEORIGINAL in exif: | |
raw_date_time = exif[EXIF_TAG_DATETIMEORIGINAL] | |
elif EXIF_TAG_DATETIME in exif: | |
raw_date_time = exif[EXIF_TAG_DATETIME] | |
else: | |
return None | |
return datetime.datetime.strptime(raw_date_time, "%Y:%m:%d %H:%M:%S") | |
def rename(directory, test=False): | |
"""Rename file in the directory""" | |
files = sorted(os.listdir(directory)) | |
mapping = {} | |
for fn in files: | |
if not fn.endswith((".jpg", ".JPG", ".jpeg", ".JPEG")): | |
print("Skipping non JPEG file: {}".format(fn)) | |
continue | |
filename = os.path.join(directory, fn) | |
exif = get_exif(filename) | |
dtobj = get_datetime(exif) | |
# Pick new filename | |
fn_prefix = os.path.join( | |
directory, | |
dtobj.strftime("%Y%m%d_%H%M%S") | |
) | |
newfn = fn_prefix + JPEG_SUFFIX | |
if filename == newfn: | |
# Nothing to do | |
continue | |
# If the file already exists, increment counter | |
i = 1 | |
while os.path.exists(newfn): | |
newfn = fn_prefix + "_" + str(i) + JPEG_SUFFIX | |
i += 1 | |
mapping[filename] = newfn | |
if not mapping: | |
print("Nothing to do") | |
return | |
if test: | |
print("Test mode used, printing what would be done...") | |
else: | |
print("Renaming...") | |
for src, dest in mapping.items(): | |
print("{} -> {}".format(src, dest)) | |
if not test: | |
os.rename(src, dest) | |
if test: | |
print("Test mode used, no rename was done!") | |
if __name__ == "__main__": | |
parser = argparse.ArgumentParser( | |
description="Rename jpeg files to YYYYMMDD-HHMMSS[-X].jpg " \ | |
"format based on date &time data in EXIF metadata." | |
) | |
parser.add_argument( | |
"directory", | |
metavar="DIRECTORY", | |
help="Directory where to rename the files." | |
) | |
parser.add_argument( | |
"-t", "--test", | |
action="store_true", | |
help="Just print what would be done" | |
) | |
args = parser.parse_args() | |
# Check args | |
if not os.path.isdir(args.directory): | |
parser.error(f"{args.directory} is not a directory!") | |
rename(args.directory, test=args.test) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment