112 return self.subfile("index.html") |
112 return self.subfile("index.html") |
113 |
113 |
114 def index (self) : |
114 def index (self) : |
115 """ |
115 """ |
116 Recursively index this Folder, yielding a series of all Folder objects inside. |
116 Recursively index this Folder, yielding a series of all Folder objects inside. |
|
117 |
|
118 XXX: not used |
117 """ |
119 """ |
118 |
120 |
119 # and subfolders |
121 # and subfolders |
120 for subfolder in self.subfolders : |
122 for subfolder in self.subfolders : |
121 yield subfolder |
123 yield subfolder |
122 |
124 |
123 for item in subfolder.index_subfolders() : |
125 for item in subfolder.index_subfolders() : |
124 yield item |
126 yield item |
125 |
127 |
126 class Folder (object) : |
|
127 def __init__ (self, name='.', parent=None) : |
|
128 # the directory name, no trailing / |
|
129 self.name = unicode(name.rstrip(os.sep)) |
|
130 |
|
131 # our parent Folder, or None |
|
132 self.parent = parent |
|
133 |
|
134 # the path to this dir, as a relative path to the root of the image gallery, always starts with . |
|
135 if parent and name : |
|
136 self.path = parent.pathFor(self.name) |
|
137 else : |
|
138 self.path = self.name |
|
139 |
|
140 # the url-path to the index.html file |
|
141 self.html_path = self.path |
|
142 |
|
143 # dict of fname -> Folder |
|
144 self.subdirs = {} |
|
145 |
|
146 # dict of fname -> Image |
|
147 self.images = {} |
|
148 |
|
149 # our human-friendly title |
|
150 self.title = None |
|
151 |
|
152 # our long-winded description |
|
153 self.descr = '' |
|
154 |
|
155 # is this folder non-empty? |
|
156 self.alive = None |
|
157 |
|
158 # self.images.values(), but sorted by filename |
|
159 self.sorted_images = [] |
|
160 |
|
161 # the ShortURL key to this dir |
|
162 self.shorturl_code = None |
|
163 |
|
164 # were we filtered out? |
|
165 self.filtered = False |
|
166 |
|
167 def pathFor (self, *fnames) : |
|
168 """ |
|
169 Return a root-relative path to the given path inside this dir |
|
170 """ |
|
171 return os.path.join(self.path, *fnames) |
|
172 |
|
173 def index (self, filters=None) : |
|
174 """ |
|
175 Look for other dirs and images inside this dir. Filters must be either None, |
|
176 whereupon all files will be included, or a dict of {filename -> next_filter}. |
|
177 If given, only filenames that are present in the dict will be indexed, and in |
|
178 the case of dirs, the next_filter will be passed on to that Folder's index |
|
179 method. |
|
180 """ |
|
181 |
|
182 if filters : |
|
183 self.filtered = True |
|
184 |
|
185 # iterate through listdir |
|
186 for fname in os.listdir(self.path) : |
|
187 # the full filesystem path to it |
|
188 fpath = self.pathFor(fname) |
|
189 |
|
190 # ignore dotfiles |
|
191 if fname.startswith('.') : |
|
192 log.debug("Skipping dotfile %s", fname) |
|
193 continue |
|
194 |
|
195 # apply filters |
|
196 if filters : |
|
197 if fname in filters : |
|
198 next_filter = filters[fname] |
|
199 else : |
|
200 log.debug("Skip `%s' as we have a filter", fname) |
|
201 continue |
|
202 else : |
|
203 next_filter = None |
|
204 |
|
205 # recurse into subdirs, but not thumbs/previews |
|
206 if (os.path.isdir(fpath) |
|
207 and (fname not in (settings.THUMB_DIR, settings.PREVIEW_DIR)) |
|
208 and (self.parent or fname not in settings.ROOT_IGNORE) |
|
209 ) : |
|
210 log.down(fname) |
|
211 |
|
212 f = Folder(fname, self) |
|
213 |
|
214 try : |
|
215 if f.index(next_filter) : # recursion |
|
216 # if a subdir is alive, we are alive as well |
|
217 self.subdirs[fname] = f |
|
218 self.alive = True |
|
219 except Exception, e : |
|
220 log.warning("skip - %s: %s" % (type(e), e)) |
|
221 |
|
222 log.up() |
|
223 |
|
224 # handle images |
|
225 elif os.path.isfile(fpath) and utils.isImage(fname) : |
|
226 log.next(fname) |
|
227 self.images[fname] = image.Image(self, fname) |
|
228 |
|
229 # ignore everything else |
|
230 else : |
|
231 log.debug("Ignoring file %s", fname) |
|
232 |
|
233 # sort and link the images |
|
234 if self.images : |
|
235 self.alive = True |
|
236 |
|
237 # sort the images |
|
238 fnames = self.images.keys() |
|
239 fnames.sort() |
|
240 |
|
241 prev = None |
|
242 |
|
243 # link |
|
244 for fname in fnames : |
|
245 img = self.images[fname] |
|
246 |
|
247 img.prev = prev |
|
248 |
|
249 if prev : |
|
250 prev.next = img |
|
251 |
|
252 prev = img |
|
253 |
|
254 # add to the sorted images list |
|
255 self.sorted_images.append(img) |
|
256 |
|
257 # figure out our title/ descr. Must be done before our parent dir is rendered (self.title) |
|
258 title_path = self.pathFor(settings.TITLE_FILE) |
|
259 |
|
260 self.title, self.descr = utils.readTitleDescr(title_path) |
|
261 |
|
262 # default title for the root dir |
|
263 if self.title or self.descr : |
|
264 self.alive = True |
|
265 pass # use what was in the title file |
|
266 |
|
267 elif not self.parent : |
|
268 self.title = 'Index' |
|
269 |
|
270 else : |
|
271 self.title = self.name |
|
272 |
|
273 if not self.alive : |
|
274 log.debug("Dir %s isn't alive" % self.path) |
|
275 |
|
276 return self.alive |
|
277 |
|
278 def getObjInfo (self) : |
|
279 """ |
|
280 Metadata for shorturls2.db |
|
281 """ |
|
282 return 'dir', self.path, '' |
|
283 |
|
284 def breadcrumb (self, forImg=None) : |
|
285 """ |
|
286 Returns a [(fname, title)] list of this dir's parent dirs |
|
287 """ |
|
288 |
|
289 f = self |
|
290 b = [] |
|
291 d = 0 |
|
292 |
|
293 while f : |
|
294 # functionality of the slightly-hacked-in variety |
|
295 if f is self and forImg is not None : |
|
296 url = helpers.url_for_page(self.getPageNumber(forImg)) |
|
297 else : |
|
298 url = dirUp(d) |
|
299 |
|
300 b.insert(0, (url, f.title)) |
|
301 |
|
302 d += 1 |
|
303 f = f.parent |
|
304 |
|
305 return b |
|
306 |
|
307 def getPageNumber (self, img) : |
|
308 """ |
|
309 Get the page number that the given image is on |
|
310 """ |
|
311 |
|
312 return self.sorted_images.index(img) // settings.IMAGE_COUNT |
|
313 |
|
314 def countParents (self, acc=0) : |
|
315 if self.parent : |
|
316 return self.parent.countParents(acc+1) |
|
317 else : |
|
318 return acc |
|
319 |
|
320 def inRoot (self, *fnames) : |
|
321 """ |
|
322 Return a relative URL from this dir to the given path in the root dir |
|
323 """ |
|
324 |
|
325 c = self.countParents() |
|
326 |
|
327 return utils.url_join(*((['..']*c) + list(fnames))) |
|
328 |
|
329 def render (self) : |
|
330 """ |
|
331 Render the index.html, Images, and recurse into subdirs |
|
332 """ |
|
333 |
|
334 # ded folders are skipped |
|
335 if not self.alive : |
|
336 # dead, skip, no output |
|
337 return |
|
338 |
|
339 index_mtime = utils.mtime(self.pathFor("index.html")) |
|
340 dir_mtime = utils.mtime(self.path) |
|
341 |
|
342 # if this dir's contents were filtered out, then we can't render the index.html, as we aren't aware of all the images in here |
|
343 if self.filtered : |
|
344 log.warning("Dir `%s' contents were filtered, so we won't render the gallery index again", self.path) |
|
345 |
|
346 elif index_mtime > dir_mtime : |
|
347 # no changes, pass, ignored |
|
348 pass |
|
349 |
|
350 else : |
|
351 # create the thumb/preview dirs if needed |
|
352 for dir in (settings.THUMB_DIR, settings.PREVIEW_DIR) : |
|
353 path = self.pathFor(dir) |
|
354 |
|
355 if not os.path.isdir(path) : |
|
356 log.info("mkdir %s", dir) |
|
357 os.mkdir(path) |
|
358 |
|
359 # sort the subdirs |
|
360 subdirs = self.subdirs.values() |
|
361 subdirs.sort(key=lambda d: d.name) |
|
362 |
|
363 # paginate! |
|
364 images = self.sorted_images |
|
365 image_count = len(images) |
|
366 pages = [] |
|
367 |
|
368 while images : |
|
369 pages.append(images[:settings.IMAGE_COUNT]) |
|
370 images = images[settings.IMAGE_COUNT:] |
|
371 |
|
372 pagination_required = len(pages) > 1 |
|
373 |
|
374 if pagination_required : |
|
375 log.info("%d pages @ %d images", len(pages), settings.IMAGE_COUNT) |
|
376 elif not pages : |
|
377 log.info("no images, render for subdirs") |
|
378 pages = [[]] |
|
379 |
|
380 for cur_page, images in enumerate(pages) : |
|
381 if pagination_required and cur_page > 0 : |
|
382 shorturl = "%s/%s" % (self.shorturl_code, cur_page+1) |
|
383 else : |
|
384 shorturl = self.shorturl_code |
|
385 |
|
386 # render to index.html |
|
387 gallery_tpl.render_to(self.pathFor(url_for_page(cur_page)), |
|
388 stylesheet_url = self.inRoot('style.css'), |
|
389 title = self.title, |
|
390 breadcrumb = self.breadcrumb(), |
|
391 |
|
392 dirs = subdirs, |
|
393 images = images, |
|
394 |
|
395 num_pages = len(pages), |
|
396 cur_page = cur_page, |
|
397 |
|
398 description = self.descr, |
|
399 |
|
400 shorturl = self.inRoot('s', shorturl), |
|
401 shorturl_code = shorturl, |
|
402 ) |
|
403 |
|
404 # render images |
|
405 image_count = len(self.sorted_images) |
|
406 for i, img in enumerate(self.images.itervalues()) : |
|
407 log.next("[%-4d/%4d] %s", i + 1, image_count, img.name) |
|
408 |
|
409 img.render() |
|
410 |
|
411 # recurse into subdirs |
|
412 for dir in self.subdirs.itervalues() : |
|
413 log.down(dir.name) |
|
414 |
|
415 dir.render() |
|
416 |
|
417 log.up() |
|
418 |
|