#!/bin/python # My contribution to the world not needing *another* web gallery tool. # Needs ImageMagick. import sys, os, stat, getopt import cPickle as pickle recognised_extensions = [ "jpeg", "jpg", "gif", "png", "tif", "tiff" ] thumbs_dir = "g" thumb_width = 160 thumb_height = 160 thumbs_per_page = 100 template_file = None def capture(cmd): """Run a command, returning the tuple (output, exitcode).""" (pr, pw) = os.pipe() pid = os.fork() if pid == 0: os.close(pr) os.dup2(pw, 1) devnull = os.open("/dev/null", os.O_RDWR) os.dup2(devnull, 2) os.execvp(cmd[0], cmd) os._exit(20) os.close(pw) f = os.fdopen(pr) output = f.readlines() f.close() (deadpid, status) = os.waitpid(pid, 0) return (output, status >> 8) def nice_size(size): """Return a nicely-formatted file size.""" sizes = [ "B", "KiB", "MiB", "GiB", "TiB" ] s = "empty" for n in range(len(sizes)): r = 1024 ** n if size > r: s = str(size / r) + sizes[n] return s def get_metadata(cache, image, st): """Get the type and size of an image, returning the tuple (type, width, height). Returns None for type on failure.""" if cache.has_key(image): (time, md) = cache[image] if time == st.st_mtime: return md (output, status) = capture(("identify", image)) if status != 0 or len(output) != 1: return (None, 0, 0) ident = output[0].strip()[len(image) + 1:].split(" ", 2) size = ident[1] pos = size.find("+") if pos != -1: size = size[:pos] wh = size.split("x") if len(wh) != 2: return (None, 0, 0) md = (ident[0], int(wh[0]), int(wh[1])) cache[image] = (st.st_mtime, md) return md def make_thumbnail(image, thumbname, width, height): """Generate a thumbnail for an image. Return 1 on success, 0 on failure.""" factor = 1.0 if width > thumb_width or height > thumb_height: wfactor = thumb_width / (1.0 * width) hfactor = thumb_height / (1.0 * height) factor = min(wfactor, hfactor) width = int(factor * width) height = int(factor * height) size = str(width) + "x" + str(height) border = str((thumb_width - width) / 2) + "x" + str((thumb_height - height) / 2) cmd = ["convert", "-size", size, image, "-resize", size, "-border", border, "-bordercolor", "grey", thumbname] (output, status) = capture(cmd) return status == 0 def die(error): print >>sys.stderr, error sys.exit(20) def index_name(i): if i == 0: s = "" else: s = str(i) return "index" + s + ".html" def make_index(dir, images, dirs, others, pages, page): iname = thumbs_dir + "/" + index_name(0) body = [] try: f = open("../" + iname) f.close() body.append('

Parent directory

') except: pass if len(dirs) > 0: body.append('

Subdirectories

') body.append('') if len(others) > 0: body.append('

Other files

') body.append('') if len(images) > 0: body.append('

Images

') (first, last) = pages[page] if len(pages) != 1: bits = [] for i in range(len(pages)): (f, l) = pages[i] r = str(f + 1) + '-' + str(l) if i == page: bits.append(r) else: bits.append('' + r + '') body.append('

' + " ".join(bits) + '

') body.append('
') for (image, size, width, height) in images[first:last]: label = image i = label.rindex(".") if i > 0: label = label[:i] if len(label) > 15: label = label[:15] + "..." body.append('
') body.append('
') body.append('' + image + '') body.append('
') body.append('
') body.append('

' + label + '

') body.append('

' + nice_size(size) + ', ' + str(width) + 'x' + str(height) + '

') body.append('
') body.append('
') body.append('
') title = "Directory " + dir try: title = open(thumbs_dir + "/title").read().strip() except IOError: pass blurb = "" try: blurb = open(thumbs_dir + "/blurb").read().strip() except IOError: pass f = open(template_file) html = f.read() f.close() html = html.replace("__TITLE__", title) html = html.replace("__BLURB__", blurb) html = html.replace("__BODY__", "\n".join(body)) f = open(thumbs_dir + "/" + index_name(page), "w") f.write(html) f.close() def process_dir(dir): if dir[-1] == "/": dir = dir[:-1] print "Processing " + dir + "..." oldcwd = os.getcwd() os.chdir(dir) try: os.mkdir(thumbs_dir) except OSError: pass try: f = open(thumbs_dir + "/metadata_cache") metadata_cache = pickle.load(f) f.close() except IOError: metadata_cache = {} images = [] dirs = [] others = [] for file in os.listdir("."): if file == "g": continue st = os.stat(file) if stat.S_ISDIR(st.st_mode): dirs.append(file) continue elif not stat.S_ISREG(st.st_mode): continue ext = file.split(".")[-1] if not ext.lower() in recognised_extensions: others.append((file, st.st_size)) continue (type, width, height) = get_metadata(metadata_cache, file, st) if type is None: continue need_thumbnail = 1 thumbname = thumbs_dir + "/thumb-" + file + ".jpg" try: tst = os.stat(thumbname) if tst.st_mtime > st.st_mtime: need_thumbnail = 0 except OSError: pass if need_thumbnail: print "Thumbnailing " + file + "...", if make_thumbnail(file, thumbname, width, height): print "OK" else: print "failed" continue images.append((file, st.st_size, width, height)) images.sort() dirs.sort() others.sort() num_images = len(images) pages = [] for i in range((num_images / thumbs_per_page) + 1): f = i * thumbs_per_page l = f + thumbs_per_page if l > num_images: l = num_images pages.append((f, l)) for i in range(len(pages)): make_index(dir, images, dirs, others, pages, i) f = open(thumbs_dir + "/metadata_cache", "w") pickle.dump(metadata_cache, f) f.close() for dir in dirs: process_dir(dir) os.chdir(oldcwd) def main(argv): try: (optlist, argv) = getopt.getopt(argv[1:], "t:") except getopt.GetoptError, s: die(s) if len(argv) < 1: die("usage: gallstone [-t template] [...]") template_dir = os.getenv("HOME") + "/.gallstone" global template_file template_file = template_dir + "/default" for o, a in optlist: if o == "-t": template_file = template_dir + "/" + a for x in argv: process_dir(x) if __name__ == "__main__": main(sys.argv)