Skip to content

Instantly share code, notes, and snippets.

@paceaux
Last active October 4, 2023 15:39
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save paceaux/3e3cb88aacfc92560504dcae282086eb to your computer and use it in GitHub Desktop.
Save paceaux/3e3cb88aacfc92560504dcae282086eb to your computer and use it in GitHub Desktop.
Camera Phone Image Sorter
# Description:
# Sorts files created with a date and time in the file name, puts them in their respective folders
# This was created to sort images taken with a Samsung Galaxy phone
# Expected file naming convention is
# "year-month-day hour.minute.second.fileextension"
# "2017-7-5 18.23.45.jpg"
#
#
# requires Pillow:
# pip install Pillow
#
#
# Arguments:
# -p, --path : fully qualified path to folder
# -e, --extName : file extension
# default: jpg
# -s, --sort : time parameters to sort by;
# default: year,month
# options: year,month,day,hour,minute,second
# Note:
# -t hour,minute will NOT generate a folder for year and month
# -t, --timeSource: where to get the time
# default: filename
# options: filename, os, exif
# -d, --destination: where the sorted folders will be
# default: current directory
#
# Sample Usages
#
# Sort current directory of jpg into year and month
# python sorter.py
#
# sort specific directory's videos into year and month
# python sorter.py -p [full/path/to/folder] -e mp4
#
# Sort current directory of png into month and hour:
# python sorter.py -e png --d month,hour
#
# Sort current jpgs by exif data into year, month, day
# python sorter.py -e jpg -t exif -s year,month,day
#
import os, glob, datetime, shutil, sys, getopt, time, fnmatch
from collections import namedtuple
from PIL import Image
from PIL.ExifTags import TAGS, GPSTAGS
from os.path import join
_TAGS_r = dict(((v, k) for k, v in TAGS.items()))
_GPSTAGS_r = dict(((v, k) for k, v in GPSTAGS.items()))
# CLASSES:
class FileRules:
def __init__(self):
self.path = os.getcwd()
self.fileExtension = "jpg"
self.timeSource = "filename" # options: filename, os, exif
self.destination = ""
def getFilePattern(self):
return "*.{0}".format(self.fileExtension)
def PathAndType(self):
return join("/", self.path, self.getFilePattern())
class FilterRules:
def __init__(self):
self.years = True
self.months = True
self.days = False
self.hours = False
self.minutes = False
self.seconds = False
# GETTERS
def getFiles(fileRules):
fileList = []
directoryList = os.listdir(fileRules.path)
for entry in directoryList:
if fnmatch.fnmatch(entry, fileRules.getFilePattern()):
fileList.append(entry)
return fileList
def getUsedExifTags(exifData):
usedKeys = []
if (exifData is not None):
allKeys = list(exifData.keys())
usedKeys = [k for k in allKeys if k in TAGS]
return usedKeys
def getExifTags(file):
img = Image.open(file)
exifData = img._getexif()
usedKeys = getUsedExifTags(exifData)
tags = {}
for usedKey in usedKeys:
tags[TAGS[usedKey]] = exifData[usedKey]
return tags
def getTimeTuple(yearMonthDay, hourMinuteSecond):
time = namedtuple('time', 'year month day hour minute second')
time.year = yearMonthDay[0]
time.month = yearMonthDay[1]
time.day = yearMonthDay[2]
time.hour = hourMinuteSecond[0]
time.minute = hourMinuteSecond[1]
time.second = hourMinuteSecond[2]
return time
def getCreateTimeFromExif(file):
exifTags = getExifTags(file)
datetime = exifTags['DateTimeOriginal']
dateHourSegments = datetime.split(' ')
yearMonthDay = dateHourSegments[0].split(':')
hourMinuteSecond = dateHourSegments[1].split(':')
time = getTimeTuple(yearMonthDay, hourMinuteSecond)
return time
def getCreateTimeFromOs(file):
time = os.path.getctime(file)
return datetime.datetime.fromtimestamp(time)
def getCreateTimeFromFileName(file):
fileName = os.path.basename(file)
dateHourSegments = fileName.split(' ')
yearMonthDay = dateHourSegments[0].split('-')
hourMinuteSecond = dateHourSegments[1].split('.')
time = getTimeTuple(yearMonthDay, hourMinuteSecond)
return time
def getCreateTime(file, fileRules):
createTime = getCreateTimeFromFileName(file)
if (fileRules.timeSource == "os"):
createTime = getCreateTimeFromOs(file)
if (fileRules.timeSource == "exif"):
createTime = getCreateTimeFromExif(file)
return createTime
def getFileDestination(createTime, filterRules, destination):
year = str(createTime.year) if filterRules.years == True else ""
month = str(createTime.month) if filterRules.months == True else ""
day = str(createTime.day) if filterRules.days == True else ""
hour = str(createTime.hour) if filterRules.hours == True else ""
minute = str(createTime.minute) if filterRules.minutes == True else ""
second = str(createTime.second) if filterRules.seconds == True else ""
destination = join("/", destination, year, month, day, hour, minute, second)
return destination
def createDirectory(path, dirName):
if not os.path.exists(path+dirName):
os.makedirs(path+dirName)
def moveFile(file, filterRules, fileRules):
createdTime = getCreateTime(file, fileRules)
fileDestination = getFileDestination(createdTime, filterRules, fileRules.destination)
createDirectory(fileRules.path,fileDestination)
try:
shutil.move(file, fileRules.path + "/" + fileDestination)
except:
print("{0} already exists in that location".format(file))
pass
def main(argv):
fileRules = FileRules()
filterRules = FilterRules()
try:
opts, args = getopt.getopt(argv, "p:e:s:t:d:", ["path=", "extName=", "sort=", "timeSource=", "destination="])
except getopt.GetoptError:
print("Error with directory")
sys.exit(2)
for opt, arg in opts:
if opt in ("-p", "--path"):
fileRules.path = arg
elif opt in ("-e", "--extName"):
fileRules.fileExtension = arg
elif opt in ("-t", "--timeSource"):
fileRules.timeSource = arg
elif opt in ("-s", "--sort"):
if "day" in arg : filterRules.days = True
if "hour" in arg : filterRules.hours = True
if "minute" in arg : filterRules.minutes = True
if "second" in arg : filterRules.seconds = True
elif opt in ("-d", "--destination"):
fileRules.destination = arg
print("Moving files with the extension {0} \n in the directory {1} \n based on the time from {2}".format(fileRules.fileExtension, fileRules.path, fileRules.timeSource))
listOfFiles = getFiles(fileRules)
for file in listOfFiles:
moveFile(file, filterRules, fileRules)
if __name__ == "__main__":
main(sys.argv[1:])
@paceaux
Copy link
Author

paceaux commented Oct 4, 2023

In the event file names are yyyymmdd_hhmmss:

def getCreateTimeFromFileName(file):
    fileName = os.path.basename(file)
    dateTimeSegments = fileName.split('_')
    ymdSegment = dateTimeSegments[0]
    hmsSegment = dateTimeSegments[1]
    year = ymdSegment[:4]
    month = ymdSegment[4:6]
    day = ymdSegment[6:8]
    hour = hmsSegment[:2]
    minute = hmsSegment[2:4]
    second = hmsSegment[4:6]
    yearMonthDay = [year, month, day]
    hourMinuteSecond = [hour, minute, second]

    time = getTimeTuple(yearMonthDay, hourMinuteSecond)

    return time

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment