]> git.ipfire.org Git - ipfire.org.git/commitdiff
accounts: Deliver avatars in WEBP format if the browser supports it
authorMichael Tremer <michael.tremer@ipfire.org>
Sat, 6 Jan 2024 17:17:18 +0000 (17:17 +0000)
committerMichael Tremer <michael.tremer@ipfire.org>
Sat, 6 Jan 2024 17:17:18 +0000 (17:17 +0000)
Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
src/backend/accounts.py
src/backend/util.py
src/web/base.py
src/web/users.py

index beb4636dfa26bc89e6109a1eeeb9b3f519bc6331..2b2e42be1d68f3963152be0459ef4106c623334f 100644 (file)
@@ -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)
index f6285c02a134aecb9fee274e49799f1f8119f2b3..c665602caab1fe46b78cfea6de0f4b589a6d845d 100644 (file)
@@ -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"
index 11406547c62d9ae0dfdd92a90d63ef758dfff16a..468e21391f624b05ebcd9a023c2d632f26d442e7 100644 (file)
@@ -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
index 7867e9d632f16b8cb09845864e22d0210b513077..6917fdbd98403d640ba8bd06d2028196eb74a243 100644 (file)
@@ -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):