Last active
June 20, 2018 19:52
-
-
Save avdyushin/e6068aa152bb7a51187026da0a9adb0c to your computer and use it in GitHub Desktop.
Reorder photos by Exif date
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
#!/usr/bin/python | |
# coding: utf8 | |
import io | |
import re | |
import time | |
import shutil | |
import os.path | |
import hashlib | |
import argparse | |
import mimetypes | |
import exifread | |
from datetime import datetime | |
class PreparePhotos: | |
def __init__(self, target_folder = ".", destination_folder = "./output"): | |
self.target_folder = target_folder | |
self.destination_folder = destination_folder | |
def sync(self, args): | |
for directory, _, files in os.walk(self.target_folder): | |
for filename in files: | |
path = os.path.join(directory, filename) | |
mimetype, _ = mimetypes.guess_type(path) | |
name, ext = os.path.splitext(path) | |
if (mimetype and mimetype.startswith('image/')) or ext.upper() == '.HEIC': | |
tags = exifread.process_file(open(path, 'rb'), details=False) | |
self.move(path, tags) | |
print 'Done' | |
def md5(self, path): | |
hash_md5 = hashlib.md5() | |
with open(path, 'rb') as f: | |
for chunk in iter(lambda: f.read(4096), b""): | |
hash_md5.update(chunk) | |
return hash_md5.hexdigest() | |
def move(self, path, tags): | |
date = None | |
if 'EXIF DateTimeOriginal' in tags.keys(): | |
# 2013:06:01 18:26:49 | |
date = str(tags['EXIF DateTimeOriginal']) | |
try: | |
date = datetime.strptime(date, '%Y:%m:%d %H:%M:%S') | |
except ValueError: | |
date = datetime.fromtimestamp(os.stat(path).st_birthtime) | |
else: | |
date = datetime.fromtimestamp(os.stat(path).st_birthtime) | |
model = '' | |
if 'Image Model' in tags.keys(): | |
model = '_' + str(tags['Image Model']).replace(' ', '_') | |
filename, ext = os.path.splitext(path) | |
yearstr = date.strftime('%Y') | |
datestr = date.strftime('%Y-%m-%d') | |
source_dir = os.path.join(self.destination_folder, yearstr, datestr) | |
print 'Moving', path, '...' | |
if not os.path.isdir(source_dir): | |
print 'Creating', source_dir, '...' | |
os.makedirs(source_dir) | |
source_name = date.strftime('%Y%m%d-%H%M%S') + model | |
source_fullpath = os.path.join(source_dir, source_name + ext) | |
if os.path.exists(source_fullpath): | |
if self.md5(path) == self.md5(source_fullpath): | |
print 'Skipping', source_fullpath, '...' | |
return source_fullpath | |
postfix = 1 | |
while True: | |
new_fullpath = os.path.join(source_dir, source_name + '-' + str(postfix) + ext) | |
if os.path.exists(new_fullpath) and self.md5(path) == self.md5(new_fullpath): | |
print 'Skipping', new_fullpath, '...' | |
return new_fullpath | |
if not os.path.exists(new_fullpath): | |
source_fullpath = new_fullpath | |
break | |
postfix += 1 | |
utime = time.mktime(date.timetuple()) | |
shutil.copy(path, source_fullpath) | |
os.utime(source_fullpath, (utime, utime)) | |
print '->', source_fullpath | |
return source_fullpath | |
parser = argparse.ArgumentParser(description="Foo") | |
parser.add_argument("--quite", help="quite") | |
parser.add_argument("target_folder", help="target folder") | |
parser.add_argument("destination_folder", help="destination folder") | |
args = parser.parse_args() | |
tf = args.target_folder | |
df = args.destination_folder | |
#tf = "photos" | |
#df = "output" | |
pp = PreparePhotos(target_folder = tf, destination_folder = df) | |
pp.sync(args) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment