Iterates through a directory, reading the EXIF data from each jpg/jpeg file. Parses the date/time from EXIF data and:
- if it differs from file modification date/time then changes file date/time
- moves file to
YYYY/YYYY_MM_DD
directory
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 | #!/usr/bin/env python
__version__ = '$Id: exif_redater.py 2294 2014-12-11 10:04:54Z mn $'
USAGE = "exif_redater.py\n\tredate .jpg/.jpeg files according to EXIF data"
"""
Iterates through a directory, reading the EXIF data from each jpg/jpeg file.
Parses the date/time from EXIF data and:
1. if it differs from file modification date/time then changes file date/time
2. moves file to YYYY/YYYY_MM_DD directory
author: Michal Niklas
"""
import os
import os.path
import shutil
import sys
import time
import traceback
try:
from PIL import Image
except:
pass
try:
import exifread
except:
pass
ALL_CNT = 0
CHANGED_CNT = 0
DEBUG = 0
# which file should be checked
EXTENSIONS = ('.jpg', '.jpeg')
# what tags use to redate file (use first found)
DT_TAGS = ["Image DateTime", "EXIF DateTimeOriginal", "DateTime"]
def log_error(s):
sys.stderr.write('%s\n' % (s))
def show_fdt(fdt):
"""human readable format of file modification datetime"""
return time.strftime("%Y-%m-%d %H:%M:%S", time.gmtime(fdt))
def exif_info2time(ts):
"""changes EXIF date ('2005:10:20 23:22:28') to number of seconds since 1970-01-01"""
tpl = time.strptime(ts + 'UTC', '%Y:%m:%d %H:%M:%S%Z')
return time.mktime(tpl)
MAX_DIFF = 130.0
def get_exif_date_exif(jpegfn):
"""return EXIF datetime using exifread (formerly EXIF)"""
dt_value = None
f = open(jpegfn, 'rb')
try:
tags = exifread.process_file(f)
if DEBUG:
print('tags cnt: %d' % len(tags))
print('\n'.join(tags))
for dt_tag in DT_TAGS:
try:
dt_value = '%s' % tags[dt_tag]
if DEBUG:
print('%s: %s' % (dt_tag, dt_value))
break
except:
continue
if dt_value:
exif_time = exif_info2time(dt_value)
return exif_time
finally:
f.close()
return None
def get_exif_date_pil(jpegfn):
"""return EXIF datetime using PIL"""
im = Image.open(jpegfn)
if hasattr(im, '_getexif'):
exifdata = im._getexif()
dt_value = exifdata[0x9003]
exif_time = exif_info2time(dt_value)
return exif_time
return None
def redate_by_exif(fn):
"""reads EXIF from jpg/jpeg and if file datetime differs from EXIF changes file date"""
global ALL_CNT, CHANGED_CNT
ALL_CNT += 1
exif_time = None
s = os.stat(fn)
file_time = s[8]
if DEBUG:
print(fn)
try:
exif_time = get_exif_date_pil(fn)
except:
s1 = traceback.format_exc()
try:
exif_time = get_exif_date_exif(fn)
except:
s2 = traceback.format_exc()
print('Something is terribly wrong! Both PIL and exifread raises exception')
print('-' * 20)
print(s1)
print('-' * 20)
print(s2)
print('-' * 20)
print('-' * 20)
if exif_time:
dir_n = time.strftime("%Y/%Y_%m_%d", time.gmtime(exif_time))
try:
os.makedirs(dir_n)
except:
pass
secs_diff = file_time - exif_time
print("%s %s -> %s (%s)" % (fn, show_fdt(file_time), show_fdt(exif_time), dir_n))
if secs_diff > MAX_DIFF or secs_diff < -MAX_DIFF:
os.utime(fn, (exif_time, exif_time))
CHANGED_CNT += 1
shutil.move(fn, dir_n)
def process_dir(_, dir_name, files):
"""looks for .jpg/.jpeg file in dir"""
sys.stdout.write('%-70s\r' % (dir_name))
for fname in files:
can_change = False
fnl = fname.lower()
for ext in EXTENSIONS:
if fnl.endswith(ext):
can_change = True
break
if can_change:
fname = os.path.join(dir_name, fname)
if os.path.isfile(fname):
redate_by_exif(fname)
def main():
os.path.walk('.', process_dir, None)
print('\nChecked: %d\nChanged %d\n' % (ALL_CNT, CHANGED_CNT))
def test():
redate_by_exif('grunwald.jpg')
if __name__ == '__main__':
if '--version' in sys.argv:
print(__version__)
elif '--help' in sys.argv:
print(USAGE)
elif '--test' in sys.argv:
test()
else:
main()
|
Works with PIL
or exifread
(formerly EXIF
).
Chnaged posixpath to os.path. Thanks Frederic Mantegazza :)
I also extended EXIF tags to look for. My Canon writes "Image DateTime" while hp photosmart 120 does not, but hp writes "EXIF DateTimeOriginal". You should check what tags you camera writes.
posixpath import. Is there any reason to directly import the posixpath module, instead of os.path? I think this recipe works on all plateformes...
Changed. Changed posixpath to os.path. Thanks :)
Use os.path.join(). Instead of
you may want to use
for more platform indepedent.
OK. Changed.
Python Image Library (PIL) extracts EXIF date in smaller amount of code:
Sample: http://code.activestate.com/recipes/576646/
I changed it to work with both
PIL
andexifread
(formerlyEXIF
). Unfortunately some photos made with Olympus camera cannot be read withexifread
(see: https://github.com/ianare/exif-py/issues/42 )