/usr/share/pyshared/quodlibet/util/thumbnails.py is in exfalso 2.3.2-1.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 | # -*- coding: utf-8 -*-
# Copyright 2009 Christoph Reiter
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 as
# published by the Free Software Foundation
import os
import urllib
import tempfile
try:
import hashlib as hash
except ImportError:
import md5 as hash
import gobject
import gtk
from quodlibet.util import mtime, fsnative, fsdecode, pathname2url
from quodlibet.const import USERDIR
def add_border(pixbuf, val, round=False):
"""Add a 1px border to the pixbuf and round of the edges if needed.
val is the border brightness from 0 to 255"""
c = (val << 24) | (val << 16) | (val << 8) | 0xFF
w, h = pixbuf.get_width(), pixbuf.get_height()
newpb = gtk.gdk.Pixbuf(
gtk.gdk.COLORSPACE_RGB, True, 8, w + 2, h + 2)
newpb.fill(c)
pixbuf.copy_area(0, 0, w, h, newpb, 1, 1)
if round:
m = (c & 0xFFFFFF00) | 0xDD
p = (c & 0xFFFFFF00) | 0xBB
o = (c & 0xFFFFFF00) | 0x70
n = (c & 0xFFFFFF00) | 0x40
l = -1
e = -2
mask = (
(0, 0, n, p),
(0, o, m, l),
(n, m, e, e),
(p, l, e, e)
)
overlay = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB, True, 8, 1, 1)
overlay.fill(m)
for y, row in enumerate(mask):
for x, pix in enumerate(row):
for xn, yn in [(x, y), (w+1-x, y), (w+1-x, h+1-y), (x, h+1-y)]:
if pix == l:
overlay.composite(newpb, xn, yn, 1, 1, 0, 0, 1, 1,
gtk.gdk.INTERP_NEAREST, 70)
elif pix != e:
newpb.subpixbuf(xn, yn, 1, 1).fill(pix)
return newpb
def calc_scale_size(boundary, size, scale_up=True):
"""Returns the biggest possible size to fit into the boundary,
respecting the aspect ratio."""
bwidth, bheight = boundary
iwidth, iheight = size
scale_w, scale_h = iwidth, iheight
if iwidth > bwidth or iheight > bheight or scale_up:
bratio = float(bwidth) / bheight
iratio = float(iwidth) / iheight
if iratio > bratio:
scale_w = bwidth
scale_h = int(bwidth / iratio)
else:
scale_w = int(bheight * iratio)
scale_h = bheight
return scale_w, scale_h
def scale(pixbuf, boundary, scale_up=True):
"""Scale a pixbuf so it fits into the boundary.
(preserves image aspect ratio)"""
size = pixbuf.get_width(), pixbuf.get_height()
scale_w, scale_h = calc_scale_size(boundary, size, scale_up)
return pixbuf.scale_simple(scale_w, scale_h, gtk.gdk.INTERP_BILINEAR)
def get_thumbnail(path, boundary):
"""Get a thumbnail of an image. Will create/use a thumbnail in
the user's thumbnail directory if possible. Follows the
Free Desktop specification. http://jens.triq.net/thumbnail-spec/"""
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 gtk.gdk.pixbuf_new_from_file_at_size(path, width, height)
if os.name == "nt":
thumb_folder = os.path.join(USERDIR, "thumbnails")
else:
thumb_folder = os.path.expanduser('~/.thumbnails')
if not os.path.exists(thumb_folder):
os.mkdir(thumb_folder)
os.chmod(thumb_folder, 0700)
if width <= 128 and height <= 128:
size_name = "normal"
thumb_size = 128
else:
size_name = "large"
thumb_size = 256
cache_dir = os.path.join(thumb_folder, size_name)
if not os.path.exists(cache_dir):
os.mkdir(cache_dir)
os.chmod(cache_dir, 0700)
if isinstance(path, str): bytes = fsdecode(path).encode("utf-8")
else: bytes = path.encode("utf-8")
uri = "file://" + pathname2url(bytes)
thumb_name = hash.md5(uri).hexdigest() + ".png"
thumb_path = os.path.join(cache_dir, thumb_name)
pb = meta_mtime = None
if os.path.exists(thumb_path):
pb = gtk.gdk.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 = gtk.gdk.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)
mime = gtk.gdk.pixbuf_get_file_info(path)[0]['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.save(thumb_path, "png", options)
os.chmod(thumb_path, 0600)
return scale(pb, boundary)
|