Welcome, guest | Sign In | My Account | Store | Cart
#!/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()

History