move thumbnail rendering from render.py to thumbnail.py, and implement staleness checking for Images, plus index_images for Folder
authorTero Marttila <terom@fixme.fi>
Thu, 11 Jun 2009 00:36:19 +0300
changeset 85 7da934333469
parent 84 891545a38a2b
child 86 d9a981cc0806
move thumbnail rendering from render.py to thumbnail.py, and implement staleness checking for Images, plus index_images for Folder
degal/filesystem.py
degal/folder.py
degal/image.py
degal/render.py
degal/thumbnail.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.
--- 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)
+