|
1 import os, os.path |
|
2 |
|
3 import settings, image, utils |
|
4 from log import index, render |
|
5 from template import gallery as gallery_tpl |
|
6 from helpers import url_for_page |
|
7 |
|
8 def dirUp (count=1) : |
|
9 """ |
|
10 Returns a relative path to the directly count levels above the current one |
|
11 """ |
|
12 |
|
13 if not count : |
|
14 return '.' |
|
15 |
|
16 return os.path.join(*(['..']*count)) |
|
17 |
|
18 def isImage (fname) : |
|
19 """ |
|
20 Is the given filename likely to be an image file? |
|
21 """ |
|
22 |
|
23 fname = fname.lower() |
|
24 base, ext = os.path.splitext(fname) |
|
25 ext = ext.lstrip('.') |
|
26 |
|
27 return ext in settings.IMAGE_EXTS |
|
28 |
|
29 class Folder (object) : |
|
30 def __init__ (self, name='.', parent=None) : |
|
31 # the directory name, no trailing / |
|
32 self.name = name.rstrip(os.sep) |
|
33 |
|
34 # our parent Folder, or None |
|
35 self.parent = parent |
|
36 |
|
37 # the path to this dir, as a relative path to the root of the image gallery, always starts with . |
|
38 if parent and name : |
|
39 self.path = parent.pathFor(self.name) |
|
40 else : |
|
41 self.path = self.name |
|
42 |
|
43 # the url-path to the index.html file |
|
44 self.html_path = self.path |
|
45 |
|
46 # dict of fname -> Folder |
|
47 self.subdirs = {} |
|
48 |
|
49 # dict of fname -> Image |
|
50 self.images = {} |
|
51 |
|
52 # our human-friendly title |
|
53 self.title = None |
|
54 |
|
55 # our long-winded description |
|
56 self.descr = '' |
|
57 |
|
58 # is this folder non-empty? |
|
59 self.alive = None |
|
60 |
|
61 # self.images.values(), but sorted by filename |
|
62 self.sorted_images = [] |
|
63 |
|
64 # the ShortURL key to this dir |
|
65 self.shorturl_code = None |
|
66 |
|
67 # were we filtered out? |
|
68 self.filtered = False |
|
69 |
|
70 def pathFor (self, *fnames) : |
|
71 """ |
|
72 Return a root-relative path to the given path inside this dir |
|
73 """ |
|
74 return os.path.join(self.path, *fnames) |
|
75 |
|
76 def index (self, filters=None) : |
|
77 """ |
|
78 Look for other dirs and images inside this dir. Filters must be either None, |
|
79 whereupon all files will be included, or a dict of {filename -> next_filter}. |
|
80 If given, only filenames that are present in the dict will be indexed, and in |
|
81 the case of dirs, the next_filter will be passed on to that Folder's index |
|
82 method. |
|
83 """ |
|
84 |
|
85 index.info("Indexing %s", self.path) |
|
86 |
|
87 if filters : |
|
88 self.filtered = True |
|
89 |
|
90 # iterate through listdir |
|
91 for fname in os.listdir(self.path) : |
|
92 # the full filesystem path to it |
|
93 fpath = self.pathFor(fname) |
|
94 |
|
95 # ignore dotfiles |
|
96 if fname.startswith('.') : |
|
97 index.debug("Skipping dotfile %s", fname) |
|
98 continue |
|
99 |
|
100 # apply filters |
|
101 if filters : |
|
102 if fname in filters : |
|
103 next_filter = filters[fname] |
|
104 else : |
|
105 index.debug("Skip `%s' as we have a filter", fname) |
|
106 continue |
|
107 else : |
|
108 next_filter = None |
|
109 |
|
110 # recurse into subdirs, but not thumbs/previews |
|
111 if (os.path.isdir(fpath) |
|
112 and (fname not in (settings.THUMB_DIR, settings.PREVIEW_DIR)) |
|
113 and (self.parent or fname not in settings.ROOT_IGNORE) |
|
114 ) : |
|
115 index.debug("Found subdir %s", fpath) |
|
116 f = self.subdirs[fname] = Folder(fname, self) |
|
117 if f.index(next_filter) : # recursion |
|
118 # if a subdir is alive, we are alive as well |
|
119 self.alive = True |
|
120 |
|
121 # handle images |
|
122 elif os.path.isfile(fpath) and isImage(fname) : |
|
123 index.debug("Found image %s", fname) |
|
124 self.images[fname] = image.Image(self, fname) |
|
125 |
|
126 # ignore everything else |
|
127 else : |
|
128 index.debug("Ignoring file %s", fname) |
|
129 |
|
130 # sort and link the images |
|
131 if self.images : |
|
132 self.alive = True |
|
133 |
|
134 # sort the images |
|
135 fnames = self.images.keys() |
|
136 fnames.sort() |
|
137 |
|
138 prev = None |
|
139 |
|
140 # link |
|
141 for fname in fnames : |
|
142 img = self.images[fname] |
|
143 |
|
144 img.prev = prev |
|
145 |
|
146 if prev : |
|
147 prev.next = img |
|
148 |
|
149 prev = img |
|
150 |
|
151 # add to the sorted images list |
|
152 self.sorted_images.append(img) |
|
153 |
|
154 # figure out our title/ descr. Must be done before our parent dir is rendered (self.title) |
|
155 title_path = self.pathFor(settings.TITLE_FILE) |
|
156 |
|
157 self.title, self.descr = utils.readTitleDescr(title_path) |
|
158 |
|
159 # default title for the root dir |
|
160 if self.title : |
|
161 self.alive = True |
|
162 pass # use what was in the title file |
|
163 |
|
164 elif not self.parent : |
|
165 self.title = 'Index' |
|
166 |
|
167 else : |
|
168 self.title = self.name |
|
169 |
|
170 if self.descr : |
|
171 self.alive = True |
|
172 |
|
173 return self.alive |
|
174 |
|
175 def getObjInfo (self) : |
|
176 """ |
|
177 Metadata for shorturls2.db |
|
178 """ |
|
179 return 'dir', self.path, '' |
|
180 |
|
181 def breadcrumb (self) : |
|
182 """ |
|
183 Returns a [(fname, title)] list of this dir's parent dirs |
|
184 """ |
|
185 |
|
186 f = self |
|
187 b = [] |
|
188 d = 0 |
|
189 |
|
190 while f : |
|
191 b.insert(0, (dirUp(d), f.title)) |
|
192 |
|
193 d += 1 |
|
194 f = f.parent |
|
195 |
|
196 return b |
|
197 |
|
198 def countParents (self, acc=0) : |
|
199 if self.parent : |
|
200 return self.parent.countParents(acc+1) |
|
201 else : |
|
202 return acc |
|
203 |
|
204 def inRoot (self, *fnames) : |
|
205 """ |
|
206 Return a relative URL from this dir to the given path in the root dir |
|
207 """ |
|
208 |
|
209 c = self.countParents() |
|
210 |
|
211 return utils.url_join(*((['..']*c) + list(fnames))) |
|
212 |
|
213 def render (self) : |
|
214 """ |
|
215 Render the index.html, Images, and recurse into subdirs |
|
216 """ |
|
217 |
|
218 # ded folders are skipped |
|
219 if not self.alive : |
|
220 render.info("Skipping dir %s (no images)", self.path) |
|
221 return |
|
222 |
|
223 # 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 |
|
224 if self.filtered : |
|
225 render.warning("Dir `%s' contents were filtered, so we won't render the gallery index again", self.path) |
|
226 |
|
227 else : |
|
228 # create the thumb/preview dirs if needed |
|
229 for dir in (settings.THUMB_DIR, settings.PREVIEW_DIR) : |
|
230 path = self.pathFor(dir) |
|
231 |
|
232 if not os.path.isdir(path) : |
|
233 render.info("Creating dir %s", path) |
|
234 os.mkdir(path) |
|
235 |
|
236 # sort the subdirs |
|
237 subdirs = self.subdirs.values() |
|
238 subdirs.sort(key=lambda d: d.name) |
|
239 |
|
240 render.info("Rendering %s", self.path) |
|
241 |
|
242 # paginate! |
|
243 images = self.sorted_images |
|
244 image_count = len(images) |
|
245 pages = [] |
|
246 |
|
247 while images : |
|
248 pages.append(images[:settings.IMAGE_COUNT]) |
|
249 images = images[settings.IMAGE_COUNT:] |
|
250 |
|
251 pagination_required = len(pages) > 1 |
|
252 |
|
253 if pagination_required : |
|
254 render.info("Index split into %d pages of %d images each", len(pages), settings.IMAGE_COUNT) |
|
255 |
|
256 for cur_page, images in enumerate(pages) : |
|
257 if pagination_required and cur_page > 0 : |
|
258 shorturl = "%s/%s" % (self.shorturl_code, cur_page+1) |
|
259 else : |
|
260 shorturl = self.shorturl_code |
|
261 |
|
262 # render to index.html |
|
263 gallery_tpl.render_to(self.pathFor(url_for_page(cur_page)), |
|
264 stylesheet_url = self.inRoot('style.css'), |
|
265 title = self.title, |
|
266 breadcrumb = self.breadcrumb(), |
|
267 |
|
268 dirs = subdirs, |
|
269 images = images, |
|
270 |
|
271 num_pages = len(pages), |
|
272 cur_page = cur_page, |
|
273 |
|
274 description = self.descr, |
|
275 |
|
276 shorturl = self.inRoot('s', shorturl), |
|
277 shorturl_code = shorturl, |
|
278 ) |
|
279 |
|
280 # render images |
|
281 for img in self.images.itervalues() : |
|
282 img.render() |
|
283 |
|
284 # recurse into subdirs |
|
285 for dir in self.subdirs.itervalues() : |
|
286 dir.render() |
|
287 |