paulo@52: import os paulo@52: import re paulo@52: import glob paulo@52: import traceback paulo@54: import datetime paulo@54: import random paulo@54: import urlparse paulo@52: paulo@52: import html paulo@52: paulo@53: import logging paulo@53: logging.basicConfig( paulo@71: level=logging.INFO, paulo@53: filename="_LOG", paulo@53: format="%(asctime)s %(levelname)-8s %(message)s", paulo@53: ) paulo@52: paulo@52: paulo@71: def _lahat(): paulo@71: ret = None paulo@71: paulo@71: try: paulo@71: with open("_lahat") as lahat_f: paulo@71: ret = lahat_f.read().strip() paulo@71: except IOError: paulo@71: pass paulo@71: paulo@71: return ret paulo@71: paulo@52: paulo@54: def _is_pics_dir(dirpath): paulo@54: return os.path.exists(os.path.join(dirpath, "_picsroot")) paulo@52: paulo@52: paulo@54: def _get_dir_dt(dirpath): paulo@54: if _is_pics_dir(dirpath): paulo@54: dirpath = os.path.join(dirpath, "_picsroot") paulo@54: paulo@54: return datetime.datetime.fromtimestamp(os.stat(dirpath).st_mtime) paulo@54: paulo@54: paulo@54: def _format_dt(dt): paulo@54: return dt.strftime("%Y-%m-%d") paulo@53: paulo@53: paulo@53: def _parse_path_info(path_info): paulo@53: ppi = path_info.split(os.sep) paulo@53: if len(ppi) > 1 and ppi[-1] == '': paulo@53: del ppi[-1] paulo@53: paulo@53: return ppi paulo@53: paulo@53: paulo@53: def _numeric_pad_basename(path, maxdigits=20): paulo@53: return os.path.basename(path).zfill(maxdigits) paulo@53: paulo@53: paulo@53: def _get_images(d): paulo@57: exts = [".jpg", ".webm"] paulo@57: paulo@53: thumb_fns = glob.glob(os.path.join(d, "thumbs", "*.jpg")) paulo@53: thumb_fns = sorted(thumb_fns, key=_numeric_pad_basename) paulo@53: logging.debug("thumb_fns = %s" % thumb_fns) paulo@53: paulo@57: browse_dir = os.path.join(d, "browse") paulo@57: browse_contents = set(os.listdir(browse_dir)) paulo@57: logging.debug("browse_contents = %s" % browse_contents) paulo@57: paulo@57: browse_fns = [] paulo@57: for i in thumb_fns: paulo@57: i_basename = os.path.splitext(os.path.basename(i))[0] paulo@57: try: paulo@57: for j in exts: paulo@57: browse_fn_basename = i_basename + j paulo@57: if browse_fn_basename in browse_contents: paulo@57: browse_fns.append(os.path.join(browse_dir, browse_fn_basename)) paulo@57: raise StopIteration paulo@57: except StopIteration: paulo@57: pass paulo@57: else: paulo@57: raise RuntimeError("Cannot find browse image for %s" % i) paulo@53: logging.debug("browse_fns = %s" % browse_fns) paulo@53: paulo@53: return zip(thumb_fns, browse_fns) paulo@53: paulo@52: paulo@54: class Main: paulo@54: def _get_pics_url(self, dirpath): paulo@54: script_name = self._environ.get("SCRIPT_NAME", '') paulo@54: return os.path.normpath(os.path.join(os.path.dirname(script_name), dirpath)) paulo@54: paulo@54: paulo@54: def _get_app_url(self, dirpath): paulo@54: script_name = self._environ.get("SCRIPT_NAME", '') paulo@54: return os.path.normpath(os.path.join(script_name, dirpath)) paulo@54: paulo@54: paulo@54: def _get_standard_html_doc(self, title): paulo@54: root = html.HTML("html") paulo@54: paulo@54: header = root.header paulo@54: header.link(rel="stylesheet", type="text/css", href=self._get_pics_url("index.css")) paulo@54: header.title(title) paulo@54: paulo@54: body = root.body paulo@54: body.h1(title) paulo@54: paulo@54: return (root, header, body) paulo@54: paulo@54: paulo@75: def _go_thumbnail_links_to_browse_imgs_html_body(self, body, t, b, a_args={}, img_args={}, lazyload=False): paulo@54: thumb_img_url = self._get_pics_url(t) paulo@54: browse_url = self._get_app_url(b) paulo@52: paulo@56: a = body.a(href=browse_url, **a_args) paulo@75: if lazyload: paulo@75: img_args = dict(img_args) paulo@75: img_args["data-src"] = thumb_img_url paulo@75: a.img(**img_args) paulo@75: else: paulo@75: a.img(src=thumb_img_url, **img_args) paulo@52: paulo@54: body.text(' ') paulo@54: paulo@57: paulo@57: def _go_browse_image_html_body(self, body, img): paulo@57: browse_img_url = self._get_pics_url(img) paulo@57: paulo@57: ext = os.path.splitext(img)[1] paulo@57: if ext == ".webm": paulo@57: body.video(src=browse_img_url, autoplay="true", loop="true") paulo@57: else: paulo@57: body.img(src=browse_img_url) paulo@57: paulo@54: paulo@54: def __init__(self, environ): paulo@54: self._environ = environ paulo@54: self._page_func = None paulo@71: self._show_index = False paulo@54: paulo@54: #logging.debug("environ = %s" % (sorted(self._environ.items(), key=lambda x: x[0]),)) paulo@54: logging.debug("environ['PATH_INFO'] = %s" % self._environ["PATH_INFO"]) paulo@54: logging.debug("environ['SCRIPT_NAME'] = %s" % self._environ["SCRIPT_NAME"]) paulo@54: logging.debug("environ['QUERY_STRING'] = %s" % self._environ["QUERY_STRING"]) paulo@54: paulo@54: pi = self._environ["PATH_INFO"] paulo@54: ppi = _parse_path_info(pi) paulo@54: logging.debug("ppi = %s" % ppi) paulo@54: paulo@54: if len(ppi) < 1 or ppi[0] != '': paulo@54: raise AssertionError("Parsed path length must start empty: " + pi) paulo@54: paulo@71: self._qs = urlparse.parse_qs(self._environ["QUERY_STRING"]) paulo@71: logging.debug("self._qs = %s" % self._qs) paulo@71: if self._qs.get("lahat", [''])[0] == _lahat(): paulo@71: self._show_index = True paulo@71: paulo@54: if len(ppi) >= 2 and _is_pics_dir(ppi[1]): paulo@54: if len(ppi) == 2: paulo@54: self._page_func = self.page_thumbs paulo@54: elif len(ppi) >= 4 and ppi[2] == "browse" and os.path.exists(os.path.join(*ppi)): paulo@54: self._page_func = self.page_browse paulo@71: elif len(ppi) == 1 and self._show_index: paulo@54: self._page_func = self.page_index paulo@54: paulo@54: if self._page_func is None: paulo@54: raise RuntimeError("Cannot find path: " + pi) paulo@54: paulo@53: paulo@54: def page(self): paulo@54: return unicode(self._page_func()).encode("utf-8") paulo@54: paulo@53: paulo@54: def page_index(self): paulo@75: n = 5 # number of thumbnails to display per dir paulo@53: paulo@54: (html_root, html_header, html_body) = self._get_standard_html_doc("Pictures") paulo@54: paulo@75: html_header.script('', type="text/javascript", src=self._get_pics_url("lazyload.js")) paulo@75: paulo@54: pics_dirs = [] paulo@54: for i in os.listdir('.'): paulo@54: if _is_pics_dir(i): paulo@54: pics_dirs.append((i, _get_dir_dt(i))) paulo@54: paulo@54: pics_dirs.sort(key=lambda x: x[1], reverse=True) paulo@75: paulo@54: for (d, dt) in pics_dirs: paulo@54: html_body.h2.a(d, href=self._get_app_url(d)) paulo@54: html_body.h3(_format_dt(dt)) paulo@54: paulo@54: imgs = _get_images(d) paulo@54: imgs_idx = [(i, img) for (i, img) in enumerate(imgs)] paulo@54: paulo@54: sampled_imgs_idx = random.sample(imgs_idx, min(len(imgs_idx), n)) paulo@54: sampled_imgs_idx.sort(key=lambda x: x[0]) paulo@54: paulo@54: html_p = html_body.p paulo@54: for (i, (t, b)) in sampled_imgs_idx: paulo@75: self._go_thumbnail_links_to_browse_imgs_html_body(html_p, t, b, lazyload=True) paulo@75: paulo@54: return html_root paulo@54: paulo@54: paulo@54: def page_thumbs(self): paulo@54: ppi = _parse_path_info(self._environ["PATH_INFO"]) paulo@54: d = os.path.join(*ppi) paulo@54: (html_root, html_header, html_body) = self._get_standard_html_doc(d) paulo@53: paulo@54: from_img = None paulo@71: if "from" in self._qs: paulo@71: from_img = self._qs["from"][0] paulo@54: paulo@54: html_p = html_body.p paulo@54: for (t, b) in _get_images(d): paulo@54: if from_img is not None and b == from_img: paulo@56: self._go_thumbnail_links_to_browse_imgs_html_body(html_p, t, b, img_args={"klass":"sel2", "id":"selected"}) paulo@54: else: paulo@54: self._go_thumbnail_links_to_browse_imgs_html_body(html_p, t, b) paulo@54: paulo@71: # TODO: fix me paulo@71: #if self._show_index: paulo@71: # html_body.a("(Other pictures)", href=self._get_app_url('')) paulo@71: paulo@54: return html_root paulo@54: paulo@54: paulo@54: def page_browse(self): paulo@54: ppi = _parse_path_info(self._environ["PATH_INFO"]) paulo@54: d = os.path.join(*ppi[:2]) paulo@54: imgs = _get_images(d) paulo@54: img = os.path.join(*ppi) paulo@54: paulo@54: # thumbnail preview ribbon paulo@54: w = 7 # must be odd paulo@54: v = w/2 paulo@54: imgs_circ = [None] * w paulo@54: x = None paulo@54: n = len(imgs) paulo@54: for (i, (t, b)) in enumerate(imgs): paulo@54: if b == img: paulo@54: x = i + 1 paulo@54: imgs_circ[v] = (t, b) paulo@54: for j in range(1, v + 1): paulo@54: if (i + j) < n: imgs_circ[v + j] = imgs[i + j] paulo@54: if (i - j) >= 0: imgs_circ[v - j] = imgs[i - j] paulo@54: paulo@54: break paulo@54: paulo@54: if x is None: paulo@54: raise AssertionError paulo@54: paulo@54: (html_root, html_header, html_body) = self._get_standard_html_doc(u"%s \u2014 %s of %s" % (d, x, len(imgs))) paulo@56: paulo@56: html_header.script('', type="text/javascript", src=self._get_pics_url("np_keys.js")) paulo@54: paulo@57: self._go_browse_image_html_body(html_body.p, img) paulo@54: paulo@54: logging.debug("imgs_circ = %s" % imgs_circ) paulo@54: paulo@54: html_p = html_body.p paulo@56: for (i, img_c) in enumerate(imgs_circ): paulo@56: if img_c is not None: paulo@56: (t, b) = img_c paulo@56: a_args = {} paulo@56: img_args = {} paulo@54: if b == img: paulo@56: a_args = {"id": "up"} paulo@56: img_args = {"klass": "sel"} paulo@56: b = "%s?from=%s#selected" % (d, img) paulo@56: elif i == v + 1: paulo@56: a_args = {"id": "next"} paulo@56: elif i == v - 1: paulo@56: a_args = {"id": "prev"} paulo@56: paulo@56: self._go_thumbnail_links_to_browse_imgs_html_body(html_p, t, b, a_args, img_args) paulo@54: paulo@54: return html_root paulo@53: paulo@52: paulo@52: def app(environ, start_response): paulo@52: response_code = "500 Internal Server Error" paulo@52: response_type = "text/plain; charset=UTF-8" paulo@52: paulo@52: try: paulo@54: response_body = Main(environ).page() paulo@52: response_code = "200 OK" paulo@52: response_type = "text/html; charset=UTF-8" paulo@52: except: paulo@52: response_body = traceback.format_exc() paulo@52: paulo@52: response_headers = [ paulo@52: ("Content-Type", response_type), paulo@52: ("Content-Length", str(len(response_body))), paulo@52: ] paulo@52: paulo@52: start_response(response_code, response_headers) paulo@52: paulo@52: return [response_body]