#!/usr/bin/python import os import subprocess import sys import pyexiv2 class DefaultDict(dict): def __getitem__(self, key): if key in self: return dict.__getitem__(self, key) else: return "" class App(object): def run(self): self.target_dir = sys.argv[2] self.root = os.path.abspath(sys.argv[1]) self.index_template = self.read_template("index") self.image_template = self.read_template("image") self.index_image_template = self.read_template("indeximage") self.index_dir_template = self.read_template("indexdir") for parent, dirname, subdirs, images in self.find_images(self.root): self.generate_html_for_dir(parent, dirname, subdirs, images) def read_template(self, basename): return file(basename + ".template").read() def find_images(self, root): for dirname, dirnames, filenames in os.walk(root, topdown=False): filenames = [x for x in filenames if self.is_image_file(dirname, x)] dirnames = [x for x in dirnames if self.has_image_files(dirname, x)] if dirnames or filenames: if dirname == root: parent = None else: parent = self.chop_root(root, os.path.dirname(dirname)) dirname = self.chop_root(root, dirname) yield parent, dirname, dirnames, filenames def chop_root(self, root, pathname): if pathname == root: return "." if not root.endswith(os.sep): root += os.sep if pathname == root: return "." assert pathname.startswith(root), "%s %s" % (pathname, root) return pathname[len(root):] def is_image_file(self, dirname, basename): return basename.lower().endswith(".jpg") def has_image_files(self, dirname, basename): fullname = os.path.join(dirname, basename) basenames = os.listdir(fullname) for x in basenames: if self.is_image_file(fullname, x): return True return False def generate_html_for_dir(self, parent, dirname, subdirs, images): if not os.path.exists(self.target(dirname)): os.makedirs(self.target(dirname)) self.generate_html_for_index(parent, dirname, subdirs, images) for i in range(len(images)): image = images[i] prev = images[i-1] next = images[(i+1) % len(images)] self.generate_html_for_image(dirname, image, prev, next) def target(self, pathname, *args): return os.path.join(self.target_dir, pathname, *args) def generate_html_for_index(self, parent, dirname, subdirs, images): imagelist = self.generate_snippet_for_index_image_list(dirname, images) subdirlist = self.generate_snippet_for_subdir_list(parent, dirname, subdirs) values = { "directorydescription": os.path.abspath(dirname), "indeximagelist": imagelist, "subdirlist": subdirlist, } self.apply_template(dirname, "index.html", self.index_template, values) def generate_snippet_for_index_image_list(self, dirname, images): snippet = [] for image in images: basename, suffix = os.path.splitext(image) target_dirname = self.target(dirname) image_path = os.path.join(self.root, dirname, image) thumb = os.path.basename(self.thumbnail_path(dirname, image)) values = { "imagebasename": basename, "thumbnailname": thumb, "imagepathname": self.relative_path(target_dirname, image_path) } snippet.append(self.index_image_template % values) return "".join(snippet) def generate_snippet_for_subdir_list(self, parent, dirname, subdirs): snippet = [] if parent: target_dirname = self.target(dirname) target_parent = self.target(parent) relative_parent = self.relative_path(target_dirname, target_parent) subdirs = [relative_parent] + subdirs for subdir in subdirs: values = { "subdirname": subdir, } snippet.append(self.index_dir_template % values) return "".join(snippet) def generate_html_for_image(self, dirname, image, prev, next): basename, suffix = os.path.splitext(image) target_dirname = self.target(dirname) image_path = os.path.join(self.root, dirname, image) prev_basename, suffix = os.path.splitext(os.path.basename(prev)) next_basename, suffix = os.path.splitext(os.path.basename(next)) values = DefaultDict({ "description": basename, "imagepathname": self.relative_path(target_dirname, image_path), "nextimage": next_basename + ".html", "previmage": prev_basename + ".html", }) img = pyexiv2.Image(image_path) img.readMetadata() for key in img.exifKeys() + img.iptcKeys(): values[key] = img[key] self.apply_template(dirname, basename + ".html", self.image_template, values) self.create_thumbnail(dirname, image) def common_ancestor(self, path1, path2): """Find the common ancestor directory of two paths. Both paths must be absolute. """ parts1 = path1.split(os.sep) parts2 = path2.split(os.sep) parts = [] while parts1 and parts2 and parts1[0] == parts2[0]: parts.append(parts1[0]) del parts1[0] del parts2[0] return os.sep.join(parts) + os.sep def relative_path(self, dirname, pathname): """Create a relative path from dirname to pathname. For example, if dirname is '/home/liw/public_html/gallery' and pathname is '/home/liw/public_html/images/lamola/img_1234.jpg', this function would return '../images/lamola/img_1234.jpg'. """ dirname = os.path.abspath(dirname) pathname = os.path.abspath(pathname) if not dirname.endswith(os.sep): dirname += os.sep common_ancestor = self.common_ancestor(dirname, pathname) dirname_relative = dirname[len(common_ancestor):] dirname_relative = [".." for x in dirname_relative.split(os.sep) if x] dirname_relative = os.sep.join(dirname_relative) pathname_relative = pathname[len(common_ancestor):] return os.path.join(dirname_relative, pathname_relative) def apply_template(self, dirname, filename, template, values): self.write(self.target(dirname, filename), template % values) def write(self, filename, contents): f = file(filename, "w") f.write(contents) f.close() def thumbnail_path(self, dirname, image): basename, suffix = os.path.splitext(image) return os.path.join(self.target_dir, dirname, basename + "_thumbnail.jpg") def create_thumbnail(self, dirname, image): image_path = os.path.join(self.root, dirname, image) subprocess.check_call(["convert", "-geometry", "200x200", image_path, self.thumbnail_path(dirname, image)]) if __name__ == "__main__": App().run()