RSS

(root)/misc-scripts : /image-process.py (revision 75)

Line Revision Contents
1 53
#!/usr/bin/python -tt
2
""" image-process.py (c) 2008 Matthew John Ernisse <mernisse@ub3rgeek.net>
3
4
Redistribution and use in source and binary forms,
5
with or without modification, are permitted provided
6
that the following conditions are met:
7
8
    * Redistributions of source code must retain the
9
      above copyright notice, this list of conditions
10
      and the following disclaimer.
11
    * Redistributions in binary form must reproduce
12
     the above copyright notice, this list of conditions
13
      and the following disclaimer in the documentation
14
      and/or other materials provided with the distribution.
15
16
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
19
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
20
COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
21
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
22
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
23
OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
25
TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
26
USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
28 59
$Id: image-process.py,v 1.3 2008/12/31 06:30:48 mernisse Exp $
29 53
"""
30
31 56
import datetime
32 59
import gio
33 53
import getopt
34
import os
35
import pyexiv2
36 59
import random
37 53
import re
38
import sys
39
40 56
from stat import *
41
42 59
# directories... I really wish there was a better way to query this crap.
43
# I should be able to get this from Gnome/XDG
44 74
PHOTOS = "%s/%s/mine" % (os.environ["HOME"], "Pictures")
45
OTHER = "%s/%s/other" % (os.environ["HOME"], "Pictures")
46 53
47
# these are evil tags that fuck my term up.
48
BLACKLIST = [
49
	"Exif.Nikon3.DataDump",
50
	]
51
52
def Usage():
53
	binary = os.path.basename(sys.argv[0])
54
	print """Usage: %s [-v] FILE ... 
55
   or: %s -d [-v] DIRECTORY ...
56
57
Process images listed on the command line.
58
Options:
59
	-d 	treat command line argumetnts as directories.
60 59
	-v	display EXIF data prior to renaming
61 53
""" %  (binary, binary)
62
	return
63
64
def fileCopy(src, dst):
65
	fd = [None, None]
66
	buf = None
67
	bs = 16384
68
	count = 0
69
70
	try:
71
		fd[0] = open(src, "rb")
72
	except IOError, e:
73
		print "couldn't open src %s (%s)" % (src, str(e))
74
		return 127
75
76
	try:
77
		fd[1] = open(dst, "wb")
78
	except IOError, e:
79
		print "couldn't open dst %s (%s)" % (dst, str(e))
80
		return 127
81
82
	(st_mode, st_ino, st_dev, st_nlink, st_uid, st_gid, st_size, st_atime, 
83
	 st_mtime, st_ctime) = os.stat(src)
84
85
	sys.stdout.write("0%")
86
87
	while True:
88
		buf = fd[0].read(bs)
89
		if buf:
90
			fd[1].write(buf)
91
			count += 1
92
93
			pct = (st_size - (count * bs)) / 100
94
			if pct > 25 and pct < 50:
95
				sys.stdout.write("..25%")
96
			if pct > 50 and pct < 75:
97
				sys.stdout.write("..50%")
98
			if pct > 75 and pct < 100:
99
				sys.stdout.write("..75%")
100
		else:
101
			break
102
103
	fd[0].close()
104
	fd[1].flush()
105
	fd[1].close()
106
107
	try:
108
		os.utime(dst, (st_atime, st_mtime))
109
		os.chmod(dst, st_mode)
110
	except Exception, e:
111
		print "couldn't update dst's attributes. %s" % (str(e))
112
		os.unlink(dst)
113
114
	sys.stdout.write("..100%\n")
115
	return 0
116
117
def printExif(file):
118
	global BLACKLIST
119
120
	image = pyexiv2.Image(file)
121
	image.readMetadata()
122
123
	for key in image.exifKeys():
124
		if key in BLACKLIST:
125
			continue
126
127
		print "%s: %s" % (key, image[key])
128
129
def dateTimeRename(file):
130 59
	global PHOTOS
131 53
	image = pyexiv2.Image(file)
132
	image.readMetadata()
133
134
	make = "UNKN"
135
	model = "0000"
136
137
	try:
138
		capture_date = image['Exif.Photo.DateTimeOriginal']
139
	except:
140
		print "couldn't get EXIF information for %s." % (file)
