Reorganize photos from an existing folder structure into a new structure

I have a directory of photoys that have been (re-) organized before into a directory structure: Year/Month/Day.

Apart from the fact that it is not a convenient grouping to me, there are errors in it. For example; in my folder of 2007 there are pictures from 2010.

I know there are some programs (such as Shotwell) to organize photos, but they struggle with the large number of pictures I have, and also they all seem to maintain the existing file structure.

What I would like to do is to get all the pictures out of their folders and reorganize them correctly according the structure: Year/Month. That would make it easy for me to browse, and put them together in albums.

Would there be an idiot-proof way of doing this? Also, a link to a helpful forum discussion on this could help me a lot.


Script to reorganize photos into /year/month directories

The script below does the following:

  • It determines the month the picture was taken
  • Inside your targeted directory, it creates (if necessary) sub directories per year (if photos were found)
  • Inside these year directories, it creates sub directories per month
  • If the script is unable to find an appropriate date, it creates a folder named "undetermined"

Folders per year

enter image description here

Sub folders per month

enter image description here

Handling duplicates:

  • An important thing is to take care of duplicates in an appropriate way: digital cameras take shots in similarly named series (like IMG_1.jpeg, IMG_2.jpeg etc). Therefore duplicates need to be automatically renamed, or either the script will break or a large number of photo's will be lost.
    The script renames duplicates like:

    IMG_1.jpeg, duplicate_1_IMG_1.jpeg, duplicate_2_IMG_1.jpeg etc
    

How to use

  1. The script uses exif to read the meta-data:

    sudo apt-get install exif
    
  2. copy the script below into an empty file.

  3. In the head section of the script, define the directory where you want the photos to be copied to (targeted directory):

    #---
    save_to = "/targeted/directory"
    #---
    
  4. save the script as reorganize.py.

  5. Create the targeted (superior) directory if necessary
  6. Run the script, with the source directory (the folder with your photos) as an argument, by the command:

    python3 /path/to/reorganize.py <source_directory>
    

    I made the target directory set in the script, because you might want to add other dorectories (new pictures) to the same directory, organized automatically, while the source directory will be different in future situations.

Important notes

  • The dat format can differ slightly per camera. In the script, I took into account the following formats:

    Datum en tijd       |2013:08:18 15:58:46
    

    and

    Datum en tijd       |2013-08-18 20:21:32
    

    It might be different on other cameras. If the script does not work properly, you need to post the output of the command exif <image>, but most likely, it will work as it should.

  • The script as it is copies the photos into the new (sub) directorie(s), since you probably don't want to mess with the original directory until you verified all went well. I f you want to move the files however, replace the line:

    shutil.copyfile(file, newfile)
    

    by:

    shutil.move(file, newfile)
    

The script

#!/usr/bin/env python3
import subprocess
import os
import sys
import shutil

#---
save_to = "/targeted/directory"
#---

directory = sys.argv[1]
if not os.path.exists(save_to):
    os.mkdir(save_to)

get = lambda cmd: subprocess.check_output(["/bin/bash", "-c", cmd]).decode("utf-8")

def check_dir(dr):
    if not os.path.exists(dr):
        os.mkdir(dr)

def rename_dups(target_dir, name):
    n = 1; name_orig = name
    while os.path.exists(target_dir+"/"+name):
        name = "duplicate_"+str(n)+"_"+name_orig
        n = n+1
    return target_dir+"/"+name

for root, dirs, files in os.walk(directory):
    for name in files:
        file = root+"/"+name
        try:
            date = [l for l in get("exif "+'"'+file+'"').splitlines()\
                    if "Dat" in l][0].split("|")[1].split()[0]
            if "-" in date:
                date = date.split("-")[:2]
            elif ":" in date:
                date = date.split(":")[:2]
            targeted_dir = save_to+"/"+date[0]
            check_dir(targeted_dir)
            sub_dir = targeted_dir+"/"+date[1]
        except:
            sub_dir = save_to+"/"+"undetermined"
        check_dir(sub_dir)
        newfile = rename_dups(sub_dir, name)
        shutil.copyfile(file, newfile)

Doing it with a Python Utility

A tool has been made by @andrewning for this exact purpose, allowing you to take care of this, as well as any edge cases that you come by

1. Installation

Installation is as simple as running pip install sortphotos

2. Usage

Once you install it, it exists as a command called by sortphotos. In order to sort the photos according to what you want in the question, just run it as

sortphotos -r ./source ./destination --sort %Y/%m-%b

Here the format string given by --sort determines the structure in which the photos in the source directory get organised into the destination directory, here it sorts files as shown below:

The format string can be specified as used in the strftime specification with / (forward slashes) separating subdirectories

3. Additional Notes

Any collision in names (i.e two photos with the same name in the same month) will automatically have a number appended to them. If you want to just simulate the movement of files to see if everything is in order, just use the -t flag as follows:

sortphotos -tr ./source ./destination --sort %Y/%m-%b

Any files having no valid EXIF data are stored in a subfolder titled Unknown by default but can be changed by the option --unknown-dir