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