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")