Last active
October 4, 2023 15:39
-
-
Save paceaux/3e3cb88aacfc92560504dcae282086eb to your computer and use it in GitHub Desktop.
Camera Phone Image Sorter
This file contains hidden or 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
# 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:]) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
In the event file names are
yyyymmdd_hhmmss
: