Skip to content

Commit 58d4bbf

Browse files
authored
feat: Cache transparency property of image to improve thumbnail name prediction (#1365)
* Add transparency field to avoid accessing an image file to guess thumbnail names (esp. if using modern formats like webp) * Add webp upload capability * Default svg files to transparent * Fix test migrations * Remove unused import
1 parent 10537fa commit 58d4bbf

File tree

4 files changed

+31
-5
lines changed

4 files changed

+31
-5
lines changed
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Generated by Django 4.1.9 on 2023-06-21 16:57
2+
3+
from django.db import migrations, models
4+
5+
6+
class Migration(migrations.Migration):
7+
8+
dependencies = [
9+
('filer', '0016_alter_folder_index_together_remove_folder_level_and_more'),
10+
]
11+
12+
operations = [
13+
migrations.AddField(
14+
model_name='image',
15+
name='_transparent',
16+
field=models.BooleanField(default=False),
17+
),
18+
]

filer/models/abstract.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import logging
22

3+
import easy_thumbnails.utils
34
from django.db import models
45
from django.utils.functional import cached_property
56
from django.utils.translation import gettext_lazy as _
@@ -39,6 +40,11 @@ class BaseImage(File):
3940
blank=True,
4041
)
4142

43+
_transparent = models.BooleanField(
44+
null=False,
45+
default=False,
46+
)
47+
4248
default_alt_text = models.CharField(
4349
_("default alt text"),
4450
max_length=255,
@@ -92,14 +98,18 @@ def file_data_changed(self, post_init=False):
9298
imgfile.seek(0)
9399
if self.mime_type == 'image/svg+xml':
94100
self._width, self._height = VILImage.load(imgfile).size
101+
self._transparent = True
95102
else:
96-
self._width, self._height = PILImage.open(imgfile).size
103+
pil_image = PILImage.open(imgfile)
104+
self._width, self._height = pil_image.size
105+
self._transparent = easy_thumbnails.utils.is_transparent(pil_image)
97106
imgfile.seek(0)
98107
except Exception:
99108
if post_init is False:
100109
# in case `imgfile` could not be found, unset dimensions
101110
# but only if not initialized by loading a fixture file
102111
self._width, self._height = None, None
112+
self._transparent = False
103113
return attrs_updated
104114

105115
def save(self, *args, **kwargs):

filer/templatetags/filer_admin_tags.py

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
from django.utils.safestring import mark_safe
1010
from django.utils.translation import gettext_lazy as _
1111

12-
from easy_thumbnails.conf import settings as thumbnail_settings
1312
from easy_thumbnails.files import get_thumbnailer
1413
from easy_thumbnails.options import ThumbnailOptions
1514

@@ -127,10 +126,8 @@ def file_icon_context(file, detail, width, height):
127126
thumbnail_options = ThumbnailOptions(opts)
128127
# Optimize directory listing:
129128
if not detail and width == height and width in DEFERRED_THUMBNAIL_SIZES and hasattr(file, "thumbnail_name"):
130-
# Avoid jpg thumbnails for pngs with transparency
131-
transparent = file.file.name.rsplit(".", 1)[-1] == thumbnail_settings.THUMBNAIL_TRANSPARENCY_EXTENSION
132129
# Get name of thumbnail from easy-thumbnail
133-
configured_name = thumbnailer.get_thumbnail_name(thumbnail_options, transparent=transparent)
130+
configured_name = thumbnailer.get_thumbnail_name(thumbnail_options, transparent=file._transparent)
134131
# If the name was annotated: Thumbnail exists and we can use it
135132
if configured_name == file.thumbnail_name:
136133
icon_url = default_storage.url(configured_name)

tests/utils/extended_app/migrations/0001_initial.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ class Migration(migrations.Migration):
1818
fields=[
1919
('_height', models.FloatField(blank=True, null=True)),
2020
('_width', models.FloatField(blank=True, null=True)),
21+
('_transparent', models.BooleanField(null=False, default=False)),
2122
('default_alt_text', models.CharField(blank=True, max_length=255, null=True, verbose_name='default alt text')),
2223
('default_caption', models.CharField(blank=True, max_length=255, null=True, verbose_name='default caption')),
2324
('subject_location', models.CharField(blank=True, default='', max_length=64, verbose_name='subject location')),

0 commit comments

Comments
 (0)