Skip to content

Instantly share code, notes, and snippets.

@jan-vandenberg
Created December 7, 2024 09:33
Show Gist options
  • Save jan-vandenberg/731b07e07414fca1497129a1f887e80f to your computer and use it in GitHub Desktop.
Save jan-vandenberg/731b07e07414fca1497129a1f887e80f to your computer and use it in GitHub Desktop.
organize_iphone_to_folders.py
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