annotate 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
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@124 90 @app.route("/")
paulo@124 91 def index():
paulo@124 92 n = 5 # number of thumbnails to display per dir
paulo@124 93
paulo@124 94 (root, header, body) = _get_standard_html_doc("Pictures")
paulo@124 95 header.script('', type="text/javascript", src=flask.url_for("static", filename="lazyload.js"))
paulo@124 96
paulo@124 97 #body.pre(str(list(GCS_CLIENT.list_blobs(GCS_BUCKET))))
paulo@124 98
paulo@124 99 pics_dirs = []
paulo@124 100 pics_dirs_index_blob = GCS_BUCKET.get_blob("pics/index.tsv")
paulo@124 101 if pics_dirs_index_blob:
paulo@124 102 pics_dirs_index_strlist = pics_dirs_index_blob.download_as_text().splitlines()
paulo@124 103 pics_dirs_index_reader = csv.reader(pics_dirs_index_strlist, PICSDIALECT)
paulo@124 104 pics_dirs = sorted(pics_dirs_index_reader, key=lambda x: x[0])
paulo@124 105
paulo@124 106 for (dts, d) in pics_dirs:
paulo@124 107 dt = _parse_dt(dts)
paulo@124 108 body.h2(d)
paulo@124 109 body.h3(_format_dt(dt))
paulo@124 110
paulo@124 111 #body.pre(str(list(_get_images(d))))
paulo@124 112
paulo@124 113 imgs = _get_images(d)
paulo@124 114 imgs_idx = [(i, img) for (i, img) in enumerate(imgs)]
paulo@124 115
paulo@124 116 sampled_imgs_idx = random.sample(imgs_idx, min(len(imgs_idx), n))
paulo@124 117 sampled_imgs_idx.sort(key=lambda x: x[0])
paulo@124 118
paulo@124 119 p = body.p
paulo@124 120 for (i, (t, b)) in sampled_imgs_idx:
paulo@124 121 _go_thumbnail_links_to_browse_imgs_html_body(p, d, t, b, lazyload=True)
paulo@124 122
paulo@124 123 return str(root).encode("utf-8")
paulo@124 124
paulo@124 125
paulo@124 126 @app.route("/<d>/browse/<img>")
paulo@124 127 def browse(d, img):
paulo@124 128 browse_img_blob = GCS_BUCKET.get_blob(f"pics/{d}/browse/{img}")
paulo@124 129 if not browse_img_blob:
paulo@124 130 flask.abort(404)
paulo@124 131
paulo@124 132 imgs = list(_get_images(d))
paulo@124 133
paulo@124 134 # thumbnail preview ribbon
paulo@124 135 w = 7 # must be odd
paulo@124 136 v = int(w/2)
paulo@124 137 imgs_circ = [None] * w
paulo@124 138 x = None
paulo@124 139 n = len(imgs)
paulo@124 140 for (i, (t, b)) in enumerate(imgs):
paulo@124 141 if os.path.basename(b) == img:
paulo@124 142 x = i + 1
paulo@124 143 imgs_circ[v] = (t, b)
paulo@124 144 for j in range(1, v + 1):
paulo@124 145 if (i + j) < n: imgs_circ[v + j] = imgs[i + j]
paulo@124 146 if (i - j) >= 0: imgs_circ[v - j] = imgs[i - j]
paulo@124 147 break
paulo@124 148
paulo@124 149 if x is None:
paulo@124 150 raise AssertionError
paulo@124 151
paulo@124 152 (root, header, body) = _get_standard_html_doc(f"{d} \u2014 {x} of {n}")
paulo@124 153 header.script('', type="text/javascript", src=flask.url_for("static", filename="np_keys.js"))
paulo@124 154
paulo@124 155 browse_img_url = browse_img_blob.public_url
paulo@124 156 ext = os.path.splitext(img)[1]
paulo@124 157 p = body.p
paulo@124 158 if ext == ".webm":
paulo@124 159 p.video(src=browse_img_url, autoplay="true", loop="true")
paulo@124 160 else:
paulo@124 161 p.img(src=browse_img_url)
paulo@124 162
paulo@124 163 p = body.p
paulo@124 164 for (i, img_c) in enumerate(imgs_circ):
paulo@124 165 if img_c is not None:
paulo@124 166 (t, b) = img_c
paulo@124 167 a_args = {}
paulo@124 168 img_args = {}
paulo@124 169 # FIXME
paulo@124 170 #if b == img:
paulo@124 171 # a_args = {"id": "up"}
paulo@124 172 # img_args = {"klass": "sel"}
paulo@124 173 # b = f"{d}?from={img}#selected"
paulo@124 174 if False:
paulo@124 175 pass
paulo@124 176 elif i == v + 1:
paulo@124 177 a_args = {"id": "next"}
paulo@124 178 elif i == v - 1:
paulo@124 179 a_args = {"id": "prev"}
paulo@124 180
paulo@124 181 _go_thumbnail_links_to_browse_imgs_html_body(p, d, t, b, a_args, img_args)
paulo@124 182
paulo@124 183 return str(root).encode("utf-8")