import os from collections import defaultdict import tempfile from shutil import copyfileobj from contextlib import closing from photoapp.thumb import thumb_path, image_file_style_process class ThumbGenerator(object): def __init__(self, library, storage): self.library = library self.storage = storage self._failed_thumbs_cache = defaultdict(dict) def make_thumb(self, photo, style_name): """ Create a thumbnail of the given photo, scaled/cropped to the given named style :return: local path to thumbnail file or None if creation failed or was blocked """ dest = thumb_path(style_name, photo.uuid) if self.storage.exists(dest): return self.storage.open(dest, "rb") if photo.uuid in self._failed_thumbs_cache[style_name]: return None # if photo.width is None: # todo better detection of images that PIL can't open # return None # TODO have the subprocess download the file # TODO thundering herd with tempfile.TemporaryDirectory() as tmpdir: fthumblocal = tempfile.NamedTemporaryFile(delete=True) fpath = os.path.join(tmpdir, "image") with self.library.storage.open(photo.path, 'rb') as fsrc: with open(fpath, 'wb') as ftmpdest: copyfileobj(fsrc, ftmpdest) if not image_file_style_process(fpath, fthumblocal.name, style_name, photo.orientation): self._failed_thumbs_cache[style_name][photo.uuid] = True # dont retry failed generations return with closing(self.storage.open(dest, 'wb')) as fdest: copyfileobj(fthumblocal, fdest) fthumblocal.seek(0) return fthumblocal