#!/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()