Mercurial > hg > index.fcgi > www > www-1
diff pics3/pics_flask_app.py @ 124:9b57b90aea31
initial add for pics3
author | paulo |
---|---|
date | Thu, 25 Mar 2021 00:33:42 -0700 |
parents | |
children | d216dd8e63da |
line diff
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/pics3/pics_flask_app.py Thu Mar 25 00:33:42 2021 -0700 1.3 @@ -0,0 +1,183 @@ 1.4 +import csv 1.5 +import datetime 1.6 +import random 1.7 +import os 1.8 + 1.9 +import flask 1.10 +import google.cloud.storage 1.11 +from html3.html3 import HTML 1.12 + 1.13 +app = flask.Flask(__name__) 1.14 + 1.15 +GCS_CLIENT = google.cloud.storage.Client() 1.16 +GCS_BUCKET = GCS_CLIENT.get_bucket(os.environ.get("GCS_BUCKET")) 1.17 + 1.18 + 1.19 +class PicsDialect(csv.Dialect): 1.20 + delimiter = '\t' 1.21 + quoting = csv.QUOTE_NONE 1.22 + lineterminator = '\n' 1.23 + 1.24 +PICSDIALECT = PicsDialect() 1.25 + 1.26 + 1.27 +def _parse_dt(dts): 1.28 + return datetime.datetime.strptime(dts, "%Y%m%d") 1.29 + 1.30 + 1.31 +def _format_dt(dt): 1.32 + return dt.strftime("%Y-%m-%d") 1.33 + 1.34 + 1.35 +def _numeric_pad_basename(path, maxdigits=20): 1.36 + return os.path.basename(path).zfill(maxdigits) 1.37 + 1.38 + 1.39 +def _get_images(d): 1.40 + exts = (".jpg", ".webm") 1.41 + thumb_dir = f"pics/{d}/thumbs" 1.42 + browse_dir = f"pics/{d}/browse" 1.43 + 1.44 + thumb_fns = [i.name for i in GCS_CLIENT.list_blobs(GCS_BUCKET, prefix=thumb_dir) if i.name.endswith(exts)] 1.45 + thumb_fns = sorted(thumb_fns, key=_numeric_pad_basename) 1.46 + 1.47 + browse_contents = set(i.name for i in GCS_CLIENT.list_blobs(GCS_BUCKET, prefix=browse_dir) if i.name.endswith(exts)) 1.48 + browse_fns = [] 1.49 + for i in thumb_fns: 1.50 + i_basename = os.path.splitext(os.path.basename(i))[0] 1.51 + try: 1.52 + for j in exts: 1.53 + browse_fn = browse_dir + "/" + i_basename + j 1.54 + if browse_fn in browse_contents: 1.55 + browse_fns.append(browse_fn) 1.56 + raise StopIteration 1.57 + except StopIteration: 1.58 + pass 1.59 + else: 1.60 + raise RuntimeError(f"Cannot find browse image for {i}") 1.61 + 1.62 + return zip(thumb_fns, browse_fns) 1.63 + 1.64 + 1.65 +def _get_standard_html_doc(title): 1.66 + root = HTML("html") 1.67 + 1.68 + header = root.head 1.69 + header.link(rel="stylesheet", type="text/css", href=flask.url_for("static", filename="index.css")) 1.70 + header.title(title) 1.71 + 1.72 + body = root.body 1.73 + body.h1(title) 1.74 + 1.75 + return (root, header, body) 1.76 + 1.77 + 1.78 +def _go_thumbnail_links_to_browse_imgs_html_body(body, d, t, b, a_args={}, img_args={}, lazyload=False): 1.79 + thumb_img_url = GCS_BUCKET.get_blob(t).public_url 1.80 + browse_url = flask.url_for("browse", d=d, img=os.path.basename(b)) 1.81 + 1.82 + a = body.a(href=browse_url, **a_args) 1.83 + if lazyload: 1.84 + img_args = dict(img_args) 1.85 + img_args["data-src"] = thumb_img_url 1.86 + a.img(**img_args) 1.87 + else: 1.88 + a.img(src=thumb_img_url, **img_args) 1.89 + 1.90 + body.text(" ") 1.91 + 1.92 + 1.93 +@app.route("/") 1.94 +def index(): 1.95 + n = 5 # number of thumbnails to display per dir 1.96 + 1.97 + (root, header, body) = _get_standard_html_doc("Pictures") 1.98 + header.script('', type="text/javascript", src=flask.url_for("static", filename="lazyload.js")) 1.99 + 1.100 + #body.pre(str(list(GCS_CLIENT.list_blobs(GCS_BUCKET)))) 1.101 + 1.102 + pics_dirs = [] 1.103 + pics_dirs_index_blob = GCS_BUCKET.get_blob("pics/index.tsv") 1.104 + if pics_dirs_index_blob: 1.105 + pics_dirs_index_strlist = pics_dirs_index_blob.download_as_text().splitlines() 1.106 + pics_dirs_index_reader = csv.reader(pics_dirs_index_strlist, PICSDIALECT) 1.107 + pics_dirs = sorted(pics_dirs_index_reader, key=lambda x: x[0]) 1.108 + 1.109 + for (dts, d) in pics_dirs: 1.110 + dt = _parse_dt(dts) 1.111 + body.h2(d) 1.112 + body.h3(_format_dt(dt)) 1.113 + 1.114 + #body.pre(str(list(_get_images(d)))) 1.115 + 1.116 + imgs = _get_images(d) 1.117 + imgs_idx = [(i, img) for (i, img) in enumerate(imgs)] 1.118 + 1.119 + sampled_imgs_idx = random.sample(imgs_idx, min(len(imgs_idx), n)) 1.120 + sampled_imgs_idx.sort(key=lambda x: x[0]) 1.121 + 1.122 + p = body.p 1.123 + for (i, (t, b)) in sampled_imgs_idx: 1.124 + _go_thumbnail_links_to_browse_imgs_html_body(p, d, t, b, lazyload=True) 1.125 + 1.126 + return str(root).encode("utf-8") 1.127 + 1.128 + 1.129 +@app.route("/<d>/browse/<img>") 1.130 +def browse(d, img): 1.131 + browse_img_blob = GCS_BUCKET.get_blob(f"pics/{d}/browse/{img}") 1.132 + if not browse_img_blob: 1.133 + flask.abort(404) 1.134 + 1.135 + imgs = list(_get_images(d)) 1.136 + 1.137 + # thumbnail preview ribbon 1.138 + w = 7 # must be odd 1.139 + v = int(w/2) 1.140 + imgs_circ = [None] * w 1.141 + x = None 1.142 + n = len(imgs) 1.143 + for (i, (t, b)) in enumerate(imgs): 1.144 + if os.path.basename(b) == img: 1.145 + x = i + 1 1.146 + imgs_circ[v] = (t, b) 1.147 + for j in range(1, v + 1): 1.148 + if (i + j) < n: imgs_circ[v + j] = imgs[i + j] 1.149 + if (i - j) >= 0: imgs_circ[v - j] = imgs[i - j] 1.150 + break 1.151 + 1.152 + if x is None: 1.153 + raise AssertionError 1.154 + 1.155 + (root, header, body) = _get_standard_html_doc(f"{d} \u2014 {x} of {n}") 1.156 + header.script('', type="text/javascript", src=flask.url_for("static", filename="np_keys.js")) 1.157 + 1.158 + browse_img_url = browse_img_blob.public_url 1.159 + ext = os.path.splitext(img)[1] 1.160 + p = body.p 1.161 + if ext == ".webm": 1.162 + p.video(src=browse_img_url, autoplay="true", loop="true") 1.163 + else: 1.164 + p.img(src=browse_img_url) 1.165 + 1.166 + p = body.p 1.167 + for (i, img_c) in enumerate(imgs_circ): 1.168 + if img_c is not None: 1.169 + (t, b) = img_c 1.170 + a_args = {} 1.171 + img_args = {} 1.172 + # FIXME 1.173 + #if b == img: 1.174 + # a_args = {"id": "up"} 1.175 + # img_args = {"klass": "sel"} 1.176 + # b = f"{d}?from={img}#selected" 1.177 + if False: 1.178 + pass 1.179 + elif i == v + 1: 1.180 + a_args = {"id": "next"} 1.181 + elif i == v - 1: 1.182 + a_args = {"id": "prev"} 1.183 + 1.184 + _go_thumbnail_links_to_browse_imgs_html_body(p, d, t, b, a_args, img_args) 1.185 + 1.186 + return str(root).encode("utf-8")