annotate pics3/pics_flask_app.py @ 125:d216dd8e63da

pics3: implement thumbs page
author paulo
date Sat, 01 May 2021 01:59:08 -0700
parents 9b57b90aea31
children 06f97e38e1b2
rev   line source
paulo@124 1 import csv
paulo@124 2 import datetime
paulo@124 3 import random
paulo@124 4 import os
paulo@124 5
paulo@124 6 import flask
paulo@124 7 import google.cloud.storage
paulo@124 8 from html3.html3 import HTML
paulo@124 9
paulo@124 10 app = flask.Flask(__name__)
paulo@124 11
paulo@124 12 GCS_CLIENT = google.cloud.storage.Client()
paulo@124 13 GCS_BUCKET = GCS_CLIENT.get_bucket(os.environ.get("GCS_BUCKET"))
paulo@124 14
paulo@124 15
paulo@124 16 class PicsDialect(csv.Dialect):
paulo@124 17 delimiter = '\t'
paulo@124 18 quoting = csv.QUOTE_NONE
paulo@124 19 lineterminator = '\n'
paulo@124 20
paulo@124 21 PICSDIALECT = PicsDialect()
paulo@124 22
paulo@124 23
paulo@124 24 def _parse_dt(dts):
paulo@124 25 return datetime.datetime.strptime(dts, "%Y%m%d")
paulo@124 26
paulo@124 27
paulo@124 28 def _format_dt(dt):
paulo@124 29 return dt.strftime("%Y-%m-%d")
paulo@124 30
paulo@124 31
paulo@124 32 def _numeric_pad_basename(path, maxdigits=20):
paulo@124 33 return os.path.basename(path).zfill(maxdigits)
paulo@124 34
paulo@124 35
paulo@124 36 def _get_images(d):
paulo@124 37 exts = (".jpg", ".webm")
paulo@124 38 thumb_dir = f"pics/{d}/thumbs"
paulo@124 39 browse_dir = f"pics/{d}/browse"
paulo@124 40
paulo@124 41 thumb_fns = [i.name for i in GCS_CLIENT.list_blobs(GCS_BUCKET, prefix=thumb_dir) if i.name.endswith(exts)]
paulo@124 42 thumb_fns = sorted(thumb_fns, key=_numeric_pad_basename)
paulo@124 43
paulo@124 44 browse_contents = set(i.name for i in GCS_CLIENT.list_blobs(GCS_BUCKET, prefix=browse_dir) if i.name.endswith(exts))
paulo@124 45 browse_fns = []
paulo@124 46 for i in thumb_fns:
paulo@124 47 i_basename = os.path.splitext(os.path.basename(i))[0]
paulo@124 48 try:
paulo@124 49 for j in exts:
paulo@124 50 browse_fn = browse_dir + "/" + i_basename + j
paulo@124 51 if browse_fn in browse_contents:
paulo@124 52 browse_fns.append(browse_fn)
paulo@124 53 raise StopIteration
paulo@124 54 except StopIteration:
paulo@124 55 pass
paulo@124 56 else:
paulo@124 57 raise RuntimeError(f"Cannot find browse image for {i}")
paulo@124 58
paulo@124 59 return zip(thumb_fns, browse_fns)
paulo@124 60
paulo@124 61
paulo@124 62 def _get_standard_html_doc(title):
paulo@124 63 root = HTML("html")
paulo@124 64
paulo@124 65 header = root.head
paulo@124 66 header.link(rel="stylesheet", type="text/css", href=flask.url_for("static", filename="index.css"))
paulo@124 67 header.title(title)
paulo@124 68
paulo@124 69 body = root.body
paulo@124 70 body.h1(title)
paulo@124 71
paulo@124 72 return (root, header, body)
paulo@124 73
paulo@124 74
paulo@124 75 def _go_thumbnail_links_to_browse_imgs_html_body(body, d, t, b, a_args={}, img_args={}, lazyload=False):
paulo@124 76 thumb_img_url = GCS_BUCKET.get_blob(t).public_url
paulo@124 77 browse_url = flask.url_for("browse", d=d, img=os.path.basename(b))
paulo@124 78
paulo@124 79 a = body.a(href=browse_url, **a_args)
paulo@124 80 if lazyload:
paulo@124 81 img_args = dict(img_args)
paulo@124 82 img_args["data-src"] = thumb_img_url
paulo@124 83 a.img(**img_args)
paulo@124 84 else:
paulo@124 85 a.img(src=thumb_img_url, **img_args)
paulo@124 86
paulo@124 87 body.text(" ")
paulo@124 88
paulo@124 89
paulo@125 90 def _go_thumbnail_links_to_thumbs_html_body(body, d, t, a_args={}, img_args={}):
paulo@125 91 thumb_img_url = GCS_BUCKET.get_blob(t).public_url
paulo@125 92 thumbs_url_args = {"from": os.path.basename(t), "_anchor": "selected"}
paulo@125 93 thumbs_url = flask.url_for("thumbs", d=d, **thumbs_url_args)
paulo@125 94
paulo@125 95 a = body.a(href=thumbs_url, **a_args)
paulo@125 96 a.img(src=thumb_img_url, **img_args)
paulo@125 97
paulo@125 98 body.text(" ")
paulo@125 99
paulo@125 100
paulo@124 101 @app.route("/")
paulo@124 102 def index():
paulo@124 103 n = 5 # number of thumbnails to display per dir
paulo@124 104
paulo@124 105 (root, header, body) = _get_standard_html_doc("Pictures")
paulo@124 106 header.script('', type="text/javascript", src=flask.url_for("static", filename="lazyload.js"))
paulo@124 107
paulo@124 108 pics_dirs = []
paulo@124 109 pics_dirs_index_blob = GCS_BUCKET.get_blob("pics/index.tsv")
paulo@124 110 if pics_dirs_index_blob:
paulo@124 111 pics_dirs_index_strlist = pics_dirs_index_blob.download_as_text().splitlines()
paulo@124 112 pics_dirs_index_reader = csv.reader(pics_dirs_index_strlist, PICSDIALECT)
paulo@125 113 pics_dirs = sorted(pics_dirs_index_reader, key=lambda x: x[0], reverse=True)
paulo@124 114
paulo@124 115 for (dts, d) in pics_dirs:
paulo@124 116 dt = _parse_dt(dts)
paulo@125 117 body.h2.a(d, href=flask.url_for("thumbs", d=d))
paulo@124 118 body.h3(_format_dt(dt))
paulo@124 119
paulo@124 120 imgs = _get_images(d)
paulo@124 121 imgs_idx = [(i, img) for (i, img) in enumerate(imgs)]
paulo@124 122
paulo@124 123 sampled_imgs_idx = random.sample(imgs_idx, min(len(imgs_idx), n))
paulo@124 124 sampled_imgs_idx.sort(key=lambda x: x[0])
paulo@124 125
paulo@124 126 p = body.p
paulo@124 127 for (i, (t, b)) in sampled_imgs_idx:
paulo@124 128 _go_thumbnail_links_to_browse_imgs_html_body(p, d, t, b, lazyload=True)
paulo@124 129
paulo@124 130 return str(root).encode("utf-8")
paulo@124 131
paulo@124 132
paulo@124 133 @app.route("/<d>/browse/<img>")
paulo@124 134 def browse(d, img):
paulo@124 135 browse_img_blob = GCS_BUCKET.get_blob(f"pics/{d}/browse/{img}")
paulo@124 136 if not browse_img_blob:
paulo@124 137 flask.abort(404)
paulo@124 138
paulo@124 139 imgs = list(_get_images(d))
paulo@124 140
paulo@124 141 # thumbnail preview ribbon
paulo@124 142 w = 7 # must be odd
paulo@124 143 v = int(w/2)
paulo@124 144 imgs_circ = [None] * w
paulo@124 145 x = None
paulo@124 146 n = len(imgs)
paulo@124 147 for (i, (t, b)) in enumerate(imgs):
paulo@124 148 if os.path.basename(b) == img:
paulo@124 149 x = i + 1
paulo@124 150 imgs_circ[v] = (t, b)
paulo@124 151 for j in range(1, v + 1):
paulo@124 152 if (i + j) < n: imgs_circ[v + j] = imgs[i + j]
paulo@124 153 if (i - j) >= 0: imgs_circ[v - j] = imgs[i - j]
paulo@124 154 break
paulo@124 155
paulo@124 156 if x is None:
paulo@124 157 raise AssertionError
paulo@124 158
paulo@124 159 (root, header, body) = _get_standard_html_doc(f"{d} \u2014 {x} of {n}")
paulo@124 160 header.script('', type="text/javascript", src=flask.url_for("static", filename="np_keys.js"))
paulo@124 161
paulo@124 162 browse_img_url = browse_img_blob.public_url
paulo@124 163 ext = os.path.splitext(img)[1]
paulo@124 164 p = body.p
paulo@124 165 if ext == ".webm":
paulo@124 166 p.video(src=browse_img_url, autoplay="true", loop="true")
paulo@124 167 else:
paulo@124 168 p.img(src=browse_img_url)
paulo@124 169
paulo@124 170 p = body.p
paulo@124 171 for (i, img_c) in enumerate(imgs_circ):
paulo@124 172 if img_c is not None:
paulo@124 173 (t, b) = img_c
paulo@124 174 a_args = {}
paulo@124 175 img_args = {}
paulo@125 176 if os.path.basename(b) == img:
paulo@125 177 a_args = {"id": "up"}
paulo@125 178 img_args = {"klass": "sel"}
paulo@125 179 b = None
paulo@124 180 elif i == v + 1:
paulo@124 181 a_args = {"id": "next"}
paulo@124 182 elif i == v - 1:
paulo@124 183 a_args = {"id": "prev"}
paulo@124 184
paulo@125 185 if b is None:
paulo@125 186 _go_thumbnail_links_to_thumbs_html_body(p, d, t, a_args, img_args)
paulo@125 187 else:
paulo@125 188 _go_thumbnail_links_to_browse_imgs_html_body(p, d, t, b, a_args, img_args)
paulo@124 189
paulo@124 190 return str(root).encode("utf-8")
paulo@125 191
paulo@125 192
paulo@125 193 @app.route("/<d>")
paulo@125 194 def thumbs(d):
paulo@125 195 args = flask.request.args
paulo@125 196 print(f"args = {args}")
paulo@125 197
paulo@125 198 from_img = args.get("from")
paulo@125 199
paulo@125 200 imgs = list(_get_images(d))
paulo@125 201 (root, header, body) = _get_standard_html_doc(d)
paulo@125 202
paulo@125 203 p = body.p
paulo@125 204 for (t, b) in imgs:
paulo@125 205 img_args = {}
paulo@125 206 if os.path.basename(b) == from_img:
paulo@125 207 img_args={"klass": "sel2", "id":"selected"}
paulo@125 208 _go_thumbnail_links_to_browse_imgs_html_body(p, d, t, b, img_args=img_args)
paulo@125 209
paulo@125 210 return str(root).encode("utf-8")