# HG changeset patch # User Tero Marttila # Date 1244669779 -10800 # Node ID 7da934333469dc5ad20c57efe6a2e31eb562ed09 # Parent 891545a38a2bbf73e6a9790f652569851448cf34 move thumbnail rendering from render.py to thumbnail.py, and implement staleness checking for Images, plus index_images for Folder diff -r 891545a38a2b -r 7da934333469 degal/filesystem.py --- a/degal/filesystem.py Wed Jun 10 23:33:28 2009 +0300 +++ b/degal/filesystem.py Thu Jun 11 00:36:19 2009 +0300 @@ -531,6 +531,33 @@ # perform the copy shutil.copyfile(file.path, self.path) + def newer_than (self, file) : + """ + Tests if this file is newer than the given file. + + Returns True if it is, False if it isn't, or None if either file doesn't exist. + + XXX: stat cache? + """ + + stat_self = self.stat(soft=True) + stat_file = file.stat(soft=True) + + if stat_self and stat_file : + return stat_self.st_mtime > stat_file.st_mtime + + else : + return None + + def older_than (self, file) : + """ + Tests if this file is older than the given file. + + Opposite of newer_than. + """ + + return file.newer_than(self) + class Directory (Node) : """ A directory is a node that contains other nodes. diff -r 891545a38a2b -r 7da934333469 degal/folder.py --- a/degal/folder.py Wed Jun 10 23:33:28 2009 +0300 +++ b/degal/folder.py Thu Jun 11 00:36:19 2009 +0300 @@ -19,7 +19,7 @@ # info self.title = None self.description = None - + @lazy_load def preview_dir (self) : """ @@ -114,17 +114,17 @@ else : return self.subfile("index.html") - def index (self) : + def index_images (self, for_update=True) : """ - Recursively index this Folder, yielding a series of all Folder objects inside. + Return a series of Images inside of this folder. - XXX: not used + If `for_update` is given, only images that are stale will be returned. """ - # and subfolders - for subfolder in self.subfolders : - yield subfolder + for image in self.images : + if for_update and not image.stale() : + # skip + continue - for item in subfolder.index_subfolders() : - yield item - + yield image + diff -r 891545a38a2b -r 7da934333469 degal/image.py --- a/degal/image.py Wed Jun 10 23:33:28 2009 +0300 +++ b/degal/image.py Thu Jun 11 00:36:19 2009 +0300 @@ -4,7 +4,7 @@ from __future__ import with_statement -import filesystem, render, html, format +import filesystem, format, thumbnail from utils import lazy_load import PIL.Image @@ -29,20 +29,16 @@ # the .html file for this image self.html = self.parent.subfile(self.basename + '.html') + # our preview/thumbnail + self.preview = thumbnail.Thumbnail(self, self.parent.preview_dir, self.config.preview_size) + self.thumb = thumbnail.Thumbnail(self, self.parent.thumb_dir, self.config.thumb_size) + # info self.title = self.name self.description = None @lazy_load - def stat (self) : - """ - Load and return the os.stat info for this file - """ - - return super(Image, self).stat() - - @lazy_load - def image (self) : + def pil_image (self) : """ Loads the image as a PIL.Image """ @@ -77,11 +73,11 @@ """ # load stuff - stat = self.stat + stat = self.stat() exif = self.exif # XXX: avoid having to open the image? - size = self.image.size + size = self.pil_image.size # build return dict({ @@ -93,32 +89,21 @@ (name, exif[tag]) for tag, name in self.config.exif_tags if exif and tag in exif )) - @lazy_load - def thumb (self) : + def stale (self) : """ - Load and update the thumbnail if needed + Tests if this Image is stale, based on preview/thumb. """ - # renderer to use - # XXX: get from elsewhere - render_machine = self.config.get_renderer() - - # render if needed - return render_machine.render_lazy(self, - self.config.thumb_size, self.parent.thumb_dir.subnode(self.name) - ) + return self.preview.stale() or self.thumb.stale() - @lazy_load - def preview (self) : + def update (self) : """ - Load and update the preview if needed + Updates this Image's thumb/preview """ + + if self.preview.stale() : + self.preview.update() - # renderer to use - # XXX: get from elsewhere - render_machine = self.config.get_renderer() + if self.thumb.stale() : + self.thumb.update() - return render_machine.render_lazy(self, - self.config.preview_size, self.parent.preview_dir.subnode(self.name) - ) - diff -r 891545a38a2b -r 7da934333469 degal/render.py --- a/degal/render.py Wed Jun 10 23:33:28 2009 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,57 +0,0 @@ -""" - Rendering images as thumbnails/previews. -""" - -import PIL.Image - -class RenderMachine (object) : - """ - RAWR! I'm the render machine! - - TODO: use multithreaded rendering (PIL supports it) - """ - - def __init__ (self, config) : - """ - Use the given Configuration object's settings for rendering - """ - - self.config = config - - def render_out (self, image, size, file) : - """ - Creates a thumbnail from the given Image of the given `size`, and saves it at `out`. - - Returns the file for convenience - """ - - # load the PIL.Image - img = image.image - - # we need to create a copy, as .thumbnail mutates the Image - img_out = img.copy() - - # then resample to given size - img_out.thumbnail(size, resample=True) - - # and write out - img_out.save(file.path) - - return file - - def render_lazy (self, image, size, out) : - """ - Renders the given image with render_out if `out` does not yet exist or is older than image. - """ - - # stat the output file - out_stat = out.stat(soft=True) - - # compare - if not out_stat or out_stat.st_mtime < image.stat.st_mtime : - # render anew - return self.render_out(image, size, out) - - else : - # already rendered - return out diff -r 891545a38a2b -r 7da934333469 degal/thumbnail.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/degal/thumbnail.py Thu Jun 11 00:36:19 2009 +0300 @@ -0,0 +1,50 @@ +""" + State for thumbnails; derivates of Images +""" + +import filesystem + +import PIL.Image + +class Thumbnail (filesystem.File) : + """ + A Thumbnail is a derivate of an Image, usually resized to some other size. + """ + + def __init__ (self, image, subdir, size) : + """ + Initialize to link against the given `image`. + + `subdir` specifies the directory to store this thumbnail in. + `size` determines the target resolution of the output thumbnail. + """ + + # our file path + # XXX: this should be the binary fsname, not name + super(Thumbnail, self).__init__(subdir.subnode(image.name)) + + # store + self.image = image + self.size = size + + def stale (self) : + """ + Tests if this thumbnail is stale + """ + + return self.older_than(self.image) + + def update (self) : + """ + Render new output thumbnail + """ + + # load a copy of the PIL.Image, as .thumbnail mutates it + thumb = self.image.pil_image.copy() + + # resample to given size + thumb.thumbnail(self.size, resample=True) + + # and write out + thumb.save(self.path) +