I’ve been backing up my iPhone photos to my Windows 10 desktop using Microsoft’s OneDrive. The photos are on my local drive as well as the cloud. The downside is that all the photos, movies, and screen shots are dumped into a single directory, and there are a lot of them.
I prefer to have them sorted by date into directories. A directory for each year, with sub-directories for each month. I could do this by hand, but that’s a pain, and I have to remember to do it on a regular basis. Plus I’ll probably want to do the same for my wife’s iPhone photos. So I decided to write a python script to scan the camera roll directory, and copy the files to a directory in my photo archive section. The scrip will create directories if needed and skip files that are already in place.
That part was pretty straight forward. I used os.chdir() to get to source directory and os.listdir() to get the directory contents. Don’t want to create month directories for directories, just files, so I used os.path.isfile() to filter out non-files, and then check the file extension. I only want jpg, mov, png, and tiff files. I use Camera+ most of the time, which produces tiff files instead of jpg files. The png files are screenshots.
I used os.stat() to get the create time, and found files I exported to the camera roll from Camera plus had a create time of when they were exported, not the time the photo was taken. (Once I started having Camera+ dump straight to the camera roll, I didn’t have this problem).
So, I dug a little deeper and found I could get an image created time stamp with a getImageDate() call. Downside was this didn’t work for png, tiff, or mov files. So I had to do some extra sorting, and wrote another function to use on just the jpg files. I called the open() function from the Image library, and extracted the exif data using the _getexif() function. This works most of the time, so when it fails I had it return a ‘?’ rather than the time stamp string. Seeing that caused a fall back to the getImageDate() function.
This extra call to the Image library made the placement of the files more accurate. I had the base time function return the month as a three character string and the year as a 4 characters string. This required some basic string manipulation. Those two parameters were added to predetermined destination and passed to a function I had written for another project that checks if directory exits, and creates it if it does not.
Next was to have the program check to see if the file already existed in the correct destination directory. If it did, don’t bother copying it again. This will make the incremental runs faster and save on unneeded file transfers.
Now I have a functional script that I can set up to automatically run once a week. Once the files are archived off OneDrive, I can removed them there and on the iPhone is order to free up space.
Update: Source Code
The tricky part is getting the time stamps. Here are the three routines that handle that. Wordpress mucks with the spacing, but you should be able to figure it out.
def getImageDate(sPath,file): 'Get the image created time stamp' img = Image.open(sPath + "\\" + file) tTime = "?" imgData = img._getexif() if 306 in imgData and imgData < tTime: # 306 = DateTime tTime = imgData if 36867 in imgData and imgData < tTime: # 36867 = DateTimeOriginal tTime = imgData if 36868 in imgData and imgData < tTime: # 36868 = DateTimeDigitized tTime = imgData return tTime
def getTime2(file,sPath): 'If the file is not a jpg, get the last modified time, which is close enough' os.chdir(sPath)
(mode, ino, dev, nlink, uid, gid, size, atime, mtime, ctime) = os.stat(file)
year = time.ctime(mtime)[-4:]
month = time.ctime(mtime)[4:7]
return month, year
'Most files are jpg files, so this is the default path'
month = "Jan"
year = "1956" #If we see this year, we know there is a problem
months = ["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"]
ext = file[-3:].lower()
if ext == 'jpg':
cTime = getImageDate(sPath,file)
if cTime != '?':
year = cTime[0:4]
monthNum = cTime[5:7]
month = months[(int(monthNum)) - 1]
month,year = getTime2(file,sPath)
month,year = getTime2(file,sPath)