From: Michael Tremer Date: Sat, 6 Jan 2024 17:17:18 +0000 (+0000) Subject: accounts: Deliver avatars in WEBP format if the browser supports it X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=e6340233728cce473f3f09b54f6ddee9d6e2d27f;p=ipfire.org.git accounts: Deliver avatars in WEBP format if the browser supports it Signed-off-by: Michael Tremer --- diff --git a/src/backend/accounts.py b/src/backend/accounts.py index beb4636d..2b2e42be 100644 --- a/src/backend/accounts.py +++ b/src/backend/accounts.py @@ -1277,7 +1277,7 @@ class Account(LDAPObject): return url - async def get_avatar(self, size=None): + async def get_avatar(self, size=None, format=None): # Check the PostgreSQL database photo = self._fetch_avatar() @@ -1294,7 +1294,8 @@ class Account(LDAPObject): return photo # Compose the cache key - key = "accounts:%s:avatar:%s:%s" % (self.uid, self.avatar_hash, size) + key = "accounts:%s:avatar:%s:%s:%s" \ + % (self.uid, self.avatar_hash, format or "N/A", size) # Try to retrieve something from the cache avatar = await self.backend.cache.get(key) @@ -1302,7 +1303,7 @@ class Account(LDAPObject): return avatar # Generate a new thumbnail - avatar = util.generate_thumbnail(photo, size, square=True) + avatar = util.generate_thumbnail(photo, size, square=True, format=format) # Save to cache for 24h await self.backend.cache.set(key, avatar, 86400) diff --git a/src/backend/util.py b/src/backend/util.py index f6285c02..c665602c 100644 --- a/src/backend/util.py +++ b/src/backend/util.py @@ -217,7 +217,7 @@ def generate_thumbnail(image, size, square=False, format=None, **args): # If we cannot open the image, we return it in raw form except PIL.UnidentifiedImageError as e: - return image + return image.getvalue() # Save image format format = format or image.format or "JPEG" diff --git a/src/web/base.py b/src/web/base.py index 11406547..468e2139 100644 --- a/src/web/base.py +++ b/src/web/base.py @@ -73,6 +73,21 @@ class BaseHandler(tornado.web.RequestHandler): self.render("error.html", status_code=status_code, message=message, **kwargs) + def browser_accepts(self, t): + """ + Checks if type is in Accept: header + """ + accepts = [] + + for elem in self.request.headers.get("Accept", "").split(","): + # Remove q=N + type, delim, q = elem.partition(";") + + accepts.append(type) + + # Check if the filetype is in the list of accepted ones + return t in accepts + @property def hostname(self): # Return hostname in production diff --git a/src/web/users.py b/src/web/users.py index 7867e9d6..6917fdbd 100644 --- a/src/web/users.py +++ b/src/web/users.py @@ -48,6 +48,11 @@ class ShowHandler(base.BaseHandler): class AvatarHandler(base.BaseHandler): async def get(self, uid): + if self.browser_accepts("image/webp"): + format = "WEBP" + else: + format = "JPEG" + # Get the desired size of the avatar file size = self.get_argument("size", None) @@ -67,14 +72,14 @@ class AvatarHandler(base.BaseHandler): self.set_expires(31536000) # Resize avatar - avatar = await account.get_avatar(size) + avatar = await account.get_avatar(size, format=format) # If there is no avatar, we serve a default image if not avatar: logging.debug("No avatar uploaded for %s" % account) # Generate a random avatar with only one letter - avatar = await self._get_avatar(account, size=size) + avatar = await self._get_avatar(account, size=size, format=format) m = magic.Magic(mime=True, uncompress=True) @@ -92,7 +97,7 @@ class AvatarHandler(base.BaseHandler): # Deliver payload self.finish(avatar) - async def _get_avatar(self, account, size=None, **args): + async def _get_avatar(self, account, size=None, format=None, **args): letters = account.initials if size is None: @@ -102,14 +107,11 @@ class AvatarHandler(base.BaseHandler): if size >= 2048: size = 2048 - print("LETTERS", letters) - # Cache key - key = "avatar:letter:%s:%s" % ("".join(letters), size) + key = "avatar:letter:%s:%s:%s" % ("".join(letters), format or "N/A", size) # Fetch avatar from the cache - #avatar = await self.backend.cache.get(key) - avatar = None + avatar = await self.backend.cache.get(key) if not avatar: avatar = self._make_avatar(letters, size=size, **args) @@ -154,7 +156,7 @@ class AvatarHandler(base.BaseHandler): draw.text((width // 2, height // 2), "".join(letters), font=font, anchor="mm", fill=COLOUR_DARK) - return util.generate_thumbnail(image, size, square=True) + return util.generate_thumbnail(image, size, square=True, format=format) class EditHandler(base.BaseHandler):