141 56
		mtime = os.stat(file)[ST_MTIME]
142
		capture_date = datetime.date.fromtimestamp(mtime)
143
		pass
144 53
145
	try:
146
		make = image['Exif.Image.Make']
147
		model = image['Exif.Image.Model']
148
	except:
149
		# this isn't fatal.
150
		pass
151
152
	fname = "%s_%s-%s.jpg" % (
153
		capture_date.strftime("%Y-%m-%d_%H-%M-%S"),
154
		make,
155
		model
156
	)
157
158
	archive = "%s/%s" % ( 
159
		capture_date.strftime("%Y"),
160
		capture_date.strftime("%m_%B")
161
	)
162
163 59
	if not os.path.exists("%s/%s" % (PHOTOS, archive)):
164 53
		try:
165 59
			os.makedirs("%s/%s" % (PHOTOS, archive))
166 53
		except Exception, e:
167
			print "couldn't make %s/%s, %s" % (
168 59
				PHOTOS,
169 53
				archive,
170
				str(e)
171
			)
172
			return 127
173
174 59
	print "%s" % ( fname )
175
	prefix = fname
176
	while True:
177
		if os.path.exists("%s/%s/%s" % ( PHOTOS, archive, fname )):
178
			# The dest file exists, let us err on the side of 
179
			# safety -- try to find ourselves a fname that 
180
			# exists.
181
			fname = prefix + "_" + random.randrange(0,9999)
182
		else:
183
			break
184
		
185
	fileCopy(file, "%s/%s/%s" % ( PHOTOS, archive, fname ))
186 53
	return 0
187
188
def Main():
189 59
	global PHOTOS, OTHER
190 53
191 59
	if not os.path.exists(PHOTOS):
192
		print "%s: does not exist." % (PHOTOS)
193 53
		sys.exit(127)
194
195 59
	if not os.path.exists(OTHER):
196
		print "%s: does not exist." % (OTHER)
197 56
		sys.exit(127)
198
199 53
	dirs = None
200 56
	links = None
201 53
	verbose = None
202 56
	files = []
203 53
204
	try:
205
		opts, args = getopt.getopt(sys.argv[1:], "dv")
206
	except Exception, e:
207
		print "str(e)"
208
		Usage()
209
		return 1
210
211
	for o, a in opts:
212
		if o == "-v":
213
			verbose = True
214
		elif o == "-d":
215
			dirs = True
216
		else:
217
			Usage()
218
			return 1
219
220 56
	# process the args as dirnames, walk them and push the files up into
221
	# the files list.  else if dirs is false, then just copy args [] to
222
	# files []
223 53
	if dirs:
224 56
		for dir in args:
225
			for d in os.walk(dir):
226
				for file in d[2]:
227
					files.append("%s/%s" % ( d[0], file ))
228
	else:
229
		files = args
230
	
231
	for file in files:
232 59
		if re.search(r'^\.', os.path.basename(file)):
233
			# We don't touch hidden files or folders.
234
			continue
235
236 53
		if not re.search(r'\.jpg$', file, re.I):
237 59
			print "%s: moving non-jpeg" % (file)
238
			code = fileCopy(file, "%s/%s" % (
239
				OTHER,
240
				os.path.basename(file)
241
				)
242
			)
243
244
			if code > 0:
245
				return code
246
			
247
			gF = gio.File(file)
248
			if not gF.trash():
249
				print "Could not throw out %s" % (file)
250
251 53
			continue
252
253
		if not os.path.exists(file):
254 59
			print "%s: File does not exist" % (file)
255 53
			continue
256
257
		if verbose:
258
			code = printExif(file)
259 59
			if code > 0:
260
				return code
261 53
262 59
		code = dateTimeRename(file)
263 53
		if code > 0:
264
			return code
265
266 59
		# use Gnome's GIO bindings to throw the file
267
		# out, using the Gnome Trash, this seems nicer
268
		# than just calling os.unlink
269
		gF = gio.File(file)
270
		if not gF.trash():
271
			print "Could not throw out %s" % (file)
272
273
274 53
if __name__ == "__main__":
275
	if not len(sys.argv) > 1:
276
		Usage()
277
		sys.exit(1)
278
279
	sys.exit(Main())

Loggerhead 1.17 is a web-based interface for Bazaar branches