#!/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()
Diff to Previous Revision
--- revision 5 2008-09-04 08:21:33
+++ revision 6 2014-12-11 10:07:39
@@ -1,32 +1,49 @@
-#!/usr/bin/python
+#!/usr/bin/env python
-__version__ = '$Id: exif_redater.py 470 2008-03-06 06:40:14Z mn $'
+__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 if it differs from file
-modification date/time then changes file date/time
+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
-import EXIF
+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):
@@ -36,42 +53,90 @@
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, '%Y:%m:%d %H:%M:%S')
+ 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
- dt_value = None
exif_time = None
s = os.stat(fn)
- f = open(fn, 'rb')
+ file_time = s[8]
+ if DEBUG:
+ print(fn)
try:
- tags = EXIF.process_file(f)
- for dt_tag in DT_TAGS:
- try:
- dt_value = "%s" % tags[dt_tag]
- break
- except:
- continue
- if dt_value:
- file_time = s[8]
- exif_time = exif_info2time(dt_value)
- finally:
- f.close()
+ 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
- MAX_DIFF = 130.0
+ 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:
- print "%s %s -> %s" % (fn, show_fdt(file_time), show_fdt(exif_time))
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()
@@ -82,22 +147,24 @@
if can_change:
fname = os.path.join(dir_name, fname)
if os.path.isfile(fname):
- try:
- redate_by_exif(fname)
- except:
- pass
+ redate_by_exif(fname)
def main():
os.path.walk('.', process_dir, None)
- print '\nChecked: %d\nChanged %d\n' % (ALL_CNT, CHANGED_CNT)
+ print('\nChecked: %d\nChanged %d\n' % (ALL_CNT, CHANGED_CNT))
-if '--version' in sys.argv:
- print __version__
-else:
- if __name__ == '__main__':
- if '--help' in sys.argv:
- print USAGE
- else:
- main()
+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()