Skip to content

Instantly share code, notes, and snippets.

@zealot128
Last active February 2, 2023 15:48
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save zealot128/0c8a2b7d830ec521bc649e2e8f2fa6fb to your computer and use it in GitHub Desktop.
Save zealot128/0c8a2b7d830ec521bc649e2e8f2fa6fb to your computer and use it in GitHub Desktop.
Exif DateTimeOriginal Mass-Fix
require 'bundler/inline'
gemfile do
source 'https://rubygems.org'
# gem 'exifr'
gem 'mini_exiftool'
gem 'activesupport', require: 'active_support/all'
gem 'pry'
end
# Expects images in:
# YEAR/YYYY-MM-DD/*.jpg
# Will adjust the datetimeoriginal if more than 1 day difference or broken old format
# Will fix DateTimeOriginal if it is missing OR
all = Dir['*/**/*.jpg', '*/**/*.jpeg', '*/**/*.JPG', '*/**/*.JPEG']
# to improve speed after rerun pass a picture to skip until
last = "2009/2009-01-31/glaschuete.JPG"
last = nil
class ImageFix
attr_reader :file
def initialize(file)
@file = file
end
def run
unless should_be_date
log :error, "could not find should-be-date in path"
exit 1
end
dt = exif["datetimeoriginal"] || exif["createdate"]
if dt.is_a?(String)
fixup_broken_date(dt)
end
if skip?(dt)
print '.'
return
end
# +0.5 to go to middle of the day
update_date = Date.parse(should_be_date).to_datetime + 0.5
exif["datetimeoriginal"] = update_date
exif["createdate"] = update_date
log :update, "update #{update_date} (was '#{dt}')"
exif.save
end
private
def log(type, msg)
puts "\n[#{type.to_s.upcase}] @ #{@file} #{msg}"
end
def skip?(dt)
# no existing Exif date
return false unless dt
# partial match -> nothing
return true if dt.to_s[should_be_date]
# OK if within 1 day LATER
target = should_be_date.to_date
dt.to_date <= target.to_date + 1
end
def fixup_broken_date(dt)
if dt[/(\d\d\d\d):(\d?\d):(\d?\d)(.*)/]
# use .rjust(2, "0") to pad with leading zeros
new_dt = "#{$1}-#{$2.rjust(2, "0")}-#{$3.rjust(2, "0")}#{$4}"
log :fix, "fixing date format from '#{dt}' to '#{new_dt}'"
dt = Time.parse(new_dt)
exif["datetimeoriginal"] = dt
exif["createdate"] = dt
exif.save
else
log :warn, "could not fixup date '#{dt}'"
end
end
def exif
@exif ||= MiniExiftool.new(file)
end
def should_be_date
@should_be_date ||= @file.split('/').find { |i| i =~ /\d{4}-\d{2}-\d{2}/ }
end
end
todo = if last
all.drop_while { |i| i != last }
else
all
end
todo.each do |file|
ImageFix.new(file).run
end

Mass-Fix EXIF date of photo collection, especially scans, social media files received etc.

If you have your photos correctly sorted in a way:

*/2007-01-01/*.jpg

that script can loop through all and check if the exif DateTimeOriginal matches with the folder.

It will FIX the date, if:

  • There is no DateTimeOriginal
  • DateTimeOriginal is broken but fixable (Older cameras, like my Canon Powershop produced 2005:2:3 HH:MM_
  • The folder's name=date is (at least 1 day) OLDER than the current exif suggests (e.g. Scans, whatsapp photos etc.)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment