emin

changeset 28
1088bb11189f
parent 26
79167c58c3a5
equal deleted inserted replaced
27:4ce678884842 28:1088bb11189f
1 #! /usr/bin/env python 1 #! /usr/bin/env python3
2 # -*- python -*- 2 # -*- python -*-
3 3
4 """%prog [opts] dir [outdir] 4 """%prog [opts] dir [outdir=./gallery]
5 5
6 emin - a static web gallery builder 6 emin - a static web gallery builder
7 7
8 emin makes static Web pages for presenting lots of imagey things: photos, PDFs, 8 emin makes static Web pages for presenting lots of imagey things: photos, PDFs,
9 graphs with thumbnails as well as links to the image/doc file proper. 9 graphs with thumbnails as well as links to the image/doc file proper.
37 37
38 Author: Andy Buckley, http://www.insectnation.org\ 38 Author: Andy Buckley, http://www.insectnation.org\
39 """ 39 """
40 40
41 __author__ = "Andy Buckley <andy@insectnation.org>" 41 __author__ = "Andy Buckley <andy@insectnation.org>"
42 __version__ = "0.3.4" 42 __version__ = "0.4.0"
43 43
44 44
45 import logging 45 import logging
46 from optparse import OptionParser, OptionGroup 46 from optparse import OptionParser, OptionGroup
47 parser = OptionParser(usage=__doc__, version=__version__) 47 parser = OptionParser(usage=__doc__, version=__version__)
58 help="put all thumbnails on one page (default: %default)") 58 help="put all thumbnails on one page (default: %default)")
59 parser.add_option("-c", "--num-cols", "--cols", dest="NUM_COLS", default=5, type=int, 59 parser.add_option("-c", "--num-cols", "--cols", dest="NUM_COLS", default=5, type=int,
60 help="max number of thumbnail columns on one page (default: %default)") 60 help="max number of thumbnail columns on one page (default: %default)")
61 parser.add_option("-r", "--num-rows", "--rows", dest="NUM_ROWS", default=6, type=int, 61 parser.add_option("-r", "--num-rows", "--rows", dest="NUM_ROWS", default=6, type=int,
62 help="max number of thumbnail rows on one page (default: %default). Set < 1 for unlimited (i.e. all on one page)") 62 help="max number of thumbnail rows on one page (default: %default). Set < 1 for unlimited (i.e. all on one page)")
63 parser.add_option("--thumb-height", dest="THUMB_HEIGHT", default=150, type=int, 63 parser.add_option("--thumb-height", dest="THUMB_HEIGHT", default=250, type=int,
64 help="thumbnail height, in pixels (default: %default)") 64 help="thumbnail height, in pixels (default: %default)")
65 parser.add_option("--max-imgsize", dest="MAX_IMGSIZE", default=800, type=int, 65 parser.add_option("--max-imgsize", dest="MAX_IMGSIZE", default=800, type=int,
66 help="max large image dimension in pixels (default: %default)") 66 help="max large image dimension in pixels (default: %default)")
67 parser.add_option("--exclude", dest="EXCLUDE", default=None, 67 parser.add_option("--exclude", dest="EXCLUDE", default=None,
68 help="a regex pattern specifying image files to be excludes (default: %default)") 68 help="a regex pattern specifying image files to be excludes (default: %default)")
84 84
85 ## More imports 85 ## More imports
86 import sys, os, glob, re, math, shutil, fnmatch 86 import sys, os, glob, re, math, shutil, fnmatch
87 try: 87 try:
88 from Cheetah.Template import Template 88 from Cheetah.Template import Template
89 except Exception, e: 89 except Exception as e:
90 logging.error("Couldn't import required Cheetah package") 90 logging.error("Couldn't import required Cheetah package")
91 exit(1) 91 exit(1)
92 try: 92 try:
93 import PIL.Image as PILI 93 import PIL.Image as PILI
94 except Exception, e: 94 except Exception as e:
95 logging.error("Couldn't import required Python Imaging Library package") 95 logging.error("Couldn't import required Python Imaging Library / Pillow package")
96 exit(1) 96 exit(1)
97 97
98 98
99 ## Set processing/output dir 99 ## Set processing/output dir
100 if len(args) < 1 or len(args) > 2: 100 if len(args) < 1 or len(args) > 2:
101 parser.print_usage() 101 parser.print_usage()
102 exit(1) 102 exit(1)
103 if len(args) >= 1: 103 if len(args) >= 1:
104 opts.SRCDIR = os.path.normpath(args[0]) 104 opts.SRCDIR = os.path.normpath(args[0])
105 opts.OUTDIR = os.path.normpath(args[0]) 105 opts.OUTDIR = os.path.normpath("./gallery") #args[0])
106 if len(args) == 2: 106 if len(args) == 2:
107 opts.OUTDIR = os.path.normpath(args[1]) 107 opts.OUTDIR = os.path.normpath(args[1])
108 108
109 109
110 ## Deal with consequences of interacting optional settings 110 ## Deal with consequences of interacting optional settings
148 148
149 149
150 ## Go to the gallery directory and test if it's writeable 150 ## Go to the gallery directory and test if it's writeable
151 if not os.access(opts.OUTDIR, os.W_OK): 151 if not os.access(opts.OUTDIR, os.W_OK):
152 try: 152 try:
153 logging.info("Making output dir in %s" % opts.OUTDIR) 153 logging.debug("Making output dir in %s" % opts.OUTDIR)
154 os.makedirs(opts.OUTDIR) 154 os.makedirs(opts.OUTDIR)
155 except Exception, e: 155 except Exception as e:
156 logging.error("Problem when making output dir %s... exiting" % opts.OUTDIR) 156 logging.error("Problem when making output dir %s... exiting" % opts.OUTDIR)
157 exit(1) 157 exit(1)
158 158
159 159
160 ## Make thumbnail directory if needed 160 ## Make thumbnail directory if needed
161 opts.THUMBDIR = "thumbs" 161 opts.THUMBDIR = "thumbs"
162 THUMBDIR = os.path.join(opts.OUTDIR, opts.THUMBDIR) 162 THUMBDIR = os.path.join(opts.OUTDIR, opts.THUMBDIR)
163 try: 163 try:
164 if not os.path.isdir(THUMBDIR): 164 if not os.path.isdir(THUMBDIR):
165 logging.info("Making thumbs dir in %s" % THUMBDIR) 165 logging.debug("Making thumbs dir in %s" % THUMBDIR)
166 os.makedirs(THUMBDIR) 166 os.makedirs(THUMBDIR)
167 except Exception, e: 167 except Exception as e:
168 logging.error("Problem when making thumbnails dir... exiting") 168 logging.error("Problem when making thumbnails dir... exiting")
169 exit(1) 169 exit(1)
170 170
171 171
172 ## Build the list of pictures to display 172 ## Build the list of pictures to display
210 logging.debug("Copying %s -> %s" % (imgpath, targetpath)) 210 logging.debug("Copying %s -> %s" % (imgpath, targetpath))
211 import shutil 211 import shutil
212 shutil.copy(imgpath, targetpath) 212 shutil.copy(imgpath, targetpath)
213 213
214 214
215 ## Store some image info 215 ## Process images
216 logging.info("Processing {:d} images...".format(len(outimgs)))
216 imgsinfo = {} 217 imgsinfo = {}
217 for picpath in outimgs: 218 for picpath in outimgs:
218 picname = os.path.basename(picpath) 219 picname = os.path.basename(picpath)
219 picbase = os.path.splitext(picname)[0] 220 picbase = os.path.splitext(picname)[0]
220 221
221 ## Convert EPS, PDF, TIFF to Web-viewable formats 222 ## Convert EPS, PDF, TIFF to Web-viewable formats
222 picversions = {} 223 picversions = {}
223 picpathparts = os.path.splitext(picpath) 224 picpathparts = os.path.splitext(picpath)
224 picnameparts = os.path.splitext(picname) 225 picnameparts = os.path.splitext(picname)
225 extn = picnameparts[1].lower() 226 extn = picnameparts[1].lower()
227 pictype = extn[1:].upper(); pictype = "TIFF" if pictype == "TIF" else pictype
228 picversions[pictype] = picname
226 convcmd = None 229 convcmd = None
227 if extn in [".tif", ".tiff"]: 230 if pictype in ["TIFF", "EPS"]:
228 picversions["TIFF"] = picname 231 newpictype = "PNG" if pictype == "EPS" else "JPG"
229 newpicname = picname + ".jpg" 232 newextn = "." + newpictype.lower()
230 newpicpath = picpath + ".jpg" 233 newpicname = picname + newextn
231 picversions["JPG"] = newpicname 234 newpicpath = picpath + newextn
232 convcmd = ["convert", picpath, newpicpath] 235 picversions[newpictype] = newpicname
236 logging.debug("Converting {} to {}".format(picpath, newpicpath))
237 img = PILI.open(picpath, "r")
238 img.save(newpicpath)
233 picname = newpicname 239 picname = newpicname
234 picpath = newpicpath 240 picpath = newpicpath
235 elif extn in [".eps", ".pdf"]: 241 elif extn in [".pdf"]:
236 picversions[extn[1:].upper()] = picname
237 newpicname = picname + ".png" 242 newpicname = picname + ".png"
238 newpicpath = picpath + ".png" 243 newpicpath = picpath + ".png"
239 picversions["PNG"] = newpicname 244 picversions["PNG"] = newpicname
240 convcmd = ["convert", "-density", "200", "-resize", "800x700", picpath, newpicpath] 245 logging.debug("Converting {} to {}".format(picpath, newpicpath))
246 try:
247 import pdf2image, tempfile
248 with tempfile.TemporaryDirectory() as tmppath:
249 pdfimgs = pdf2image.convert_from_path(picpath, output_folder=tmppath, first_page=0, last_page=1) #, fmt="png")
250 #pdfimgs = pdf2image.convert_from_path(picpath, output_folder=tmppath)
251 pdfimg = pdfimgs[0].save(newpicpath)
252 if len(pdfimgs) > 1:
253 logging.warning("Multi-page PDF {}: showing only page 1".format(picpath))
254 except ImportError:
255 import subprocess
256 try:
257 convcmd = ["convert", "-density", "200", "-resize", "800x700", picpath, newpicpath]
258 subprocess.check_call(convcmd)
259 except:
260 logging.error("PDF conversion failed for {}... skipping".format(picpath))
261 continue
241 picname = newpicname 262 picname = newpicname
242 picpath = newpicpath 263 picpath = newpicpath
243 else:
244 picversions[extn[1:].upper()] = picname
245
246 ## Do the conversion
247 if convcmd:
248 try:
249 # TODO: threading / multiprocessing for speed-up?
250 import subprocess
251 subprocess.check_call(convcmd)
252 except:
253 raise
254 264
255 ## Main pic info 265 ## Main pic info
256 info = ImageInfo() 266 info = ImageInfo()
257 info.name = picname 267 info.name = picname
258 info.path = picpath 268 info.path = picpath
267 277
268 ## Make thumbnail 278 ## Make thumbnail
269 ## TODO: Be lazy! 279 ## TODO: Be lazy!
270 #if opts.FORCE or not os.access(thumbpath, os.R_OK) or os.stat(thumbpath).st_mtime > os.stat(pic).st_mtime: 280 #if opts.FORCE or not os.access(thumbpath, os.R_OK) or os.stat(thumbpath).st_mtime > os.stat(pic).st_mtime:
271 try: 281 try:
272 logging.debug("Making new thumbnail %s for %s (max height %d)" % \ 282 logging.debug("Making new thumbnail %s for %s (max height %d)" % (thumbpath, picname, opts.THUMB_HEIGHT))
273 (thumbpath, picname, opts.THUMB_HEIGHT))
274 thumbimg = PILI.open(picpath, "r") 283 thumbimg = PILI.open(picpath, "r")
275 thumbimg.thumbnail((100000000, opts.THUMB_HEIGHT), resample=PILI.ANTIALIAS) 284 thumbimg.thumbnail((100000000, opts.THUMB_HEIGHT), resample=PILI.ANTIALIAS)
276 thumbimg.save(thumbpath) 285 thumbimg.save(thumbpath)
277 info.thumbsize = thumbimg.size 286 info.thumbsize = thumbimg.size
278 except Exception, e: 287 except Exception as e:
279 logging.warning("Problem when making thumbnail from %s... exiting" % picpath) 288 logging.warning("Problem when making thumbnail from %s... exiting" % picpath)
280 exit(1) 289 exit(1)
281 290
282 ## Store info 291 ## Store info
283 imgsinfo[picname] = info 292 imgsinfo[picname] = info
417 #for n, thumb in enumerate($PAGEPICS) 426 #for n, thumb in enumerate($PAGEPICS)
418 #if $n % $NUM_COLS == 0 and $n not in (0, len($PAGEPICS)-1) 427 #if $n % $NUM_COLS == 0 and $n not in (0, len($PAGEPICS)-1)
419 <tr/><tr> 428 <tr/><tr>
420 #end if 429 #end if
421 #set info = $PICINFO[$thumb] 430 #set info = $PICINFO[$thumb]
422 <td> 431 <td style="text-align:right;">
423 <a href="$info.relpath" $jsrel><img alt="$thumb" src="$info.relthumbpath" style="border:0;" width="$info.thumbx" height="$info.thumby" /></a><br/> 432 <a href="$info.relpath" $jsrel><img alt="$thumb" src="$info.relthumbpath" style="border:0;" width="$info.thumbx" height="$info.thumby" /></a><br/>
424 #for fmt, name in $info.versions.iteritems() 433 #for fmt, name in $info.versions.items()
425 <a class="format" href="$name">$fmt.lower()</a> 434 <a class="format" href="$name">$fmt.lower()</a>
426 #end for 435 #end for
427 </td> 436 </td>
428 #end for 437 #end for
429 </tr> 438 </tr>
482 # print THUMBNAMES 491 # print THUMBNAMES
483 # THUMBPATHS = [os.path.join(relthumbdir, name) for name in THUMBNAMES] 492 # THUMBPATHS = [os.path.join(relthumbdir, name) for name in THUMBNAMES]
484 # print THUMBPATHS 493 # print THUMBPATHS
485 # THUMBDIMS = [t.size for t in thumbsinfo.values()[pics_start : pics_end]] 494 # THUMBDIMS = [t.size for t in thumbsinfo.values()[pics_start : pics_end]]
486 495
487 logging.info("Writing to index file %s" % PAGEPATH) 496 logging.debug("Writing to index file %s" % PAGEPATH)
488 f = open(PAGEPATH, "w") 497 f = open(PAGEPATH, "w")
489 logging.debug("Images on page: %s" % PAGEPICS) 498 logging.debug("Images on page: %s" % PAGEPICS)
490 tdict = {} 499 tdict = {}
491 tdict["NUM_PAGES"] = NUM_PAGES 500 tdict["NUM_PAGES"] = NUM_PAGES
492 tdict["NUM_PER_PAGE"] = NUM_PER_PAGE 501 tdict["NUM_PER_PAGE"] = NUM_PER_PAGE

mercurial