-
Notifications
You must be signed in to change notification settings - Fork 0
/
archive_images.py
executable file
·117 lines (104 loc) · 4.49 KB
/
archive_images.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
#!/usr/bin/env python
"""
Sorts image files by time - copies them into folders by year and month.
Written by Friedrich C. Kischkel.
From the usage info:
usage: archive_images.py [-h] [-m] [-f] [-d D] [--exec FILTER]
SOURCE [SOURCE ...] DESTINATION
Copy images into year/month sub-folders by time they were taken. Useful to get
some chronological orientations when copying a bulk of images from a camera's
memory card to a local pictures folder.
positional arguments:
SOURCE source path(s)
DESTINATION destination path
optional arguments:
-h, --help show this help message and exit
-m, --move move file instead of copying it (default: copy)
-f, --force force overwriting of existing files (default: do not
overwrite)
-d D, --depth D descend this deep into SOURCE directories
--exec FILTER for each SRC, execute FILTER SRC DST
"""
import os
import re
import shutil
import time
import argparse
import subprocess
IMAGE_FILE = re.compile(r"""\.(jpe?g)|(png)|(tiff?)$""", re.IGNORECASE)
EXIF_TIME_FORMAT = "%Y:%m:%d %H:%M:%S"
def time_taken(path):
"""Get time a picture was taken or at least file c/mtime."""
times = [
time.localtime(os.path.getctime(path)),
time.localtime(os.path.getmtime(path)),
time.localtime() # now
]
import exifread
with open(path, 'rb') as imagefile:
tags = exifread.process_file(imagefile, details=False)
for tag in ['Image DateTime', 'EXIF DateTimeOriginal', 'EXIF DateTimeDigitized']:
try:
times.append(time.strptime(str(tags[tag]), EXIF_TIME_FORMAT))
except KeyError, err:
print \
"WARNING: tag %(tag)s could not be retrieved from %(file)s" % \
{"tag": err, "file": path}
times.sort()
return times[0]
def archive_image(srcpath, filename, dstpath, overwrite=False, file_function=shutil.copy2):
"""Copy image "filename" in "path" into a subfolder "dstpath"."""
if re.search(IMAGE_FILE, filename):
srcpath = os.path.join(srcpath, filename)
ctime = time_taken(srcpath)
dst = os.path.join(
dstpath,
time.strftime("%Y", ctime),
time.strftime("%m", ctime))
try:
os.makedirs(dst)
except OSError:
pass
if not overwrite and os.path.exists(os.path.join(dst, filename)):
raise IOError('"%(path)s" already exists' % \
{'path': os.path.join(dst, filename)})
file_function(srcpath, dst)
def archive_all(srcpath, dstpath, overwrite=False, max_depth=None, file_function=shutil.copy2):
"""Copy files by creation time into sub-folders"""
iteration = 0
for current, _, files in os.walk(srcpath):
for filename in files:
try:
archive_image(current, filename, dstpath, overwrite, file_function)
except IOError, err:
print "ERROR: copying image: %(msg)s" % {'msg': str(err)}
iteration += 1
if max_depth != None and iteration > max_depth:
return
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="""\
Copy images into year/month sub-folders by time they were taken.
Useful to get some chronological orientations when copying a bulk of
images from a camera's memory card to a local pictures folder.""")
parser.add_argument('SOURCE', nargs='+', help='source path(s)')
parser.add_argument('DESTINATION', nargs=1, help='destination path')
parser.add_argument('-m', '--move', action='store_true', default=False, help='move file instead of copying it (default: copy)')
parser.add_argument('-f', '--force', action='store_true', default=False, help='force overwriting of existing files (default: do not overwrite)')
parser.add_argument('-d', '--depth', metavar='D', type=int, help='descend this deep into SOURCE directories')
parser.add_argument('--exec', metavar='FILTER', dest='execext', help='for each SRC, execute FILTER SRC DST')
ARGS = parser.parse_args()
fct = shutil.copy2
if ARGS.move:
fct = shutil.move
if ARGS.execext:
def make_call(cmd):
"""Create lambda for call to 'cmd src dst'."""
return lambda src, dst: subprocess.check_call(cmd.split() + [src, dst])
fct = make_call(ARGS.execext)
for source in ARGS.SOURCE:
archive_all(\
source,\
ARGS.DESTINATION[0],\
overwrite=ARGS.force,\
max_depth=ARGS.depth,\
file_function=fct)