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