move thumbnail rendering from render.py to thumbnail.py, and implement staleness checking for Images, plus index_images for Folder
--- 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.
--- 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
+
--- 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)
- )
-
--- 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
--- /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)
+