diff --git a/photoimport.py b/photoimport.py index 0b259ad..dc6fe78 100644 --- a/photoimport.py +++ b/photoimport.py @@ -2,67 +2,53 @@ 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. """ - # Use exiftool to extract the CreateDate tag. - # We use -d to define the desired output format: YYYY-MM-DD HH:MM:SS try: - # Command: exiftool -d "Format" -CreateDate "file_path" - # The -d option allows specifying the desired output format. result = subprocess.run( ['exiftool', '-d', "%Y-%m-%d", '-CreateDate', file_path], capture_output=True, text=True, check=True ) - # The result contains "CreateDate : 2026-04-19". - # Extract only the value after the colon. output = result.stdout.strip() if ':' in output: date_string = output.split(':', 1)[1].strip() else: date_string = output if date_string: - # Replace '-' with '\' as requested for path compatibility. return date_string.replace('-', '\\') return "Date not found in metadata." except subprocess.CalledProcessError as e: - # This handles cases where exiftool fails (e.g., corrupted file) return f"ERROR: exiftool failed. Check file integrity or permissions. (Details: {e.stderr.strip()})" except FileNotFoundError: - # This handles cases where 'exiftool' command is not found 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, and prints file details, - showing the proposed destination path based on the 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. """ - # Define accepted extensions (in lowercase) - accepted_extensions = ( - '.jpg', '.jpeg', '.dng', '.raw', '.cr2', '.cr3', '.raf' - ) - + accepted_extensions = ('.jpg', '.jpeg', '.dng', '.raw', '.cr2', '.cr3', '.raf') + print(f"--- Starting photo scan in: {root_dir} ---") found_files = [] - # 1. Find all files try: for foldername, subfolders, filenames in os.walk(root_dir): for filename in filenames: full_path = os.path.join(foldername, filename) - - # Check the file extension _, ext = os.path.splitext(filename) - - # Convert extension to lowercase for case-insensitive matching 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 @@ -70,58 +56,87 @@ def scan_photos(root_dir, base_target_path): print(f"\nAN UNEXPECTED ERROR OCCURRED DURING DIRECTORY TRAVERSAL: {e}") return - if found_files: - print("\n========================================================") - print("--- File Name & Proposed Destination Directory ---") - print("========================================================\n") - - processed_count = 0 - for file_path in found_files: - # Extract the date using the external tool - date_time_str = get_exif_date(file_path) - - # 1. Construct the full destination directory path - # The base_target_path is expected to end with a separator. - # The date_time_str is in YYYY\MM\DD format (backslash-separated). - - # Explicitly join with backslashes for Windows-style paths. - if not base_target_path.endswith('\\'): - destination_dir = base_target_path + '\\' + date_time_str - else: - destination_dir = base_target_path + date_time_str - - print(f"File: {os.path.basename(file_path)}") - print(f" -> Extracted Date: {date_time_str}") - print(f" -> Proposed Destination Directory: {destination_dir}") - print("-" * 40) - processed_count += 1 - - print(f"\n========================================================") - print(f"--- Scan complete. Processed {processed_count} files. ---") - print(f"Files with the same date will point to the same destination directory.") - else: + 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 and their proposed destinations + print("\n========================================================") + print("--- File Name & Proposed Destination Directory ---") + print("========================================================\n") + + for file_path, date_time_str, dest_dir in file_info_list: + print(f"File: {os.path.basename(file_path)}") + print(f" -> Extracted Date: {date_time_str}") + print(f" -> Proposed Destination Directory: {dest_dir}") + print("-" * 40) + + print(f"\n========================================================") + print(f"--- Scan complete. Found {len(file_info_list)} files. ---") + print("Files with the same date will point to the same destination directory.") + + # 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: + shutil.copy2(file_path, dest_file) + 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__": - # Check if two arguments are provided (script name + drive + path) if len(sys.argv) != 3: print("Usage: python photoimport.py ") print("Example (for Windows): python photoimport.py Q:\\ I:\\Photos") sys.exit(1) - # Argument 1: The directory to scan (the drive) root_scan_dir = sys.argv[1] - # Argument 2: The path to display to the user (the base path for grouping) base_target_path = sys.argv[2] - - # Echo the specified path + print(f"Using base target path for grouping: {base_target_path}") - # Ensure the scanning path ends with a directory separator - # This is important for os.path.join to work correctly if not root_scan_dir.endswith(os.sep): root_scan_dir = root_scan_dir + os.sep - - # Perform the scan using the first argument (the drive) and the base path - scan_photos(root_scan_dir, base_target_path) \ No newline at end of file + + scan_photos(root_scan_dir, base_target_path)