# Path: gallery/imgutil.py
# def make_thumbnail(image_name, thumb_name, preset,
#                    image_storage, thumb_storage):
# 
#     options = getattr(settings, 'GALLERY_RESIZE_OPTIONS', {})
#     presets = getattr(settings, 'GALLERY_RESIZE_PRESETS', {})
# 
#     # Load the image
#     image = Image.open(image_storage.open(image_name))
#     format = image.format
# 
#     if format == 'JPEG':
#         # Auto-rotate JPEG files based on EXIF information
#         try:                                                # pragma: no cover
#             # Use of an undocumented API — let's catch exceptions liberally
#             orientation = image._getexif()[274]
#             image = exif_rotations[orientation](image)
#         except Exception:
#             pass
# 
#         # Increase Pillow's buffer from 64k to 4MB to avoid JPEG save errors
#         ImageFile.MAXBLOCK = 4194304
# 
#     # Pre-crop if requested and the aspect ratios don't match exactly
#     image_width, image_height = image.size
#     thumb_width, thumb_height, crop = presets[preset]
#     if crop:
#         if thumb_width * image_height > image_width * thumb_height:
#             target_height = image_width * thumb_height // thumb_width
#             top = (image_height - target_height) // 2
#             image = image.crop((0, top, image_width, top + target_height))
#         elif thumb_width * image_height < image_width * thumb_height:
#             target_width = image_height * thumb_width // thumb_height
#             left = (image_width - target_width) // 2
#             image = image.crop((left, 0, left + target_width, image_height))
# 
#     # Resize
#     image.thumbnail((thumb_width, thumb_height), Image.ANTIALIAS)
# 
#     # Save the thumbnail
#     thumb_bytes_io = io.BytesIO()
#     image.save(thumb_bytes_io, format, **options.get(image.format, {}))
#     thumb_bytes_io.seek(0)
#     thumb_storage.save(thumb_name, thumb_bytes_io)
# 
# Path: gallery/storages.py
# @functools.lru_cache()
# def get_storage(name):
#     name = name.upper()
#     storage_setting = f'GALLERY_{name}_STORAGE'
#     dir_setting = f'GALLERY_{name}_DIR'
#     try:
#         storage_class = getattr(settings, storage_setting)
#     except AttributeError:
#         # There's a good chance that this fallback will survive for a long
#         # time because deprecating it would require updating all the tests.
#         try:
#             storage_dir = getattr(settings, dir_setting)
#         except AttributeError:
#             raise ImproperlyConfigured(
#                 f"Please define {storage_setting} or {dir_setting}")
#         else:
#             return FileSystemStorage(location=storage_dir)
#     else:
#         return import_string(storage_class)()


import hashlib
import os
from django.conf import settings
from django.contrib.auth.models import Group, User
from django.db import models
from django.db.models import Q
from django.urls import reverse
from django.utils.translation import gettext_lazy as _
from .imgutil import make_thumbnail
from .storages import get_storage
        return photos.order_by('date', 'filename')[:1].get()

    def get_previous_in_queryset(self, photos):
        if self.date is None:
            photos = photos.filter(
                date__isnull=True, filename__lt=self.filename)
        else:
            photos = photos.filter(
                Q(date__isnull=True) |
                Q(date__lt=self.date) |
                Q(date=self.date, filename__gt=self.filename))
        return photos.order_by('-date', '-filename')[:1].get()

    def image_name(self):
        return os.path.join(self.album.dirpath, self.filename)

    def thumb_name(self, preset):
        prefix = self.album.date.strftime('%y%m')
        hsh = hashlib.md5()
        hsh.update(str(settings.SECRET_KEY).encode())
        hsh.update(str(self.album.pk).encode())
        hsh.update(str(self.pk).encode())
        hsh.update(str(settings.GALLERY_RESIZE_PRESETS[preset]).encode())
        ext = os.path.splitext(self.filename)[1].lower()
        return os.path.join(prefix, hsh.hexdigest() + ext)

    def thumbnail(self, preset):
        image_name = self.image_name()
        thumb_name = self.thumb_name(preset)
        photo_storage = get_storage('photo')

