> ## Content Index
> Fetch the complete content index at: https://billmoriarty.com/llms.txt
> Use this file to discover other available public pages before exploring further.

# A Python script that sorts my photos while I'm doing something else
- URL: https://billmoriarty.com/a-python-script-that-sorts-my-photos-while-im-doing-something-else/
- Published: 2026-04-21T17:50:28.000Z
- Updated: 2026-04-24T19:44:10.000Z
- Author: Bill Moriarty

I have a folder named `to_digikam` on my Fedora computer where photos and videos land. They come from many places: Syncthing transfers from my Mac, camera offloads, or cloud exports. They arrive with generic names like `IMG_0842.heic`or `PXL_20260421.jpg`, sitting in one flat, chaotic pile until I find the time to deal with them.

I wanted a workflow that would handle one tedious part of my organization workflow automatically. Specifically, a script that would:

1. **Extract the YYYY-MM-DD** from the actual photo metadata.
2. **Rename the file** by prepending that date to the original filename.
3. **Move the file** into a `year/unsorted` directory within my main digiKam library.

I’m a big fan of Derek Sivers’ approach to photo file naming: embedding context directly into the filename. Something like `2025-09-12 Sky at the beach.jpg` is more useful to me than `IMG_5502.jpg`.

The filesystem's "modified date" is fragile - many normal copy and sync operations reset it. The only real record is the metadata that tracks when the photo was taken. We can use **ExifTool** (the `perl-Image-ExifTool` package on Fedora), to read that internal timestamp and place it in the filename. The code then moves the file to `Pictures/photos/[YEAR]/Unsorted/`. I still keep the "Unsorted" folder because then I can look at it in digiKam and decide if it should be in an album or have a tag or something.

![](https://cdn.synaps.media/billmoriarty/content/images/2026/04/python-photo-sort-1.png)

Python script to sort images

**The Mac-to-Fedora Handshake:**  
A cool part of this setup is the visual feedback. On my Mac, I export a photo into a synced folder. A few minutes later, the file vanishes. Because Syncthing is keeping the folders mirrored, the Fedora machine moving the file out of `to_digikam` triggers a "delete" on the Mac. That empty folder means Fedora handled it.

For the schedule, I went with a systemd user timer instead of a traditional cron job. On Fedora, this is the native way to handle background tasks. Unlike cron, systemd timers:

- are aware of system states (if the machine is asleep, the timer can catch up immediately upon wake).
- log directly to `journalctl` for easy troubleshooting.
- run entirely under my user account, keeping the automation isolated from the root system.

I described the logic to Gemini and it generated the Python backbone. After a few manual tweaks for my specific folder structures...success!

```Python
import os
import shutil
import subprocess
from pathlib import Path

# --- CONFIGURATION ---
# Edit these two lines for your machine.
SOURCE_DIR = Path("/home/YOUR_USERNAME/path/to/intake_folder")
DEST_BASE = Path("/home/YOUR_USERNAME/Pictures/photos")
# ---------------------

def get_date_taken(file_path):
    """Uses exiftool to get the DateTimeOriginal metadata."""
    try:
        cmd = ["exiftool", "-DateTimeOriginal", "-d", "%Y-%m-%d %Y", "-S", "-s", str(file_path)]
        result = subprocess.run(cmd, capture_output=True, text=True, check=True)
        if result.stdout:
            return result.stdout.strip().split(' ')
    except Exception as e:
        print(f"Error reading metadata for {file_path}: {e}")
    return None, None

def organize_photos():
    if not SOURCE_DIR.exists():
        print(f"Source directory {SOURCE_DIR} does not exist.")
        return
    for file_path in SOURCE_DIR.iterdir():
        if file_path.is_dir() or file_path.name.startswith('.'):
            continue
        date_str, year_str = get_date_taken(file_path)
        if date_str and year_str:
            new_name = f"{date_str} {file_path.name}"
            target_dir = DEST_BASE / year_str / "Unsorted"
            target_path = target_dir / new_name
            target_dir.mkdir(parents=True, exist_ok=True)
            print(f"Moving: {file_path.name} -> {target_path}")
            shutil.move(str(file_path), str(target_path))
        else:
            print(f"Skipping {file_path.name}: No EXIF date found.")

if __name__ == "__main__":
    organize_photos()
```

I expect to edit this for edge cases and more date related naming in case that one metadata field is not always accurate, but this is a great start.