|
| 1 | +#!/bin/bash |
| 2 | +# order_photos_by_exif_date |
| 3 | + |
| 4 | +# When photo's from multiple sources are thrown into one directory it might |
| 5 | +# very well happen that the filenames screw the order. You see the serie of |
| 6 | +# camera one first, then the other. |
| 7 | + |
| 8 | +# This renames all photo's to YYYYMMDD_HHMMSS.SubSec_DESCRIPTION_###.jpg by using the date from exif |
| 9 | +# information. |
| 10 | +# |
| 11 | +# The ### part is an optional random 3 digit number. If multiple photo's are |
| 12 | +# taken on the same time the serial will be incremented. |
| 13 | + |
| 14 | +# CHANGELOG: |
| 15 | +# 2019-10-04 A.Swen created. |
| 16 | + |
| 17 | +# SETTINGS |
| 18 | +date=$(date +%Y%m%d) |
| 19 | +me=$(basename $0) |
| 20 | +mydir=$(dirname $0) |
| 21 | + |
| 22 | +# FUNCTIONS |
| 23 | +die () { |
| 24 | + rc=$1 |
| 25 | + shift |
| 26 | + printf '%s\n' "=====================" >&2 |
| 27 | + printf '%s\n' "==== FATAL ERROR ====" >&2 |
| 28 | + printf '%s\n\n' "=====================" >&2 |
| 29 | + printf '%s\n\n' "$@" >&2 |
| 30 | + exit $rc |
| 31 | +} |
| 32 | + |
| 33 | +usage () { |
| 34 | + printf '%s\n' "===============" >&2 |
| 35 | + printf '%s\n' "==== USAGE ====" >&2 |
| 36 | + printf '%s\n\n' "===============" >&2 |
| 37 | + printf '%s\n' "Usage: ${me} [-d|--directory <directory>] [-D|--description <description>]" >&2 |
| 38 | + printf '%s\n\n' "example: ${me} " >&2 |
| 39 | + printf '%s\n\n' "example: ${me} -d /data/foto/bla -D 'A night at the opera'" >&2 |
| 40 | + exit 1 |
| 41 | +} |
| 42 | +get_options () { |
| 43 | + while [[ $# -gt 0 ]]; do |
| 44 | + case "$1" in |
| 45 | + --directory|-d) |
| 46 | + shift |
| 47 | + declare -r directory="$1" |
| 48 | + shift |
| 49 | + ;; |
| 50 | + --description|-D) |
| 51 | + shift |
| 52 | + declare -r description="_${1}" |
| 53 | + shift |
| 54 | + ;; |
| 55 | + -h|--help) |
| 56 | + usage |
| 57 | + ;; |
| 58 | + *) |
| 59 | + usage |
| 60 | + ;; |
| 61 | + esac |
| 62 | + done |
| 63 | +} |
| 64 | + |
| 65 | +log () { printf '%s %s\n' "$(date +%F' '%T)" "$@"; } |
| 66 | + |
| 67 | +# SCRIPT |
| 68 | +log "Started ${me}" |
| 69 | + |
| 70 | +# Without exiftool we can't do much |
| 71 | +which exiftool 2>&1 > /dev/null || die 1 "Exiftool not found" |
| 72 | +which ffprobe 2>&1 > /dev/null || die 1 "Ffprobe not found" |
| 73 | +unset directory description |
| 74 | + |
| 75 | +if [[ $# -gt 0 ]];then |
| 76 | + get_options |
| 77 | +fi |
| 78 | +[ -z "$directory" ] && directory="$(pwd)" |
| 79 | +[ -d "$directory" ] || die 2 "Directory '$directory' not found" |
| 80 | + |
| 81 | +find "$directory" -iname \*.jpg -o -iname \*.jpeg -o -iname \*.dng -o -iname \*.mp4|while read original_filename;do |
| 82 | + |
| 83 | + unset create_date_time |
| 84 | + |
| 85 | + # Try to get the date/time from the file. |
| 86 | + case $original_filename in |
| 87 | + *.jpg|*.JPG|*.jpeg) |
| 88 | + create_date_time=$(exiftool -S -SubSecCreateDate "$original_filename"|sed -e 's?SubSecCreateDate: ??g' -e 's?:??g' -e 's? ?_?' -e 's?\.?_?g') |
| 89 | + if [ -z "$create_date_time" ];then |
| 90 | + # Try the DateTimeOriginal field |
| 91 | + create_date_time=$(exiftool -S -DateTimeOriginal "$original_filename"|sed -e 's?DateTimeOriginal: ??g' -e 's?:??g' -e 's? ?_?' -e 's?\.?_?g') |
| 92 | + fi |
| 93 | + if [ -z "$create_date_time" ];then |
| 94 | + # Try the GPSDateTime field |
| 95 | + unset gps_date_time |
| 96 | + gps_date_time=$(exiftool -S -GPSDateTime "$original_filename"|sed -e 's?GPSDateTime: ??' -e 's?:?-?' -e 's?:?-?' -e 's? ?T?') |
| 97 | + if [ -n "$gps_date_time" ];then |
| 98 | + create_date_time=$(date -d "$gps_date_time" +%Y%m%d_%H%M%S) |
| 99 | + fi |
| 100 | + fi |
| 101 | + if [ -z "$create_date_time" ];then |
| 102 | + # Try the FileModifyDate field |
| 103 | + file_modify_date=$(exiftool -S -FileModifyDate "$original_filename"|sed -e 's?FileModifyDate: ??' -e 's?:?-?' -e 's?:?-?') |
| 104 | + if [ -n "$file_modify_date" ];then |
| 105 | + create_date_time=$(date -d "$file_modify_date" +%Y%m%d_%H%M%S) |
| 106 | + fi |
| 107 | + fi |
| 108 | + ext=jpg |
| 109 | + ;; |
| 110 | + *.dng|*.DNG) |
| 111 | + create_date_time=$(exiftool -S -DateTimeOriginal "$original_filename"|sed -e 's?DateTimeOriginal: ??g' -e 's?:??g' -e 's? ?_?' -e 's?\.?_?g') || die 4 "exiftool failed on $original_filename" |
| 112 | + ext=dng |
| 113 | + ;; |
| 114 | + *.mp4|*MP4) |
| 115 | + create_date_time=$(date -d $(ffprobe "$original_filename" 2>&1 |grep creation_time|head -1|sed -e 's?\s\+creation_time\s\+:\s\+??') +%Y%m%d_%H%M%S)||die 5 "Cannot get creation_time from file $original_filename". |
| 116 | + ext=mp4 |
| 117 | + ;; |
| 118 | + *) |
| 119 | + log "I don't know how to get a date from $original_filename" |
| 120 | + continue |
| 121 | + ;; |
| 122 | + esac |
| 123 | + |
| 124 | + # If no create_date_time can be extracted we can't do much more. |
| 125 | + if [ -z "$create_date_time" ];then |
| 126 | + fn=$(basename "$original_filename") |
| 127 | + log "Can't find a date/time for $fn. We don't change it." |
| 128 | + unset fn |
| 129 | + continue |
| 130 | + fi |
| 131 | + |
| 132 | + # With the information we have we can construct a new filename |
| 133 | + new_filename="$directory/${create_date_time}${description}.$ext" |
| 134 | + |
| 135 | + # Check for duplicates. Should there be one: add a random number. |
| 136 | + until [ ! -f "$new_filename" ];do |
| 137 | + log "$new_filename exists, choosing new random serial." |
| 138 | + new_filename="$directory/${create_date_time}${description}_$((RANDOM % 999)).$ext" |
| 139 | + done |
| 140 | + |
| 141 | + # Rename the file. |
| 142 | + echo Renaming \"$(basename "$original_filename")\" to \"$(basename "$new_filename")\" |
| 143 | + mv "$original_filename" "$new_filename" |
| 144 | +done |
| 145 | + |
| 146 | +# END |
0 commit comments