# Path: gallery/imgutil.py
# def make_thumbnail(image_name, thumb_name, preset,
#                    image_storage, thumb_storage):
# 
#     options = getattr(settings, 'GALLERY_RESIZE_OPTIONS', {})
#     presets = getattr(settings, 'GALLERY_RESIZE_PRESETS', {})
# 
#     # Load the image
#     image = Image.open(image_storage.open(image_name))
#     format = image.format
# 
#     if format == 'JPEG':
#         # Auto-rotate JPEG files based on EXIF information
#         try:                                                # pragma: no cover
#             # Use of an undocumented API — let's catch exceptions liberally
#             orientation = image._getexif()[274]
#             image = exif_rotations[orientation](image)
#         except Exception:
#             pass
# 
#         # Increase Pillow's buffer from 64k to 4MB to avoid JPEG save errors
#         ImageFile.MAXBLOCK = 4194304
# 
#     # Pre-crop if requested and the aspect ratios don't match exactly
#     image_width, image_height = image.size
#     thumb_width, thumb_height, crop = presets[preset]
#     if crop:
#         if thumb_width * image_height > image_width * thumb_height:
#             target_height = image_width * thumb_height // thumb_width
#             top = (image_height - target_height) // 2
#             image = image.crop((0, top, image_width, top + target_height))
#         elif thumb_width * image_height < image_width * thumb_height:
#             target_width = image_height * thumb_width // thumb_height
#             left = (image_width - target_width) // 2
#             image = image.crop((left, 0, left + target_width, image_height))
# 
#     # Resize
#     image.thumbnail((thumb_width, thumb_height), Image.ANTIALIAS)
# 
#     # Save the thumbnail
#     thumb_bytes_io = io.BytesIO()
#     image.save(thumb_bytes_io, format, **options.get(image.format, {}))
#     thumb_bytes_io.seek(0)
#     thumb_storage.save(thumb_name, thumb_bytes_io)
# 
# Path: gallery/storages.py
# @functools.lru_cache()
# def get_storage(name):
#     name = name.upper()
#     storage_setting = f'GALLERY_{name}_STORAGE'
#     dir_setting = f'GALLERY_{name}_DIR'
#     try:
#         storage_class = getattr(settings, storage_setting)
#     except AttributeError:
#         # There's a good chance that this fallback will survive for a long
#         # time because deprecating it would require updating all the tests.
#         try:
#             storage_dir = getattr(settings, dir_setting)
#         except AttributeError:
#             raise ImproperlyConfigured(
#                 f"Please define {storage_setting} or {dir_setting}")
#         else:
#             return FileSystemStorage(location=storage_dir)
#     else:
#         return import_string(storage_class)()


# Path: gallery/models.py
import hashlib
import os
from django.conf import settings
from django.contrib.auth.models import Group, User
from django.db import models
from django.db.models import Q
from django.urls import reverse
from django.utils.translation import gettext_lazy as _
from .imgutil import make_thumbnail
from .storages import get_storage

        return photos.order_by('date', 'filename')[:1].get()

    def get_previous_in_queryset(self, photos):
        if self.date is None:
            photos = photos.filter(
                date__isnull=True, filename__lt=self.filename)
        else:
            photos = photos.filter(
                Q(date__isnull=True) |
                Q(date__lt=self.date) |
                Q(date=self.date, filename__gt=self.filename))
        return photos.order_by('-date', '-filename')[:1].get()

    def image_name(self):
        return os.path.join(self.album.dirpath, self.filename)

    def thumb_name(self, preset):
        prefix = self.album.date.strftime('%y%m')
        hsh = hashlib.md5()
        hsh.update(str(settings.SECRET_KEY).encode())
        hsh.update(str(self.album.pk).encode())
        hsh.update(str(self.pk).encode())
        hsh.update(str(settings.GALLERY_RESIZE_PRESETS[preset]).encode())
        ext = os.path.splitext(self.filename)[1].lower()
        return os.path.join(prefix, hsh.hexdigest() + ext)

    def thumbnail(self, preset):
        image_name = self.image_name()
        thumb_name = self.thumb_name(preset)
        photo_storage = get_storage('photo')

        cache_storage = get_storage('cache')