#!/bin/bash # Parse arguments DRY_RUN=false INPUT_DIR="" OUTPUT_DIR="" while [[ $# -gt 0 ]]; do case $1 in --dry-run|-n) DRY_RUN=true shift ;; --input|-i) INPUT_DIR="$2" shift 2 ;; --output|-o) OUTPUT_DIR="$2" shift 2 ;; --help|-h) echo "Usage: $0 --input DIR --output DIR [OPTIONS]" echo "" echo "Required:" echo " --input, -i DIR Input directory containing images" echo " --output, -o DIR Output directory for organized images" echo "" echo "Options:" echo " --dry-run, -n Preview changes without moving files" echo " --help, -h Show this help message" exit 0 ;; *) echo "Unknown option: $1" echo "Use --help for usage information" exit 1 ;; esac done # Validate required arguments if [[ -z "$INPUT_DIR" ]]; then echo "Error: --input directory is required" echo "Use --help for usage information" exit 1 fi if [[ -z "$OUTPUT_DIR" ]]; then echo "Error: --output directory is required" echo "Use --help for usage information" exit 1 fi # Check if input directory exists if [[ ! -d "$INPUT_DIR" ]]; then echo "Error: Input directory '$INPUT_DIR' does not exist" exit 1 fi # Check if exiftool is installed if ! command -v exiftool &> /dev/null; then echo "Error: exiftool is not installed. Please install it first." echo " Ubuntu/Debian: sudo apt-get install libimage-exiftool-perl" echo " macOS: brew install exiftool" exit 1 fi # Create output directory (only if not dry-run) if [[ "$DRY_RUN" == false ]]; then mkdir -p "$OUTPUT_DIR" fi # Counters processed=0 unknown=0 failed=0 if [[ "$DRY_RUN" == true ]]; then echo "🔍 DRY RUN MODE - No files will be moved" fi echo "Starting image organization..." echo "Input directory: $INPUT_DIR" echo "Output directory: $OUTPUT_DIR" echo "" # Process each image # Using -iname for case-insensitive matching (matches .jpg, .JPG, .Jpg, etc.) while IFS= read -r image; do echo "Processing: $(basename "$image")" # Extract creation date (YYYYMMDD) # Try DateTimeOriginal first (most common for photos), then CreateDate, then ModifyDate creation_date=$(exiftool -DateTimeOriginal -d "%Y%m%d" -s3 "$image" 2>/dev/null | head -n 1 | tr -d ' ') if [[ -z "$creation_date" ]] || [[ ! "$creation_date" =~ ^[0-9]{8}$ ]]; then creation_date=$(exiftool -CreateDate -d "%Y%m%d" -s3 "$image" 2>/dev/null | head -n 1 | tr -d ' ') fi if [[ -z "$creation_date" ]] || [[ ! "$creation_date" =~ ^[0-9]{8}$ ]]; then creation_date=$(exiftool -ModifyDate -d "%Y%m%d" -s3 "$image" 2>/dev/null | head -n 1 | tr -d ' ') fi # Check if date was extracted successfully if [[ -z "$creation_date" ]] || [[ ! "$creation_date" =~ ^[0-9]{8}$ ]]; then echo " ⚠ Warning: Could not extract valid date, moving to Unknown folder" target_dir="$OUTPUT_DIR/Unknown" destination="$target_dir/$(basename "$image")" # Check if destination file already exists if [[ -f "$destination" ]]; then # Add timestamp to avoid overwrite filename=$(basename "$image") extension="${filename##*.}" basename_no_ext="${filename%.*}" destination="$target_dir/${basename_no_ext}_$(date +%s).$extension" echo " ⚠ File exists, renaming to: $(basename "$destination")" fi if [[ "$DRY_RUN" == true ]]; then echo " → Would move to: Unknown/$(basename "$destination")" else mkdir -p "$target_dir" if mv "$image" "$destination" 2>/dev/null; then echo " ✓ Moved to: Unknown/" else echo " ✗ Failed to move file" failed=$((failed + 1)) continue fi fi unknown=$((unknown + 1)) continue fi # Extract year, month, day year="${creation_date:0:4}" month="${creation_date:4:2}" day="${creation_date:6:2}" # Create subdirectories (YYYY/MM/DD) target_dir="$OUTPUT_DIR/$year/$month/$day" # Check if destination file already exists destination="$target_dir/$(basename "$image")" if [[ -f "$destination" ]]; then # Add timestamp to avoid overwrite filename=$(basename "$image") extension="${filename##*.}" basename_no_ext="${filename%.*}" destination="$target_dir/${basename_no_ext}_$(date +%s).$extension" echo " ⚠ File exists, renaming to: $(basename "$destination")" fi if [[ "$DRY_RUN" == true ]]; then # Dry run: just show what would happen echo " → Would move to: $year/$month/$day/$(basename "$destination")" processed=$((processed + 1)) else # Actually perform the move mkdir -p "$target_dir" if mv "$image" "$destination" 2>/dev/null; then echo " ✓ Moved to: $year/$month/$day/" processed=$((processed + 1)) else echo " ✗ Failed to move file" failed=$((failed + 1)) fi fi done < <(find "$INPUT_DIR" -type f \( -iname "*.jpg" -o -iname "*.jpeg" -o -iname "*.png" -o -iname "*.gif" -o -iname "*.bmp" -o -iname "*.tiff" -o -iname "*.tif" -o -iname "*.heic" -o -iname "*.heif" -o -iname "*.raw" -o -iname "*.cr2" -o -iname "*.nef" -o -iname "*.arw" -o -iname "*.dng" -o -iname "*.webp" \)) echo "" if [[ "$DRY_RUN" == true ]]; then echo "DRY RUN Summary:" echo " Would process: $processed" echo " Would move to Unknown: $unknown" else echo "Summary:" echo " Processed: $processed" echo " Moved to Unknown: $unknown" echo " Failed: $failed" fi