-
-
Save jan-vandenberg/731b07e07414fca1497129a1f887e80f to your computer and use it in GitHub Desktop.
organize_iphone_to_folders.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import os | |
import shutil | |
from datetime import datetime | |
from PIL import Image | |
from PIL.ExifTags import TAGS | |
def get_image_date(file_path): | |
""" | |
Extracts the EXIF 'DateTimeOriginal' metadata from an image. | |
Falls back to file modification date if EXIF data is not available. | |
""" | |
try: | |
# Attempt to read EXIF data | |
image = Image.open(file_path) | |
exif_data = image._getexif() | |
if exif_data: | |
for tag, value in exif_data.items(): | |
tag_name = TAGS.get(tag) | |
if tag_name == "DateTimeOriginal": | |
return datetime.strptime(value, "%Y:%m:%d %H:%M:%S") | |
except Exception as e: | |
print(f"Error reading EXIF data for {file_path}: {e}") | |
# Fallback to file modification time | |
try: | |
mod_time = os.path.getmtime(file_path) | |
return datetime.fromtimestamp(mod_time) | |
except Exception as e: | |
print(f"Error reading file modification time for {file_path}: {e}") | |
return None | |
def organize_photos_by_date(source_folder, target_folder, dry_run=True): | |
""" | |
Organizes photos into a year/month folder structure based on their date. | |
Copies files instead of moving them. Logs all actions in dry-run mode. | |
""" | |
log_file_path = "organize_photos_dryrun.log" | |
# Open the log file for writing | |
with open(log_file_path, "w") as log_file: | |
if dry_run: | |
log_file.write("=== DRY RUN MODE: No files will be moved ===\n") | |
else: | |
log_file.write("=== LIVE MODE: Files will be moved ===\n") | |
for root, _, files in os.walk(source_folder): | |
for file in files: | |
if file.lower().endswith(('.jpg', '.jpeg', '.png', '.mp4', '.mov','.aae', 'heic', 'webp', 'gif')): | |
#if file.lower().endswith(('.aae', 'heic', 'webp', 'gif')): | |
file_path = os.path.join(root, file) | |
date = get_image_date(file_path) | |
if not date: | |
log_message = f"Skipping {file_path} (no date metadata found)\n" | |
print(log_message.strip()) | |
log_file.write(log_message) | |
continue | |
# Determine the destination directory structure: year/month | |
year = date.strftime("%Y") | |
month = date.strftime("%m") | |
dest_dir = os.path.join(target_folder, year, month) | |
dest_path = os.path.join(dest_dir, file) | |
# Log the action | |
log_message = f"File: {file_path}\n -> Would move to: {dest_path}\n" | |
print(log_message.strip()) | |
log_file.write(log_message) | |
# Only copy files if not in dry-run mode | |
if not dry_run: | |
if not os.path.exists(dest_dir): | |
os.makedirs(dest_dir) | |
try: | |
shutil.move(file_path, dest_path) | |
print(f"Moved {file_path} to {dest_path}") | |
except Exception as e: | |
error_message = f"Error copying {file_path}: {e}\n" | |
print(error_message.strip()) | |
log_file.write(error_message) | |
print(f"\nDry-run log written to: {log_file_path}") | |
# Configuration | |
source_folder = "/var/services/homes/jan/Backup/Photos" # Update with your NAS folder path | |
target_folder = "/var/services/homes/jan/Backup/Photos_organized" # Update to the desired output path | |
dry_run = False # Set to False to actually move files | |
organize_photos_by_date(source_folder, target_folder, dry_run) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment