Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save apetersson/3ae4575c2598f7a4188129e220a22150 to your computer and use it in GitHub Desktop.
Save apetersson/3ae4575c2598f7a4188129e220a22150 to your computer and use it in GitHub Desktop.
#!/bin/bash
# Requirements:
# - Parameter checking: The script accepts at least one parameter (the base folder).
# An optional second parameter "scan" activates the scan mode, which only shows missing
# or incorrect DateTimeOriginal tags, without making any changes.
#
# - Extract date from folder names: The script extracts the date from the name
# of the folder. Supported formats are YYYY-MM-DD and YYYY-MM, where for the latter
# the day 01 is automatically added.
#
# - Scan mode: In scan mode, the script scans all files in a folder
# (and all subfolders) and lists those where the DateTimeOriginal tag
# is missing or set to an invalid date.
#
# - Correction mode: In correction mode, the script sets the DateTimeOriginal tag for all
# files without a valid date to the date extracted from the folder name. Invalid
# tags are also corrected. For 3gp and MP4 files, this is only done if errors were found in scan mode.
# For other files, it is done regardless.
#
# - Efficient processing: To maximize efficiency, ExifTool should be operated in "batch" mode
# by calling it once per folder instead of separate calls for each file. This reduces the startup time
# of ExifTool and improves overall performance.
#
# - Supported file types: Processing of files with the extensions .jpg, .jpeg, .png,
# .mp4, and .avi.
#
# - Recursive processing: Recursively traverse all subfolders of the specified base folder,
# to perform the above operations at each level of the folder structure.
#
# - User feedback: Informative outputs show the progress of processing, including
# warnings when no valid data can be found for a folder. The processing
# is done in alphabetical order of folder names.
if [ $# -lt 1 ] || [ $# -gt 2 ]; then
echo "Usage: $0 <base folder> [scan]"
exit 1
fi
base_dir="$1"
scan_only=false
if [ "$2" == "scan" ]; then
scan_only=true
fi
# Function to extract the date from the folder name
extract_date_from_folder_name() {
local folder_name="$1"
# Extension of the Regex to recognize the format YYYY-MM and automatically add the day 01
if [[ $folder_name =~ ([0-9]{4})-([0-9]{2})-([0-9]{2}) ]]; then
echo "${BASH_REMATCH[1]}-${BASH_REMATCH[2]}-${BASH_REMATCH[3]}"
return 0
elif [[ $folder_name =~ ([0-9]{4})-([0-9]{2}) ]]; then
echo "${BASH_REMATCH[1]}-${BASH_REMATCH[2]}-01"
return 0
else
echo ""
return 1
fi
}
# Function to scan or correct EXIF data
process_exif_data() {
local folder_path="$1"
local date_from_folder="$2"
echo "Processing folder: $folder_path"
local files=()
while IFS= read -r -d '' file; do
files+=("$file")
done < <(find "$folder_path" -maxdepth 1 -type f \( -iname "*.jpg" -o -iname "*.jpeg" -o -iname "*.png" -o -iname "*.mp4" -o -iname "*.3gp" \) -print0)
if [ "${#files[@]}" -gt 0 ]; then
if [ "$scan_only" == "true" ]; then
echo "Scanning for missing or invalid DateTimeOriginal tags..."
exiftool -api largefilesupport=1 -if 'not $DateTimeOriginal or $DateTimeOriginal =~ /^0000/' -filename -DateTimeOriginal "${files[@]}"
else
echo "Correcting EXIF data based on the date $date_from_folder..."
exiftool -api largefilesupport=1 -d "%Y-%m-%d %H:%M:%S" "-DateTimeOriginal=$date_from_folder 00:00:00" -if 'not $DateTimeOriginal or $DateTimeOriginal =~ /^0000/' "${files[@]}" -overwrite_original
fi
else
echo "No supported files found in $folder_path"
fi
}
# Recursive function to traverse the folder structure
process_folder() {
local current_folder="$1"
local inherited_date="$2"
local current_folder_name=$(basename "$current_folder")
local date_from_current_folder=$(extract_date_from_folder_name "$current_folder_name")
local effective_date=${date_from_current
if [ ! -z "$effective_date" ]; then
process_exif_data "$current_folder" "$effective_date"
else
echo "Warning: No valid date found for $current_folder. Skipping..."
fi
find "$current_folder" -mindepth 1 -maxdepth 1 -type d | sort | while read subfolder; do
process_folder "$subfolder" "$effective_date"
done
}
process_folder "$base_dir" ""
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment