|
#!/bin/bash |
|
shopt -s nullglob |
|
CURRENT_DATE=$(sudo date -s "$(curl -s --head http://google.com | grep '^Date:' | sed 's/Date: //g')") |
|
echo "Current date: $CURRENT_DATE" |
|
|
|
# DESCRIBE WHAT THIS SCRIPT DOES |
|
# This script processes Google Photos Takeout backup to restore original timestamps. |
|
# It reads .supplemental-metadata.json files and applies the original timestamps to: |
|
# 1. File system timestamps (touch) |
|
# 2. EXIF metadata (DateTimeOriginal for photos, CreationDate for videos) |
|
# 3. Handles various file types: JPG, CR2, RAW, MP4, MOV, etc. |
|
|
|
# Check if required tools are installed |
|
for tool in exiftool jq; do |
|
if ! command -v "$tool" &> /dev/null; then |
|
echo "$tool could not be found. Please install it." |
|
exit 1 |
|
fi |
|
done |
|
|
|
echo "Current directory contents:" |
|
cd "/RAID/Pictures/Google Photos Backup/Takeout/Google Photos" || exit 1 |
|
ls -alh |
|
|
|
# Set your starting folder here |
|
FOLDER_NAME="" |
|
starting_point_found=0 |
|
processed_count=0 |
|
error_count=0 |
|
|
|
# Function to detect actual file type |
|
detect_file_type() { |
|
local file="$1" |
|
exiftool -FileType -b "$file" 2>/dev/null |
|
} |
|
|
|
# Function to diagnose problematic files |
|
diagnose_file() { |
|
local file="$1" |
|
echo " π Diagnosing file: $file" |
|
echo " π File size: $(du -h "$file" | cut -f1)" |
|
echo " π File permissions: $(ls -la "$file" | awk '{print $1, $3, $4}')" |
|
echo " π Current EXIF dates:" |
|
exiftool -DateTimeOriginal -CreateDate -ModifyDate -s "$file" 2>/dev/null | sed 's/^/ /' || echo " No EXIF data found" |
|
echo " π File signature: $(file "$file")" |
|
} |
|
|
|
# Function to update metadata based on file type |
|
update_metadata() { |
|
local file="$1" |
|
local timestamp="$2" |
|
local exif_date="$3" |
|
|
|
# Detect actual file type, not just extension |
|
local file_type |
|
file_type=$(detect_file_type "$file") |
|
|
|
if [[ -z "$file_type" ]]; then |
|
echo " β Could not detect file type for $file" |
|
return 1 |
|
fi |
|
|
|
echo " π File type: $file_type" |
|
|
|
case "$file_type" in |
|
"JPEG"|"TIFF"|"PNG"|"CR2"|"NEF"|"ARW"|"DNG"|"RAF"|"ORF"|"PEF"|"RW2") |
|
# Photo files - use DateTimeOriginal |
|
|
|
# Set the date to match the timestamp. |
|
sudo date -s "$system_date" |
|
|
|
# Handle files with wrong extensions by temporarily renaming them |
|
local original_file="$file" |
|
local new_temp_file=$(mktemp) |
|
cp "$file" "$new_temp_file" |
|
local file_ext="${file##*.}" |
|
local needs_rename=false |
|
|
|
# if [[ "$file_type" != "$file_ext" ]]; then |
|
echo " π Extension mismatch: $file_type file with .$file_ext extension" |
|
# Create temporary file with correct extension |
|
local correct_ext=$(echo "$file_type" | tr '[:upper:]' '[:lower:]') |
|
working_file="${file%.*}.temp_${correct_ext}" |
|
cp "$new_temp_file" "$working_file" || exit 1 |
|
local needs_rename=true |
|
local new_creation_date=$(stat -c %y "$working_file" | cut -d'.' -f1) |
|
# Convert stat format (2013-01-21 20:00:07) to EXIF format (2013:01:21 20:00:07) |
|
local new_creation_date_exif=$(echo "$new_creation_date" | sed 's/-/:/; s/-/:/') |
|
# Extract just the date portion (YYYY:MM:DD) for comparison, ignoring time |
|
local creation_date_only=$(echo "$new_creation_date_exif" | cut -d' ' -f1) |
|
local exif_date_only=$(echo "$exif_date" | cut -d' ' -f1) |
|
rm -f "$new_temp_file" |
|
if [[ "$creation_date_only" != "$exif_date_only" ]]; then |
|
echo " β Date mismatch (ignoring time): $creation_date_only != $exif_date_only" |
|
echo " β File: $working_file" |
|
echo " β Creation date (full): $new_creation_date_exif" |
|
echo " β EXIF date (full): $exif_date" |
|
echo " β Error out." |
|
exit 1 |
|
fi |
|
echo " π Created temporary file: $(basename "$working_file")" |
|
|
|
# First try with DateTimeOriginal |
|
if exiftool -q -overwrite_original -DateTimeOriginal="$exif_date" "$working_file" 2>/tmp/exif_error.txt; then |
|
echo " β
Updated EXIF DateTimeOriginal" |
|
success=true |
|
else |
|
# Try alternative date tags for problematic files |
|
echo " β οΈ DateTimeOriginal failed, trying CreateDate..." |
|
if exiftool -q -overwrite_original -CreateDate="$exif_date" -ModifyDate="$exif_date" "$working_file" 2>/tmp/exif_error2.txt; then |
|
echo " β
Updated EXIF CreateDate/ModifyDate" |
|
success=true |
|
else |
|
success=false |
|
fi |
|
fi |
|
|
|
# Handle cleanup and file replacement |
|
if [[ "$needs_rename" == true ]]; then |
|
if [[ "$success" == true ]]; then |
|
# Replace original with updated temp file |
|
mv "$working_file" "$original_file" |
|
echo " π Replaced original file with updated version" |
|
else |
|
# Remove temp file on failure |
|
rm -f "$working_file" |
|
echo " ποΈ Cleaned up temporary file" |
|
fi |
|
fi |
|
# Reset the date to the current date. |
|
sudo date -s "$CURRENT_DATE" |
|
if [[ "$success" == true ]]; then |
|
return 0 |
|
else |
|
echo " β Failed to update EXIF metadata" |
|
echo " π Error details:" |
|
if [[ -f /tmp/exif_error.txt ]]; then |
|
sed 's/^/ /' /tmp/exif_error.txt |
|
fi |
|
if [[ -f /tmp/exif_error2.txt ]]; then |
|
sed 's/^/ /' /tmp/exif_error2.txt |
|
fi |
|
# Run detailed diagnosis |
|
diagnose_file "$original_file" |
|
return 1 |
|
fi |
|
;; |
|
"MP4"|"MOV"|"AVI"|"MKV"|"WEBM"|"3GP"|"M4V") |
|
# Video files - use CreationDate and MediaCreateDate |
|
if exiftool -q -overwrite_original -CreationDate="$exif_date" -MediaCreateDate="$exif_date" "$file" 2>/dev/null; then |
|
echo " β
Updated video metadata" |
|
return 0 |
|
else |
|
echo " β Failed to update video metadata" |
|
return 1 |
|
fi |
|
;; |
|
*) |
|
echo " β οΈ Unsupported file type: $file_type" |
|
return 1 |
|
;; |
|
esac |
|
} |
|
|
|
find . -type d | sort | while read -r dir; do |
|
dir_basename="$(basename "$dir")" |
|
|
|
# Start processing from the specified folder onwards |
|
if [[ "$dir_basename" == "$FOLDER_NAME" || -z "$FOLDER_NAME" ]]; then |
|
starting_point_found=1 |
|
fi |
|
|
|
if [[ $starting_point_found == 0 ]]; then |
|
continue |
|
fi |
|
|
|
echo "" |
|
echo "π Processing directory: $dir" |
|
|
|
cd "$dir" || continue |
|
|
|
for json in *.supplemental-metadata.json; do |
|
if [[ ! -f "$json" ]]; then |
|
continue |
|
fi |
|
|
|
img="${json%.supplemental-metadata.json}" |
|
|
|
if [[ ! -f "$img" ]]; then |
|
echo " β οΈ Image file $img not found, skipping..." |
|
continue |
|
fi |
|
|
|
# Extract timestamp from JSON |
|
ts=$(jq -r '.photoTakenTime.timestamp' "$json" 2>/dev/null) |
|
|
|
if [[ ! "$ts" =~ ^[0-9]+$ ]]; then |
|
echo " β Invalid timestamp in $json, skipping..." |
|
((error_count++)) |
|
continue |
|
fi |
|
|
|
# Convert timestamp to different formats |
|
touch_time=$(date -u -d @"$ts" +%Y%m%d%H%M.%S) |
|
exif_date=$(date -u -d @"$ts" +"%Y:%m:%d %H:%M:%S") |
|
system_date=$(date -u -d @"$ts" +"%Y-%m-%d %H:%M:%S") |
|
readable_date=$(date -u -d @"$ts" +"%Y-%m-%d %H:%M:%S UTC") |
|
|
|
echo " π Processing: $img" |
|
echo " π
Original date: $readable_date" |
|
echo " π Setting timestamp: $touch_time" |
|
|
|
sudo date -s "$system_date" |
|
# Update filesystem timestamp |
|
if touch -t "$touch_time" "$img" 2>/dev/null; then |
|
echo " β
Updated filesystem timestamp" |
|
else |
|
echo " β Failed to update filesystem timestamp" |
|
((error_count++)) |
|
continue |
|
fi |
|
|
|
# Reset the date to the current date. |
|
sudo date -s "$CURRENT_DATE" |
|
|
|
# Update metadata based on file type |
|
if update_metadata "$img" "$touch_time" "$exif_date"; then |
|
((processed_count++)) |
|
else |
|
((error_count++)) |
|
fi |
|
|
|
done |
|
|
|
cd - > /dev/null |
|
done |
|
|
|
echo "" |
|
echo "π Processing complete!" |
|
echo "β
Successfully processed: $processed_count files" |
|
echo "β Errors encountered: $error_count files" |