183 lines
5.9 KiB
Bash
183 lines
5.9 KiB
Bash
#!/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
|