def thumbnails_for_file(relative_source_path, root=None, basedir=None, subdir=None, prefix=None): """ Return a list of dictionaries, one for each thumbnail belonging to the source image. The following list explains each key of the dictionary: `filename` -- absolute thumbnail path `x` and `y` -- the size of the thumbnail `options` -- list of options for this thumbnail `quality` -- quality setting for this thumbnail """ if root is None: root = settings.MEDIA_ROOT if prefix is None: prefix = get_setting('PREFIX') if subdir is None: subdir = get_setting('SUBDIR') if basedir is None: basedir = get_setting('BASEDIR') source_dir, filename = os.path.split(relative_source_path) thumbs_path = os.path.join(root, basedir, source_dir, subdir) if not os.path.isdir(thumbs_path): return [] files = all_thumbnails(thumbs_path, recursive=False, prefix=prefix, subdir='') return files.get(filename, [])
def all_thumbnails(path, recursive=True, prefix=None, subdir=None): """ Return a dictionary referencing all files which match the thumbnail format. Each key is a source image filename, relative to path. Each value is a list of dictionaries as explained in `thumbnails_for_file`. """ if prefix is None: prefix = get_setting('PREFIX') if subdir is None: subdir = get_setting('SUBDIR') thumbnail_files = {} if not path.endswith('/'): path = '%s/' % path len_path = len(path) if recursive: all = os.walk(path) else: files = [] for file in os.listdir(path): if os.path.isfile(os.path.join(path, file)): files.append(file) all = [(path, [], files)] for dir_, subdirs, files in all: rel_dir = dir_[len_path:] for file in files: thumb = re_thumbnail_file.match(file) if not thumb: continue d = thumb.groupdict() source_filename = d.pop('source_filename') if prefix: source_path, source_filename = os.path.split(source_filename) if not source_filename.startswith(prefix): continue source_filename = os.path.join(source_path, source_filename[len(prefix):]) d['options'] = d['options'] and d['options'].split('_') or [] if subdir and rel_dir.endswith(subdir): rel_dir = rel_dir[:-len(subdir)] # Corner-case bug: if the filename didn't have an extension but did # have an underscore, the last underscore will get converted to a # '.'. m = re.match(r'(.*)_(.*)', source_filename) if m: source_filename = '%s.%s' % m.groups() filename = os.path.join(rel_dir, source_filename) thumbnail_file = thumbnail_files.setdefault(filename, []) d['filename'] = os.path.join(dir_, file) thumbnail_file.append(d) return thumbnail_files
def render(self, context): # Note that this isn't a global constant because we need to change the # value for tests. raise_errors = utils.get_setting('DEBUG') # Get the source file. try: source = self.source_var.resolve(context) except VariableDoesNotExist: if raise_errors: raise VariableDoesNotExist("Variable '%s' does not exist." % self.source_var) return self.bail_out(context) if not source: if raise_errors: raise TemplateSyntaxError( "Variable '%s' is an invalid source." % self.source_var ) return self.bail_out(context) # Resolve the thumbnail option values. try: opts = {} for key, value in self.opts.iteritems(): if hasattr(value, 'resolve'): value = value.resolve(context) opts[str(key)] = value except Exception: if raise_errors: raise return self.bail_out(context) # Size variable can be either a tuple/list of two integers or a # valid string. size = opts['size'] if isinstance(size, basestring): m = RE_SIZE.match(size) if m: opts['size'] = (int(m.group(1)), int(m.group(2))) else: if raise_errors: raise TemplateSyntaxError("%r is not a valid size." % size) return self.bail_out(context) # Ensure the quality is an integer. if 'quality' in opts: try: opts['quality'] = int(opts['quality']) except (TypeError, ValueError): if raise_errors: raise TemplateSyntaxError("%r is an invalid quality." % opts['quality']) return self.bail_out(context) try: thumbnail = get_thumbnailer(source).get_thumbnail(opts) except Exception, e: if raise_errors: raise TemplateSyntaxError(u"Couldn't get the thumbnail %s: %s" % (source, e)) return self.bail_out(context)
def render(self, context): # Note that this isn't a global constant because we need to change the # value for tests. raise_errors = utils.get_setting('DEBUG') # Get the source file. try: source = self.source_var.resolve(context) except VariableDoesNotExist: if raise_errors: raise VariableDoesNotExist("Variable '%s' does not exist." % self.source_var) return self.bail_out(context) # Resolve the thumbnail option values. try: opts = {} for key, value in self.opts.iteritems(): if hasattr(value, 'resolve'): value = value.resolve(context) opts[str(key)] = value except: if raise_errors: raise return self.bail_out(context) # Size variable can be either a tuple/list of two integers or a # valid string, only the string is checked. size = opts['size'] if isinstance(size, basestring): m = RE_SIZE.match(size) if m: opts['size'] = (int(m.group(1)), int(m.group(2))) else: if raise_errors: raise TemplateSyntaxError("Variable '%s' was resolved " "but '%s' is not a valid size." % (self.size_var, size)) return self.bail_out(context) try: thumbnail = get_thumbnailer(source).get_thumbnail(opts) except: if raise_errors: raise return self.bail_out(context) # Return the thumbnail file url, or put the file on the context. if self.context_name is None: return escape(thumbnail.url) else: context[self.context_name] = thumbnail return ''
from django.core.files.base import File, ContentFile from django.core.files.storage import get_storage_class, default_storage, \ Storage from django.db.models.fields.files import ImageFieldFile, FieldFile from django.utils.html import escape from django.utils.safestring import mark_safe from easy_thumbnails import engine, models, utils import datetime import os from django.utils.http import urlquote DEFAULT_THUMBNAIL_STORAGE = get_storage_class( utils.get_setting('DEFAULT_STORAGE'))() def get_thumbnailer(object, relative_name=None): """ Get a :class:`Thumbnailer` for a source file. The ``object`` argument is usually either one of the following: * ``FieldFile`` instance (i.e. a model instance file/image field property). * ``File`` or ``Storage`` instance, and for both of these cases the ``relative_name`` argument must also be provided * A string, which will be used as the relative name (the source will be set to the default storage) For rarer needed cases, it can also be one of the following:
try: from PIL import Image except ImportError: import Image from easy_thumbnails import utils import os try: from cStringIO import StringIO except ImportError: from StringIO import StringIO DEFAULT_PROCESSORS = [ utils.dynamic_import(p) for p in utils.get_setting('PROCESSORS') ] SOURCE_GENERATORS = [ utils.dynamic_import(p) for p in utils.get_setting('SOURCE_GENERATORS') ] def process_image(source, processor_options, processors=None): """ Process a source PIL image through a series of image processors, returning the (potentially) altered image. """ if processors is None: processors = DEFAULT_PROCESSORS image = source for processor in processors: image = processor(image, **processor_options) return image
try: from PIL import Image except ImportError: import Image from easy_thumbnails import utils import os try: from cStringIO import StringIO except ImportError: from StringIO import StringIO DEFAULT_PROCESSORS = [utils.dynamic_import(p) for p in utils.get_setting('PROCESSORS')] def process_image(source, processor_options, processors=None): """ Process a source PIL image through a series of image processors, returning the (potentially) altered image. """ if processors is None: processors = DEFAULT_PROCESSORS image = source for processor in processors: image = processor(image, **processor_options) return image def save_image(image, destination=None, filename=None, **options):
try: from PIL import Image except ImportError: import Image from easy_thumbnails import utils import os try: from cStringIO import StringIO except ImportError: from StringIO import StringIO DEFAULT_PROCESSORS = [utils.dynamic_import(p) for p in utils.get_setting("PROCESSORS")] SOURCE_GENERATORS = [utils.dynamic_import(p) for p in utils.get_setting("SOURCE_GENERATORS")] def process_image(source, processor_options, processors=None): """ Process a source PIL image through a series of image processors, returning the (potentially) altered image. """ if processors is None: processors = DEFAULT_PROCESSORS image = source for processor in processors: image = processor(image, **processor_options) return image
try: from PIL import Image except ImportError: import Image from easy_thumbnails import utils import os try: from cStringIO import StringIO except ImportError: from StringIO import StringIO DEFAULT_PROCESSORS = [utils.dynamic_import(p) for p in utils.get_setting('PROCESSORS')] SOURCE_GENERATORS = [utils.dynamic_import(p) for p in utils.get_setting('SOURCE_GENERATORS')] def process_image(source, processor_options, processors=None): """ Process a source PIL image through a series of image processors, returning the (potentially) altered image. """ if processors is None: processors = DEFAULT_PROCESSORS image = source for processor in processors: image = processor(image, **processor_options) return image
def __init__(self, location=None, base_url=None, *args, **kwargs): location = utils.get_setting('MEDIA_ROOT', override=location) or None base_url = utils.get_setting('MEDIA_URL', override=base_url) or None super(ThumbnailFileSystemStorage, self).__init__(location, base_url, *args, **kwargs)
def get_thumbnail_path(path): basedir = get_setting('BASEDIR') subdir = get_setting('SUBDIR') return os.path.join(basedir, path, subdir)
import os import re from django.db import models from django.conf import settings from django.core.management.base import NoArgsCommand from easy_thumbnails.utils import get_setting try: set except NameError: from sets import Set as set # For Python 2.3 thumb_re = re.compile(r'^%s(.*)_\d{1,}x\d{1,}_[-\w]*q([1-9]\d?|100)\.jpg' % get_setting('PREFIX')) def get_thumbnail_path(path): basedir = get_setting('BASEDIR') subdir = get_setting('SUBDIR') return os.path.join(basedir, path, subdir) def clean_up(): paths = set() for app in models.get_apps(): model_list = models.get_models(app) for model in model_list: for field in model._meta.fields: if isinstance(field, models.ImageField): #TODO: take care of date formatted and callable upload_to. if (not callable(field.upload_to)
import os import re from django.db import models from django.conf import settings from django.core.management.base import NoArgsCommand from easy_thumbnails.utils import get_setting try: set except NameError: from sets import Set as set # For Python 2.3 thumb_re = re.compile(r'^%s(.*)\.\d{1,}x\d{1,}_[-\w]*q([1-9]\d?|100)\.jpg' % get_setting('PREFIX')) def get_thumbnail_path(path): basedir = get_setting('BASEDIR') subdir = get_setting('SUBDIR') return os.path.join(basedir, path, subdir) def clean_up(): paths = set() for app in models.get_apps(): model_list = models.get_models(app) for model in model_list: for field in model._meta.fields: if isinstance(field, models.ImageField): #TODO: take care of date formatted and callable upload_to. if (not callable(field.upload_to) and
from django.core.files.base import File, ContentFile from django.core.files.storage import get_storage_class, default_storage, Storage from django.db.models.fields.files import ImageFieldFile, FieldFile from django.utils.html import escape from django.utils.safestring import mark_safe from easy_thumbnails import engine, models, utils import datetime import os from django.utils.http import urlquote DEFAULT_THUMBNAIL_STORAGE = get_storage_class(utils.get_setting("DEFAULT_STORAGE"))() def get_thumbnailer(source, relative_name=None): """ Get a thumbnailer for a source file. The ``source`` argument must be one of the following: * ``Thumbnailer`` instance * ``FieldFile`` instance (i.e. a model instance file/image field property) * ``File`` or ``Storage`` instance, and for both of these cases the ``relative_name`` argument must also be provided * A string, which will be used as the relative name (the source will be set to the default storage)
class Thumbnailer(File): """ A file-like object which provides some methods to generate thumbnail images. You can subclass this object and override the following properties to change the defaults (pulled from the default settings): * thumbnail_basedir * thumbnail_subdir * thumbnail_prefix * thumbnail_quality * thumbnail_extension * source_generators * thumbnail_processors """ thumbnail_basedir = utils.get_setting('BASEDIR') thumbnail_subdir = utils.get_setting('SUBDIR') thumbnail_prefix = utils.get_setting('PREFIX') thumbnail_quality = utils.get_setting('QUALITY') thumbnail_extension = utils.get_setting('EXTENSION') thumbnail_transparency_extension = utils.get_setting( 'TRANSPARENCY_EXTENSION') source_generators = None thumbnail_processors = None def __init__(self, file, name=None, source_storage=None, thumbnail_storage=None, *args, **kwargs): super(Thumbnailer, self).__init__(file, name, *args, **kwargs) self.source_storage = source_storage or default_storage self.thumbnail_storage = (thumbnail_storage or DEFAULT_THUMBNAIL_STORAGE) def generate_thumbnail(self, thumbnail_options): """ Return an unsaved ``ThumbnailFile`` containing a thumbnail image. The thumbnail image is generated using the ``thumbnail_options`` dictionary. """ image = engine.generate_source_image(self, thumbnail_options, self.source_generators) if image is None: raise exceptions.InvalidImageFormatError( "The source file does not appear to be an image") thumbnail_image = engine.process_image(image, thumbnail_options, self.thumbnail_processors) quality = thumbnail_options.get('quality', self.thumbnail_quality) filename = self.get_thumbnail_name(thumbnail_options, transparent=self.is_transparent(thumbnail_image)) data = engine.save_image(thumbnail_image, filename=filename, quality=quality).read() thumbnail = ThumbnailFile(filename, ContentFile(data), storage=self.thumbnail_storage) thumbnail.image = thumbnail_image thumbnail._committed = False return thumbnail def get_thumbnail_name(self, thumbnail_options, transparent=False): """ Return a thumbnail filename for the given ``thumbnail_options`` dictionary and ``source_name`` (which defaults to the File's ``name`` if not provided). """ path, source_filename = os.path.split(self.name) source_extension = os.path.splitext(source_filename)[1][1:] filename = '%s%s' % (self.thumbnail_prefix, source_filename) if transparent: extension = self.thumbnail_transparency_extension else: extension = self.thumbnail_extension extension = extension or 'jpg' thumbnail_options = thumbnail_options.copy() size = tuple(thumbnail_options.pop('size')) quality = thumbnail_options.pop('quality', self.thumbnail_quality) initial_opts = ['%sx%s' % size, 'q%s' % quality] opts = thumbnail_options.items() opts.sort() # Sort the options so the file name is consistent. opts = ['%s' % (v is not True and '%s-%s' % (k, v) or k) for k, v in opts if v] all_opts = '_'.join(initial_opts + opts) data = {'opts': all_opts} basedir = self.thumbnail_basedir % data subdir = self.thumbnail_subdir % data filename_parts = [filename] if ('%(opts)s' in self.thumbnail_basedir or '%(opts)s' in self.thumbnail_subdir): if extension != source_extension: filename_parts.append(extension) else: filename_parts += [all_opts, extension] filename = '.'.join(filename_parts) return os.path.join(basedir, path, subdir, filename) def get_thumbnail(self, thumbnail_options, save=True): """ Return a ``ThumbnailFile`` containing a thumbnail. It the file already exists, it will simply be returned. Otherwise a new thumbnail image is generated using the ``thumbnail_options`` dictionary. If the ``save`` argument is ``True`` (default), the generated thumbnail will be saved too. """ opaque_name = self.get_thumbnail_name(thumbnail_options, transparent=False) transparent_name = self.get_thumbnail_name(thumbnail_options, transparent=True) if opaque_name == transparent_name: names = (opaque_name,) else: names = (opaque_name, transparent_name) for filename in names: if self.thumbnail_exists(filename): thumbnail = ThumbnailFile(name=filename, storage=self.thumbnail_storage) return thumbnail thumbnail = self.generate_thumbnail(thumbnail_options) if save: save_thumbnail(thumbnail, self.thumbnail_storage) # Ensure the right thumbnail name is used based on the transparency # of the image. filename = (self.is_transparent(thumbnail.image) and transparent_name or opaque_name) self.get_thumbnail_cache(filename, create=True, update=True) return thumbnail def thumbnail_exists(self, thumbnail_name): """ Calculate whether the thumbnail already exists and that the source is not newer than the thumbnail. If both the source and thumbnail file storages are local, their file modification times are used. Otherwise the database cached modification times are used. """ # Try to use the local file modification times first. source_modtime = self.get_source_modtime() thumbnail_modtime = self.get_thumbnail_modtime(thumbnail_name) # The thumbnail modification time will be 0 if there was an OSError, # in which case it will still be used (but always return False). if source_modtime and thumbnail_modtime is not None: return thumbnail_modtime and source_modtime <= thumbnail_modtime # Fall back to using the database cached modification times. source = self.get_source_cache() if not source: return False thumbnail = self.get_thumbnail_cache(thumbnail_name) return thumbnail and source.modified <= thumbnail.modified def get_source_cache(self, create=False, update=False): modtime = self.get_source_modtime() update_modified = modtime and datetime.datetime.fromtimestamp(modtime) if update: update_modified = update_modified or datetime.datetime.utcnow() return models.Source.objects.get_file( create=create, update_modified=update_modified, storage=self.source_storage, name=self.name) def get_thumbnail_cache(self, thumbnail_name, create=False, update=False): modtime = self.get_thumbnail_modtime(thumbnail_name) update_modified = modtime and datetime.datetime.fromtimestamp(modtime) if update: update_modified = update_modified or datetime.datetime.utcnow() source = self.get_source_cache(create=True) return models.Thumbnail.objects.get_file( create=create, update_modified=update_modified, storage=self.thumbnail_storage, source=source, name=thumbnail_name) def get_source_modtime(self): try: path = self.source_storage.path(self.name) return os.path.getmtime(path) except OSError: return 0 except NotImplementedError: return None def get_thumbnail_modtime(self, thumbnail_name): try: path = self.thumbnail_storage.path(thumbnail_name) return os.path.getmtime(path) except OSError: return 0 except NotImplementedError: return None def is_transparent(self, image): return (image.mode == 'RGBA' or (image.mode == 'P' and 'transparency' in image.info))
def render(self, context): # Note that this isn't a global constant because we need to change the # value for tests. raise_errors = utils.get_setting('DEBUG') # Get the source file. try: source = self.source_var.resolve(context) except VariableDoesNotExist: if raise_errors: raise VariableDoesNotExist("Variable '%s' does not exist." % self.source_var) return self.bail_out(context) if not source: if raise_errors: raise TemplateSyntaxError( "Variable '%s' is an invalid source." % self.source_var ) return self.bail_out(context) # Resolve the thumbnail option values. try: opts = {} for key, value in self.opts.iteritems(): if hasattr(value, 'resolve'): value = value.resolve(context) opts[str(key)] = value except Exception: if raise_errors: raise return self.bail_out(context) # Size variable can be either a tuple/list of two integers or a # valid string. size = opts['size'] if isinstance(size, basestring): m = RE_SIZE.match(size) if m: opts['size'] = (int(m.group(1)), int(m.group(2))) else: if raise_errors: raise TemplateSyntaxError("%r is not a valid size." % size) return self.bail_out(context) # Ensure the quality is an integer. if 'quality' in opts: try: opts['quality'] = int(opts['quality']) except (TypeError, ValueError): if raise_errors: raise TemplateSyntaxError("%r is an invalid quality." % opts['quality']) return self.bail_out(context) # first try to get the thumbnail url from cache, if it is available # just return it otherwise create the thumb if isinstance(source, basestring): relname = source elif isinstance(source, FieldFile): relname = source.name else: relname = str(source) opt_str = "" for key in opts.keys(): opt_str += "{0}_{1}".format(key, opts[key]) cache_key = "{0}{1}".format(relname, opt_str) cache_key = escape(cache_key).replace(" ", "_").replace("(", "").\ replace(")", "").replace(",","") cache_key = str(hash(cache_key)) thumb_url = False if thumb_url: if not self.context_name: return thumb_url # if the thumb is not cached create one and cache it try: thumbnail = get_thumbnailer(source).get_thumbnail(opts) except Exception: if raise_errors: raise result = self.bail_out(context) #cache.set(cache_key, result, TIMEOUT_CACHE) return result # Return the thumbnail file url, or put the file on the context. if self.context_name is None: result = escape(thumbnail.url) cache.set(cache_key, result, TIMEOUT_CACHE) return result else: context[self.context_name] = thumbnail return ''
from PIL import Image from django.core.files.base import File, ContentFile from django.core.files.storage import get_storage_class, default_storage, \ Storage from django.db.models.fields.files import ImageFieldFile, FieldFile from django.utils.html import escape from django.utils.safestring import mark_safe from easy_thumbnails import engine, models, utils import datetime import os from django.utils.http import urlquote DEFAULT_THUMBNAIL_STORAGE = get_storage_class( utils.get_setting('DEFAULT_STORAGE'))() def get_thumbnailer(source, relative_name=None): """ Get a thumbnailer for a source file. The ``source`` argument must be one of the following: * ``Thumbnailer`` instance * ``FieldFile`` instance (i.e. a model instance file/image field property) * ``File`` or ``Storage`` instance, and for both of these cases the ``relative_name`` argument must also be provided
def render(self, context): # Note that this isn't a global constant because we need to change the # value for tests. raise_errors = utils.get_setting('DEBUG') # Get the source file. try: source = self.source_var.resolve(context) except VariableDoesNotExist: if raise_errors: raise VariableDoesNotExist("Variable '%s' does not exist." % self.source_var) return self.bail_out(context) if not source: if raise_errors: raise TemplateSyntaxError( "Variable '%s' is an invalid source." % self.source_var ) return self.bail_out(context) # Resolve the thumbnail option values. try: opts = {} for key, value in self.opts.iteritems(): if hasattr(value, 'resolve'): value = value.resolve(context) opts[str(key)] = value except Exception: if raise_errors: raise return self.bail_out(context) # Size variable can be either a tuple/list of two integers or a # valid string. size = opts['size'] if isinstance(size, basestring): m = RE_SIZE.match(size) if m: opts['size'] = (int(m.group(1)), int(m.group(2))) else: if raise_errors: raise TemplateSyntaxError("%r is not a valid size." % size) return self.bail_out(context) # Ensure the quality is an integer. if 'quality' in opts: try: opts['quality'] = int(opts['quality']) except (TypeError, ValueError): if raise_errors: raise TemplateSyntaxError("%r is an invalid quality." % opts['quality']) return self.bail_out(context) try: thumbnail = get_thumbnailer(source).get_thumbnail(opts) except Exception: if raise_errors: raise return self.bail_out(context) # Return the thumbnail file url, or put the file on the context. if self.context_name is None: return escape(thumbnail.url) else: context[self.context_name] = thumbnail return ''