#!/usr/bin/env python3 import os import sys import subprocess import shutil def get_exif_date(file_path): """ Uses exiftool to extract the File Creation Date/Time (CreateDate). Returns the formatted date string or an error message if extraction fails. """ try: result = subprocess.run( ['exiftool', '-d', "%Y-%m-%d", '-CreateDate', file_path], capture_output=True, text=True, check=True ) output = result.stdout.strip() if ':' in output: date_string = output.split(':', 1)[1].strip() else: date_string = output if date_string: return date_string.replace('-', '\\') return "Date not found in metadata." except subprocess.CalledProcessError as e: return f"ERROR: exiftool failed. Check file integrity or permissions. (Details: {e.stderr.strip()})" except FileNotFoundError: return "CRITICAL ERROR: exiftool command not found. Please ensure exiftool is installed and in your system PATH." def scan_photos(root_dir, base_target_path): """ Recursively scans the given directory (root_dir), extracts EXIF date, prints file details with proposed destination paths, then asks for confirmation before copying files. """ accepted_extensions = ('.jpg', '.jpeg', '.dng', '.raw', '.cr2', '.cr3', '.raf') print(f"--- Starting photo scan in: {root_dir} ---") found_files = [] try: for foldername, subfolders, filenames in os.walk(root_dir): for filename in filenames: full_path = os.path.join(foldername, filename) _, ext = os.path.splitext(filename) if ext.lower() in accepted_extensions: found_files.append(full_path) except FileNotFoundError: print(f"\nFATAL ERROR: The path '{root_dir}' was not found. Please check the drive letter.") return except Exception as e: print(f"\nAN UNEXPECTED ERROR OCCURRED DURING DIRECTORY TRAVERSAL: {e}") return if not found_files: print("\n--- Scan complete. No specified photo files found in this directory or its subdirectories. ---") return # Collect file info first (file_path, date_str, dest_dir) file_info_list = [] for file_path in found_files: date_time_str = get_exif_date(file_path) if not base_target_path.endswith('\\'): destination_dir = base_target_path + '\\' + date_time_str else: destination_dir = base_target_path + date_time_str file_info_list.append((file_path, date_time_str, destination_dir)) # Print all items in compact format print() for file_path, date_time_str, dest_dir in file_info_list: filename = os.path.basename(file_path) print(f"{filename} -> {dest_dir}") print(f"\nFound {len(file_info_list)} files.") # Ask for confirmation response = input("\nDo you want to copy these items? (y/yes): ").strip().lower() if response not in ('y', 'yes'): print("Aborted. No files were copied.") return # Copy files copied_count = 0 skipped_exists_count = 0 created_dirs_count = 0 for file_path, date_time_str, dest_dir in file_info_list: dest_file = os.path.join(dest_dir, os.path.basename(file_path)) # Create destination directory if it doesn't exist if not os.path.exists(dest_dir): os.makedirs(dest_dir) created_dirs_count += 1 # Check if file already exists in destination if os.path.exists(dest_file): print(f"File already exists: {dest_file} (skipped)") skipped_exists_count += 1 else: filename = os.path.basename(file_path) print("\n--- Starting copy of: " + filename + " ---") print("Source: " + file_path) print(f"Destination: {dest_file}") shutil.copy2(file_path, dest_file) print(f"Successfully copied: {filename}") copied_count += 1 # Summary print("\n========================================================") print("--- Copy Operation Complete ---") print("========================================================") print(f"Files copied: {copied_count}") if skipped_exists_count > 0: print(f"Files skipped: {skipped_exists_count} (already exists)") if created_dirs_count > 0: print(f"Directories created: {created_dirs_count}") if __name__ == "__main__": if len(sys.argv) != 3: print("Usage: python photoimport.py ") print("Example (for Windows): python photoimport.py Q:\\ I:\\Photos") sys.exit(1) root_scan_dir = sys.argv[1] base_target_path = sys.argv[2] print(f"Using base target path for grouping: {base_target_path}") if not root_scan_dir.endswith(os.sep): root_scan_dir = root_scan_dir + os.sep scan_photos(root_scan_dir, base_target_path)