# HG changeset patch # User Tero Marttila # Date 1244232311 -10800 # Node ID 67dd32adf159668cb2f29730ebe868f45cbfa01e # Parent 5b53fe29403479b20247be3f6e458f5db7d2e9aa remove old Folder, Image code, wrap up both diff -r 5b53fe294034 -r 67dd32adf159 degal/folder.py --- a/degal/folder.py Fri Jun 05 23:03:35 2009 +0300 +++ b/degal/folder.py Fri Jun 05 23:05:11 2009 +0300 @@ -114,6 +114,8 @@ def index (self) : """ Recursively index this Folder, yielding a series of all Folder objects inside. + + XXX: not used """ # and subfolders @@ -123,296 +125,3 @@ for item in subfolder.index_subfolders() : yield item -class Folder (object) : - def __init__ (self, name='.', parent=None) : - # the directory name, no trailing / - self.name = unicode(name.rstrip(os.sep)) - - # our parent Folder, or None - self.parent = parent - - # the path to this dir, as a relative path to the root of the image gallery, always starts with . - if parent and name : - self.path = parent.pathFor(self.name) - else : - self.path = self.name - - # the url-path to the index.html file - self.html_path = self.path - - # dict of fname -> Folder - self.subdirs = {} - - # dict of fname -> Image - self.images = {} - - # our human-friendly title - self.title = None - - # our long-winded description - self.descr = '' - - # is this folder non-empty? - self.alive = None - - # self.images.values(), but sorted by filename - self.sorted_images = [] - - # the ShortURL key to this dir - self.shorturl_code = None - - # were we filtered out? - self.filtered = False - - def pathFor (self, *fnames) : - """ - Return a root-relative path to the given path inside this dir - """ - return os.path.join(self.path, *fnames) - - def index (self, filters=None) : - """ - Look for other dirs and images inside this dir. Filters must be either None, - whereupon all files will be included, or a dict of {filename -> next_filter}. - If given, only filenames that are present in the dict will be indexed, and in - the case of dirs, the next_filter will be passed on to that Folder's index - method. - """ - - if filters : - self.filtered = True - - # iterate through listdir - for fname in os.listdir(self.path) : - # the full filesystem path to it - fpath = self.pathFor(fname) - - # ignore dotfiles - if fname.startswith('.') : - log.debug("Skipping dotfile %s", fname) - continue - - # apply filters - if filters : - if fname in filters : - next_filter = filters[fname] - else : - log.debug("Skip `%s' as we have a filter", fname) - continue - else : - next_filter = None - - # recurse into subdirs, but not thumbs/previews - if (os.path.isdir(fpath) - and (fname not in (settings.THUMB_DIR, settings.PREVIEW_DIR)) - and (self.parent or fname not in settings.ROOT_IGNORE) - ) : - log.down(fname) - - f = Folder(fname, self) - - try : - if f.index(next_filter) : # recursion - # if a subdir is alive, we are alive as well - self.subdirs[fname] = f - self.alive = True - except Exception, e : - log.warning("skip - %s: %s" % (type(e), e)) - - log.up() - - # handle images - elif os.path.isfile(fpath) and utils.isImage(fname) : - log.next(fname) - self.images[fname] = image.Image(self, fname) - - # ignore everything else - else : - log.debug("Ignoring file %s", fname) - - # sort and link the images - if self.images : - self.alive = True - - # sort the images - fnames = self.images.keys() - fnames.sort() - - prev = None - - # link - for fname in fnames : - img = self.images[fname] - - img.prev = prev - - if prev : - prev.next = img - - prev = img - - # add to the sorted images list - self.sorted_images.append(img) - - # figure out our title/ descr. Must be done before our parent dir is rendered (self.title) - title_path = self.pathFor(settings.TITLE_FILE) - - self.title, self.descr = utils.readTitleDescr(title_path) - - # default title for the root dir - if self.title or self.descr : - self.alive = True - pass # use what was in the title file - - elif not self.parent : - self.title = 'Index' - - else : - self.title = self.name - - if not self.alive : - log.debug("Dir %s isn't alive" % self.path) - - return self.alive - - def getObjInfo (self) : - """ - Metadata for shorturls2.db - """ - return 'dir', self.path, '' - - def breadcrumb (self, forImg=None) : - """ - Returns a [(fname, title)] list of this dir's parent dirs - """ - - f = self - b = [] - d = 0 - - while f : - # functionality of the slightly-hacked-in variety - if f is self and forImg is not None : - url = helpers.url_for_page(self.getPageNumber(forImg)) - else : - url = dirUp(d) - - b.insert(0, (url, f.title)) - - d += 1 - f = f.parent - - return b - - def getPageNumber (self, img) : - """ - Get the page number that the given image is on - """ - - return self.sorted_images.index(img) // settings.IMAGE_COUNT - - def countParents (self, acc=0) : - if self.parent : - return self.parent.countParents(acc+1) - else : - return acc - - def inRoot (self, *fnames) : - """ - Return a relative URL from this dir to the given path in the root dir - """ - - c = self.countParents() - - return utils.url_join(*((['..']*c) + list(fnames))) - - def render (self) : - """ - Render the index.html, Images, and recurse into subdirs - """ - - # ded folders are skipped - if not self.alive : - # dead, skip, no output - return - - index_mtime = utils.mtime(self.pathFor("index.html")) - dir_mtime = utils.mtime(self.path) - - # 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 - if self.filtered : - log.warning("Dir `%s' contents were filtered, so we won't render the gallery index again", self.path) - - elif index_mtime > dir_mtime : - # no changes, pass, ignored - pass - - else : - # create the thumb/preview dirs if needed - for dir in (settings.THUMB_DIR, settings.PREVIEW_DIR) : - path = self.pathFor(dir) - - if not os.path.isdir(path) : - log.info("mkdir %s", dir) - os.mkdir(path) - - # sort the subdirs - subdirs = self.subdirs.values() - subdirs.sort(key=lambda d: d.name) - - # paginate! - images = self.sorted_images - image_count = len(images) - pages = [] - - while images : - pages.append(images[:settings.IMAGE_COUNT]) - images = images[settings.IMAGE_COUNT:] - - pagination_required = len(pages) > 1 - - if pagination_required : - log.info("%d pages @ %d images", len(pages), settings.IMAGE_COUNT) - elif not pages : - log.info("no images, render for subdirs") - pages = [[]] - - for cur_page, images in enumerate(pages) : - if pagination_required and cur_page > 0 : - shorturl = "%s/%s" % (self.shorturl_code, cur_page+1) - else : - shorturl = self.shorturl_code - - # render to index.html - gallery_tpl.render_to(self.pathFor(url_for_page(cur_page)), - stylesheet_url = self.inRoot('style.css'), - title = self.title, - breadcrumb = self.breadcrumb(), - - dirs = subdirs, - images = images, - - num_pages = len(pages), - cur_page = cur_page, - - description = self.descr, - - shorturl = self.inRoot('s', shorturl), - shorturl_code = shorturl, - ) - - # render images - image_count = len(self.sorted_images) - for i, img in enumerate(self.images.itervalues()) : - log.next("[%-4d/%4d] %s", i + 1, image_count, img.name) - - img.render() - - # recurse into subdirs - for dir in self.subdirs.itervalues() : - log.down(dir.name) - - dir.render() - - log.up() - diff -r 5b53fe294034 -r 67dd32adf159 degal/image.py --- a/degal/image.py Fri Jun 05 23:03:35 2009 +0300 +++ b/degal/image.py Fri Jun 05 23:05:11 2009 +0300 @@ -104,163 +104,32 @@ return self.metadata - def render_image (self) : + @lazy_load + def thumb (self) : """ - Renders new thumbnails/previews for this image. - - Note: this does not check for the existance of the thumbs/previews subdirs! + Load and update the thumbnail if needed """ - # load origional image - img = self.load_image() - # renderer to use # XXX: get from elsewhere render_machine = self.config.get_renderer() - # lazy-render both thumb and preview - self.thumb = render_machine.render_lazy(self + # render if needed + return render_machine.render_lazy(self self.config.thumb_size, self.parent.load_thumb_dir.subnode(self.name) ) + + @lazy_load + def preview (self) : + """ + Load and update the preview if needed + """ - self.preview = render_machine.render_lazy(self, + # renderer to use + # XXX: get from elsewhere + render_machine = self.config.get_renderer() + + return render_machine.render_lazy(self, self.config.preview_size, self.parent.load_preview_dir.subnode(self.name) ) -class Image (object) : - def __init__ (self, dir, name) : - # the image filename, e.g. DSC3948.JPG - self.name = unicode(name) - - # the Folder object that we are in - self.dir = dir - - # the relative path from the root to us - self.path = dir.pathFor(self.name) - - # the basename+ext, e.g. DSCR3948, .JPG - self.base_name, self.ext = os.path.splitext(self.name) - - # our user-friendly title - self.title = self.name - - # our long-winded description - self.descr = '' - - # the image before and after us, both may be None - self.prev = self.next = None - - # the image-relative names for the html page, thumb and preview images - self.html_name = self.name + ".html" - self.thumb_name = utils.url_join(settings.THUMB_DIR, self.name) - self.preview_name = utils.url_join(settings.PREVIEW_DIR, self.name) - - # the root-relative paths to the html page, thumb and preview images - self.html_path = self.dir.pathFor(self.html_name) - self.thumb_path = self.dir.pathFor(settings.THUMB_DIR, self.name) - self.preview_path = self.dir.pathFor(settings.PREVIEW_DIR, self.name) - - # - # Figured out after prepare - # - - # (w, h) tuple - self.img_size = None - - # the ShortURL code for this image - self.shorturl_code = None - - # EXIF data - self.exif_data = {} - - # what to use in the rendered templates, intended to be overridden by subclasses - self.series_act = "add" - self.series_verb = "Add to" - - def getObjInfo (self) : - """ - Metadata for shorturl2.db - """ - return 'img', self.dir.path, self.name - - def breadcrumb (self) : - """ - Returns a [(fname, title)] list of this image's parents - """ - - return self.dir.breadcrumb(forImg=self) + [(self.html_name, self.title)] - - def render (self) : - """ - Write out the .html file - """ - - # stat the image file to get the filesize and mtime - st = os.stat(self.path) - - self.filesize = st.st_size - self.timestamp = st.st_mtime - - # open the image in PIL to get image attributes + generate thumbnails - img = PIL.Image.open(self.path) - - self.img_size = img.size - - for out_path, geom in ((self.thumb_path, settings.THUMB_GEOM), (self.preview_path, settings.PREVIEW_GEOM)) : - # if it doesn't exist, or it's older than the image itself, generate - if utils.mtime(out_path) < self.timestamp : - log.info("render [%sx%s]", geom[0], geom[1], wait=True) - - # XXX: is this the most efficient way to do this? It seems slow - out_img = img.copy() - out_img.thumbnail(geom, resample=True) - out_img.save(out_path) - - log.done() - - # look for the metadata file - title_path = self.dir.pathFor(self.base_name + '.txt') - - self.title, self.descr = utils.readTitleDescr(title_path) - - if not self.title : - self.title = self.name - - if utils.mtime(self.html_path) < self.timestamp : - log.info("render %s.html", self.name) - - # parse the exif data from the file - try : - self.exif_data = dexif.parse_exif(self.path) - except dexif.ExifError, message: - log.warning("Reading EXIF data for %s failed: %s" % (self.filename, message)) - self.exif_data = {} - - - image_tpl.render_to(self.html_path, - stylesheet_url = self.dir.inRoot('style.css'), - title = self.title, - breadcrumb = self.breadcrumb(), - - prev = self.prev, - next = self.next, - img = self, - - description = self.descr, - - filename = self.name, - img_size = self.img_size, - file_size = self.filesize, - timestamp = self.timestamp, - exif_data = self.exif_data, - - shorturl = self.dir.inRoot('s', self.shorturl_code), - shorturl_code = self.shorturl_code, - - series_url = self.dir.inRoot('series/%s/%s' % (self.series_act, self.shorturl_code)), - series_verb = self.series_verb, - ) - - def __str__ (self) : - return "Image `%s' in `%s'" % (self.name, self.dir.path) -