2

Trying to rename family pictures, I want all the photos in this folder to be renamed as

mmdd__00X

So the 20th image on March 23rd should have a file name of

0323__020

A lot of my code comes from other threads on this, I used the code from Python File Creation Date & Rename - Request for Critique for most of it, but found I needed Date Taken (exif data) as opposed to Date Created. Problem is, the EXIF module in Pillow takes the Date Taken tag as a String, but I need it to be an int or a datetime for me to be able to modify. Or can I parse the string to fit my naming format?

import os
import datetime
from PIL import Image

target = input('Enter full directory path: ')
os.chdir(target)
allfiles = os.listdir(target)
counter = 0

def get_date_taken(path):
    return Image.open(path)._getexif()[36867]

for filename in allfiles:
        t = get_date_taken(filename)
        v = datetime.datetime.fromtimestamp(t)
        x = v.strftime('%m%d')
        y = '{:02d}'.format(counter)
        try:
            os.rename(filename, x+"__"+y+".jpg")
        except FileExistsError:
            while True:
                try:
                    counter += 1
                    y = '{:02d}'.format(counter)
                    os.rename(filename, x+"__"+y+".jpg")
                except FileExistsError:
                    continue
                counter = 0
                break

First real program, any help would be appreciated.

Community
  • 1
  • 1
Sean Pe
  • 23
  • 3

3 Answers3

0

Or can I parse the string to fit my naming format?

Yes. Use strptime() then strftime as you did to your desired format (ps you can move the underscores to the strftime call)

Charlie G
  • 495
  • 5
  • 14
0

Ok so if you're using Windows, then an OSError will be raised in your try block and your code will work with the following enhancements (aka idk enough about the .format thing). See below for the os.listdir advice.

for filename in allfiles:
    t = get_date_taken(filename)
    v = datetime.strptime(t, 'enter format of t here')
    x = v.strftime('%m%d__%H%M%S')+str(counter).zfill(3)+'.jpg'
    try:
        os.rename(filename, x)
    except OSError:
        # etc...

Also this code won't work on MacOS and Linux! You'll erase a lot of your files!

os.rename() overwrites the destination file if it has the same name as the source file

Suggestions for fixing below, but firstly os.listdir lists everything including subdirectories. If you want all files do

allfiles = [f for f in os.listdir(target) if os.path.isfile(os.path.join(target, f)]
#...or if you want only jpgs...
allfiles = [f for f in os.listdir(target) if os.path.isfile(os.path.join(target, f) and (f.lower().endswith('.jpg') or f.lower().endswith('.jpeg))]

Ok, the advice: I'd suggest renaming all files with the whole of the datetime attached (you can add the microseconds if you're taking multiple images per second). e.g.

for filename in allfiles:
    t = get_date_taken(filename)
    v = datetime.strptime(t, 'enter format of t here')
    x = v.strftime('%m%d__%H%M%S')+'.jpg'
    os.rename(filename, x)
    filename = x

So if everything went right allfiles is a list of all your renamed files. Sorting that should give you pictures by day either ascending or descending, check to which and make sure the list goes from earliest to latest.

Then iterate through with a counter that increments for each photo on a given day, resetting to 0 when a new day is reached, and replacing the time section of the filename with your counter. The time section will a fixed length and so will your extension so you could be able to chop off the end.

So that's about it. It's takes longer since it passes through your files twice and does a sort, but I wanted to play it safe. A benefit in doing this way is that it will organize your files by time.


An improvement to the code block above would be that if an exception is raised in the renaming process to convert the renamed files back to their original filenames so as to allow you to run your code again without writing new code. That would require keeping a list of new filenames like this:

new_names = []
for filename in allfiles:
    v = datetime.strptime(t, 'enter format of t here')
    x = v.strftime('%m%d__%H%M%S')+'.jpg'
    try:
        os.rename(filename, x)
    except:
        print 'something went wrong, reverting changes...'
        for new, old in zip(new_names, allfiles[:len(new_names)]):
            os.rename(new, old)
        raise
    else:
        filename = x
        new_names.append(x)
Charlie G
  • 495
  • 5
  • 14
0

First the datetime string is parsed into a time struct and then able to be format as desired:

from PIL import Image
DateTimeOriginal = 36867

for filename in os.listdir('.'):

    if isfile(filename) and filename.endswith('.jpg'):
        try:
            dto_str = Image.open(filename)._getexif()[DateTimeOriginal]
            dto = time.strptime(dto_str, '%Y:%m:%d %H:%M:%S')
        except (KeyError, TypeError):
            print('error: no exif datetime value found for %r.' % filename)
            continue

    new_filename = time.strftime('…', dto)
    # os.rename(filename, new_filename)
Gringo Suave
  • 25,443
  • 6
  • 77
  • 69