Created
February 2, 2023 17:33
-
-
Save BDeliers/f61e2c22bef49eae3b8ea1b4caa9b839 to your computer and use it in GitHub Desktop.
Automatically sort your pictures in a date-dependent directories tree !
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
#!/bin/python3 | |
""" | |
Script to make directories tree from image's EXIF shot date | |
BDeliers - January 2023 | |
""" | |
__docformat__ = 'reStructuredText' | |
import argparse | |
import datetime | |
import os | |
import shutil | |
from PIL import Image | |
EXTENSIONS = ["jpeg", "jpg", "heic", "png", "gif", "tiff", "bmp"] | |
def GetImageTakenDate(path): | |
""" | |
Input an image's path and return its taken date if exists, formatted as a datetime structure | |
""" | |
ret = None | |
im = Image.open(path) | |
exif = im.getexif() | |
if ((len(exif) > 0) and (306 in exif.keys())): | |
ret = datetime.datetime.strptime(exif[306], '%Y:%m:%d %H:%M:%S') | |
im.close() | |
return ret | |
if __name__ == "__main__": | |
""" | |
Main entry point | |
""" | |
# Parse args | |
parser = argparse.ArgumentParser(prog="PhotoOrganizer", description="Script to make directories tree from image's EXIF shot date") | |
parser.add_argument("--input", "-i", required=True, help="Input path to crawl") | |
parser.add_argument("--output", "-o", required=False, default=None, help="Output path where to create the tree (optionnal, default = Input path)") | |
parser.add_argument("--granularity", "-g", required=False, default="DAY", choices=["DAY", "MONTH", "YEAR"], help="Granularity of the tree DAY|MONTH|YEAR (optionnal, default = DAY)") | |
args = parser.parse_args() | |
INPATH = args.input | |
OUTPATH = args.output | |
GRANULARITY = args.granularity | |
if (OUTPATH == None): | |
OUTPATH = INPATH | |
# Get an absolute path if relatives are specified | |
INPATH = os.path.abspath(INPATH) | |
OUTPATH = os.path.abspath(OUTPATH) | |
print("Image files from {} will be organized and copied to {}".format(INPATH, OUTPATH)) | |
fileNames = os.listdir(INPATH) | |
for elt in fileNames: | |
# If it's an actual image | |
inFile = "{}/{}".format(INPATH, elt) | |
if (os.path.isfile(inFile) and (elt.split('.')[1].lower() in EXTENSIONS)): | |
takenDate = GetImageTakenDate(inFile) | |
# If we have a taken date, we can continue | |
if (takenDate != None): | |
if (GRANULARITY == "YEAR"): | |
outDir = "{}/{}".format(OUTPATH, takenDate.year) | |
elif (GRANULARITY == "MONTH"): | |
outDir = "{}/{}/{}".format(OUTPATH, takenDate.year, takenDate.month) | |
else: # DAY | |
outDir = "{}/{}/{}/{}".format(OUTPATH, takenDate.year, takenDate.month, takenDate.day) | |
# Else, create an "undefined" folder | |
else: | |
outDir = "{}/NoDate".format(OUTPATH) | |
# Make target directory | |
if (not os.path.exists(outDir)): | |
os.makedirs(outDir) | |
# Actually copy the file to its new location if we don't risk overwriting | |
if (not os.path.exists("{}/{}".format(outDir, elt))): | |
try: | |
shutil.copy2(inFile, outDir) | |
print("Copied file " + elt) | |
except Exception: | |
print("An error occured with the file " + elt) | |
print(Exception) | |
else: | |
print("Skipped file " + elt) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment