view pics2/pics_app.py @ 114:d8239a080f44

cs: set domain and secure cookie parameters for unset also
author paulo
date Mon, 03 Aug 2020 02:01:05 -0600
parents d521df55f56c
children
line source
1 import os
2 import re
3 import glob
4 import traceback
5 import datetime
6 import random
7 import urlparse
9 import html
11 import pinlib
13 import logging
14 logging.basicConfig(
15 level=logging.INFO,
16 filename="_LOG",
17 format="%(asctime)s %(levelname)-8s %(message)s",
18 )
21 def _is_pics_dir(dirpath):
22 return os.path.exists(os.path.join(dirpath, "_picsroot"))
25 def _get_dir_dt(dirpath):
26 if _is_pics_dir(dirpath):
27 dirpath = os.path.join(dirpath, "_picsroot")
29 return datetime.datetime.fromtimestamp(os.stat(dirpath).st_mtime)
32 def _format_dt(dt):
33 return dt.strftime("%Y-%m-%d")
36 def _parse_path_info(path_info):
37 ppi = path_info.split(os.sep)
38 if len(ppi) > 1 and ppi[-1] == '':
39 del ppi[-1]
41 return ppi
44 def _numeric_pad_basename(path, maxdigits=20):
45 return os.path.basename(path).zfill(maxdigits)
48 def _get_images(d):
49 exts = [".jpg", ".webm"]
51 thumb_fns = glob.glob(os.path.join(d, "thumbs", "*.jpg"))
52 thumb_fns = sorted(thumb_fns, key=_numeric_pad_basename)
53 logging.debug("thumb_fns = %s" % thumb_fns)
55 browse_dir = os.path.join(d, "browse")
56 browse_contents = set(os.listdir(browse_dir))
57 logging.debug("browse_contents = %s" % browse_contents)
59 browse_fns = []
60 for i in thumb_fns:
61 i_basename = os.path.splitext(os.path.basename(i))[0]
62 try:
63 for j in exts:
64 browse_fn_basename = i_basename + j
65 if browse_fn_basename in browse_contents:
66 browse_fns.append(os.path.join(browse_dir, browse_fn_basename))
67 raise StopIteration
68 except StopIteration:
69 pass
70 else:
71 raise RuntimeError("Cannot find browse image for %s" % i)
72 logging.debug("browse_fns = %s" % browse_fns)
74 return zip(thumb_fns, browse_fns)
77 class Main:
78 def _get_pics_url(self, dirpath):
79 script_name = self._environ.get("SCRIPT_NAME", '')
80 return os.path.normpath(os.path.join(os.path.dirname(script_name), dirpath))
83 def _get_app_url(self, dirpath):
84 script_name = self._environ.get("SCRIPT_NAME", '')
85 return os.path.normpath(os.path.join(script_name, dirpath))
88 def _get_standard_html_doc(self, title):
89 root = html.HTML("html")
91 header = root.head
92 header.link(rel="stylesheet", type="text/css", href=self._get_pics_url("index.css"))
93 header.title(title)
95 body = root.body
96 body.h1(title)
98 return (root, header, body)
101 def _go_thumbnail_links_to_browse_imgs_html_body(self, body, t, b, a_args={}, img_args={}, lazyload=False):
102 thumb_img_url = self._get_pics_url(t)
103 browse_url = self._get_app_url(b)
105 a = body.a(href=browse_url, **a_args)
106 if lazyload:
107 img_args = dict(img_args)
108 img_args["data-src"] = thumb_img_url
109 a.img(**img_args)
110 else:
111 a.img(src=thumb_img_url, **img_args)
113 body.text(' ')
116 def _go_browse_image_html_body(self, body, img):
117 browse_img_url = self._get_pics_url(img)
119 ext = os.path.splitext(img)[1]
120 if ext == ".webm":
121 body.video(src=browse_img_url, autoplay="true", loop="true")
122 else:
123 body.img(src=browse_img_url)
126 def __init__(self, environ):
127 self._environ = environ
128 self._page_func = None
129 self._show_index = False
131 #logging.debug("environ = %s" % (sorted(self._environ.items(), key=lambda x: x[0]),))
132 logging.debug("environ['PATH_INFO'] = %s" % self._environ["PATH_INFO"])
133 logging.debug("environ['SCRIPT_NAME'] = %s" % self._environ["SCRIPT_NAME"])
134 logging.debug("environ['QUERY_STRING'] = %s" % self._environ["QUERY_STRING"])
136 pi = self._environ["PATH_INFO"]
137 ppi = _parse_path_info(pi)
138 logging.debug("ppi = %s" % ppi)
140 if len(ppi) < 1 or ppi[0] != '':
141 raise AssertionError("Parsed path length must start empty: " + pi)
143 self._qs = urlparse.parse_qs(self._environ["QUERY_STRING"])
144 logging.debug("self._qs = %s" % self._qs)
145 try:
146 pinlib.PinMan("lahat").check(pinlib.parse_cookies(self._environ))
147 except pinlib.PinFailError:
148 pass
149 else:
150 self._show_index = True
152 if len(ppi) >= 2 and _is_pics_dir(ppi[1]):
153 if len(ppi) == 2:
154 self._page_func = self.page_thumbs
155 elif len(ppi) >= 4 and ppi[2] == "browse" and os.path.exists(os.path.join(*ppi)):
156 self._page_func = self.page_browse
157 elif len(ppi) == 1 and self._show_index:
158 self._page_func = self.page_index
160 if self._page_func is None:
161 raise RuntimeError("Cannot find path: " + pi)
164 def page(self):
165 return unicode(self._page_func()).encode("utf-8")
168 def page_index(self):
169 n = 5 # number of thumbnails to display per dir
171 (html_root, html_header, html_body) = self._get_standard_html_doc("Pictures")
173 html_header.script('', type="text/javascript", src=self._get_pics_url("lazyload.js"))
175 pics_dirs = []
176 for i in os.listdir('.'):
177 if _is_pics_dir(i):
178 pics_dirs.append((i, _get_dir_dt(i)))
180 pics_dirs.sort(key=lambda x: x[1], reverse=True)
182 for (d, dt) in pics_dirs:
183 html_body.h2.a(d, href=self._get_app_url(d))
184 html_body.h3(_format_dt(dt))
186 imgs = _get_images(d)
187 imgs_idx = [(i, img) for (i, img) in enumerate(imgs)]
189 sampled_imgs_idx = random.sample(imgs_idx, min(len(imgs_idx), n))
190 sampled_imgs_idx.sort(key=lambda x: x[0])
192 html_p = html_body.p
193 for (i, (t, b)) in sampled_imgs_idx:
194 self._go_thumbnail_links_to_browse_imgs_html_body(html_p, t, b, lazyload=True)
196 return html_root
199 def page_thumbs(self):
200 ppi = _parse_path_info(self._environ["PATH_INFO"])
201 d = os.path.join(*ppi)
202 (html_root, html_header, html_body) = self._get_standard_html_doc(d)
204 from_img = None
205 if "from" in self._qs:
206 from_img = self._qs["from"][0]
208 html_p = html_body.p
209 for (t, b) in _get_images(d):
210 if from_img is not None and b == from_img:
211 self._go_thumbnail_links_to_browse_imgs_html_body(html_p, t, b, img_args={"klass":"sel2", "id":"selected"})
212 else:
213 self._go_thumbnail_links_to_browse_imgs_html_body(html_p, t, b)
215 # TODO: fix me
216 #if self._show_index:
217 # html_body.a("(Other pictures)", href=self._get_app_url(''))
219 return html_root
222 def page_browse(self):
223 ppi = _parse_path_info(self._environ["PATH_INFO"])
224 d = os.path.join(*ppi[:2])
225 imgs = _get_images(d)
226 img = os.path.join(*ppi)
228 # thumbnail preview ribbon
229 w = 7 # must be odd
230 v = w/2
231 imgs_circ = [None] * w
232 x = None
233 n = len(imgs)
234 for (i, (t, b)) in enumerate(imgs):
235 if b == img:
236 x = i + 1
237 imgs_circ[v] = (t, b)
238 for j in range(1, v + 1):
239 if (i + j) < n: imgs_circ[v + j] = imgs[i + j]
240 if (i - j) >= 0: imgs_circ[v - j] = imgs[i - j]
242 break
244 if x is None:
245 raise AssertionError
247 (html_root, html_header, html_body) = self._get_standard_html_doc(u"%s \u2014 %s of %s" % (d, x, len(imgs)))
249 html_header.script('', type="text/javascript", src=self._get_pics_url("np_keys.js"))
251 self._go_browse_image_html_body(html_body.p, img)
253 logging.debug("imgs_circ = %s" % imgs_circ)
255 html_p = html_body.p
256 for (i, img_c) in enumerate(imgs_circ):
257 if img_c is not None:
258 (t, b) = img_c
259 a_args = {}
260 img_args = {}
261 if b == img:
262 a_args = {"id": "up"}
263 img_args = {"klass": "sel"}
264 b = "%s?from=%s#selected" % (d, img)
265 elif i == v + 1:
266 a_args = {"id": "next"}
267 elif i == v - 1:
268 a_args = {"id": "prev"}
270 self._go_thumbnail_links_to_browse_imgs_html_body(html_p, t, b, a_args, img_args)
272 return html_root
275 def app(environ, start_response):
276 response_code = "500 Internal Server Error"
277 response_type = "text/plain; charset=UTF-8"
279 try:
280 response_body = Main(environ).page()
281 response_code = "200 OK"
282 response_type = "text/html; charset=UTF-8"
283 except:
284 response_body = traceback.format_exc()
286 response_headers = [
287 ("Content-Type", response_type),
288 ("Content-Length", str(len(response_body))),
289 ]
291 start_response(response_code, response_headers)
293 return [response_body]