def get_cache_info(path, boundary): """For an image at `path` return (cache_path, thumb_size) cache_path points to a potential cache file thumb size is either 128 or 256 """ assert is_fsnative(path) width, height = boundary if width <= ThumbSize.NORMAL and height <= ThumbSize.NORMAL: size_name = "normal" thumb_size = ThumbSize.NORMAL else: size_name = "large" thumb_size = ThumbSize.LARGE thumb_folder = get_thumbnail_folder() cache_dir = os.path.join(thumb_folder, size_name) uri = "file://" + pathname2url(path) thumb_name = hashlib.md5(uri).hexdigest() + ".png" thumb_path = os.path.join(cache_dir, thumb_name) return (thumb_path, thumb_size)
def test_thumb(s): thumb = thumbnails.get_thumbnail(s.filename, (50, 60)) #check for right scaling s.failUnless(thumb) s.failUnlessEqual((thumb.get_width(), thumb.get_height()), (50, 3)) #test the thumbnail filename uri = "file://" + pathname2url(s.filename) name = hash.md5(uri).hexdigest() + ".png" path = thumbnails.get_thumbnail_folder() path = os.path.join(path, "normal", name) s.failUnless(os.path.isfile(path)) #check for metadata thumb_pb = GdkPixbuf.Pixbuf.new_from_file(path) meta_mtime = thumb_pb.get_option("tEXt::Thumb::MTime") meta_uri = thumb_pb.get_option("tEXt::Thumb::URI") s.failUnlessEqual(int(meta_mtime), int(mtime(s.filename))) s.failUnlessEqual(meta_uri, uri) #check rights if os.name != "nt": s.failUnlessEqual(os.stat(path).st_mode, 33152)
def get_thumbnail(path, boundary): """Get a thumbnail pixbuf of an image at `path`. Will create/use a thumbnail in the user's thumbnail directory if possible. Follows the Free Desktop specification: http://specifications.freedesktop.org/thumbnail-spec/ Can raise GLib.GError. Thread-safe. """ width, height = boundary new_from_file_at_size = GdkPixbuf.Pixbuf.new_from_file_at_size # larger than thumbnails, load directly if width > ThumbSize.LARGEST or height > ThumbSize.LARGEST: return new_from_file_at_size(path, width, height) path_mtime = mtime(path) if path_mtime == 0: return new_from_file_at_size(path, width, height) # embedded thumbnails come from /tmp/ # FIXME: move this to another layer if path.startswith(tempfile.gettempdir()): return new_from_file_at_size(path, width, height) thumb_path, thumb_size = get_cache_info(path, boundary) cache_dir = os.path.dirname(thumb_path) try: mkdir(cache_dir, 0o700) except OSError: return new_from_file_at_size(path, width, height) try: pb = new_from_file_at_size(thumb_path, width, height) except GLib.GError: # in case it fails to load, we recreate it pass else: meta_mtime = pb.get_option("tEXt::Thumb::MTime") if meta_mtime is not None: try: meta_mtime = int(meta_mtime) except ValueError: pass else: if meta_mtime == int(path_mtime): return pb info, pw, ph = GdkPixbuf.Pixbuf.get_file_info(path) # Too small picture, no thumbnail needed if pw < thumb_size and ph < thumb_size: return new_from_file_at_size(path, width, height) thumb_pb = new_from_file_at_size(path, thumb_size, thumb_size) uri = "file://" + pathname2url(path) mime = info.get_mime_types()[0] options = { "tEXt::Thumb::Image::Width": str(pw), "tEXt::Thumb::Image::Height": str(ph), "tEXt::Thumb::URI": uri, "tEXt::Thumb::MTime": str(int(path_mtime)), "tEXt::Thumb::Size": str(os.path.getsize(path)), "tEXt::Thumb::Mimetype": mime, "tEXt::Software": "QuodLibet" } thumb_pb.savev(thumb_path, "png", options.keys(), options.values()) try: os.chmod(thumb_path, 0o600) except OSError: pass return scale(thumb_pb, boundary)
def frompath(klass, value): """Construct a URI from an unescaped filename.""" return klass("file://" + pathname2url(value), escaped=True)
def get_thumbnail(path, boundary): """Get a thumbnail pixbuf of an image at `path`. Will create/use a thumbnail in the user's thumbnail directory if possible. Follows the Free Desktop specification: http://specifications.freedesktop.org/thumbnail-spec/ Can raise GLib.GError. """ width, height = boundary # embedded thumbnails come from /tmp/ # and too big thumbnails make no sense if path.startswith(tempfile.gettempdir()) or \ width > 256 or height > 256 or mtime(path) == 0: return GdkPixbuf.Pixbuf.new_from_file_at_size(path, width, height) if width <= 128 and height <= 128: size_name = "normal" thumb_size = 128 else: size_name = "large" thumb_size = 256 thumb_folder = get_thumbnail_folder() cache_dir = os.path.join(thumb_folder, size_name) try: mkdir(cache_dir, 0700) except OSError: return GdkPixbuf.Pixbuf.new_from_file_at_size(path, width, height) bytes_ = path if isinstance(path, unicode): bytes_ = path.encode("utf-8") uri = "file://" + pathname2url(bytes_) thumb_name = hashlib.md5(uri).hexdigest() + ".png" thumb_path = os.path.join(cache_dir, thumb_name) pb = meta_mtime = None if os.path.exists(thumb_path): pb = GdkPixbuf.Pixbuf.new_from_file(thumb_path) meta_mtime = pb.get_option("tEXt::Thumb::MTime") meta_mtime = meta_mtime and int(meta_mtime) if not pb or meta_mtime != int(mtime(path)): pb = GdkPixbuf.Pixbuf.new_from_file(path) #Too small picture, no thumbnail needed if pb.get_width() < thumb_size and pb.get_height() < thumb_size: return scale(pb, boundary) info = GdkPixbuf.Pixbuf.get_file_info(path)[0] mime = info.get_mime_types()[0] options = { "tEXt::Thumb::Image::Width": str(pb.get_width()), "tEXt::Thumb::Image::Height": str(pb.get_height()), "tEXt::Thumb::URI": uri, "tEXt::Thumb::MTime": str(int(mtime(path))), "tEXt::Thumb::Size": str(os.path.getsize(fsnative(path))), "tEXt::Thumb::Mimetype": mime, "tEXt::Software": "QuodLibet" } pb = scale(pb, (thumb_size, thumb_size)) pb.savev(thumb_path, "png", options.keys(), options.values()) try: os.chmod(thumb_path, 0600) except OSError: pass return scale(pb, boundary)