mirror of https://gitlab.com/litecord/litecord.git
images: add implementation for gif handling
- images: prefix the icon_hash with `a_` - pubsub.lazy_guild: ignore update_user when unitialized list data
This commit is contained in:
parent
2140f572e8
commit
d2bedb75a4
|
|
@ -1,6 +1,8 @@
|
||||||
|
import os
|
||||||
import mimetypes
|
import mimetypes
|
||||||
import asyncio
|
import asyncio
|
||||||
import base64
|
import base64
|
||||||
|
import tempfile
|
||||||
|
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from hashlib import sha256
|
from hashlib import sha256
|
||||||
|
|
@ -10,6 +12,7 @@ from io import BytesIO
|
||||||
from logbook import Logger
|
from logbook import Logger
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
|
|
||||||
IMAGE_FOLDER = Path('./images')
|
IMAGE_FOLDER = Path('./images')
|
||||||
log = Logger(__name__)
|
log = Logger(__name__)
|
||||||
|
|
||||||
|
|
@ -228,10 +231,54 @@ class IconManager:
|
||||||
return await self.generic_get(
|
return await self.generic_get(
|
||||||
'guild', guild_id, icon_hash, **kwargs)
|
'guild', guild_id, icon_hash, **kwargs)
|
||||||
|
|
||||||
async def _put_gif(self, scope: str, key,
|
async def _resize_gif(self, scope: str, key,
|
||||||
raw_data: bytes, **kwargs) -> Icon:
|
raw_data: bytes, target: tuple) -> tuple:
|
||||||
"""Insert a gif icon"""
|
"""Resize a GIF image."""
|
||||||
raise NotImplementedError
|
# generate a temporary file to call gifsticle to and from.
|
||||||
|
input_fd, input_path = tempfile.mkstemp(suffix='.gif')
|
||||||
|
_, output_path = tempfile.mkstemp(suffix='.gif')
|
||||||
|
|
||||||
|
input_handler = os.fdopen(input_fd, 'wb')
|
||||||
|
|
||||||
|
# make sure its valid image data
|
||||||
|
data_fd = BytesIO(raw_data)
|
||||||
|
image = Image.open(data_fd)
|
||||||
|
image.close()
|
||||||
|
|
||||||
|
log.info('resizing a GIF from {} to {}',
|
||||||
|
image.size, target)
|
||||||
|
|
||||||
|
# insert image info on input_handler
|
||||||
|
input_handler.write(raw_data)
|
||||||
|
|
||||||
|
# call gifsicle under subprocess
|
||||||
|
log.debug('input: {}', input_path)
|
||||||
|
log.debug('output: {}', output_path)
|
||||||
|
|
||||||
|
process = await asyncio.create_subprocess_shell(
|
||||||
|
f'gifsicle --resize {target[0]}x{target[1]} '
|
||||||
|
f'{input_path} > {output_path}',
|
||||||
|
stdout=asyncio.subprocess.PIPE,
|
||||||
|
stderr=asyncio.subprocess.PIPE,
|
||||||
|
)
|
||||||
|
|
||||||
|
# run it, etc.
|
||||||
|
await process.communicate()
|
||||||
|
|
||||||
|
# write over an empty data_fd
|
||||||
|
data_fd = BytesIO()
|
||||||
|
output_handler = open(output_path, 'rb')
|
||||||
|
data_fd.write(output_handler.read())
|
||||||
|
|
||||||
|
# reseek, save to raw_data, reseek again.
|
||||||
|
# TODO: remove raw_data altogether as its inefficient
|
||||||
|
# to have two representations of the same bytes
|
||||||
|
data_fd.seek(0)
|
||||||
|
raw_data = data_fd.read()
|
||||||
|
data_fd.seek(0)
|
||||||
|
|
||||||
|
return data_fd, raw_data
|
||||||
|
|
||||||
|
|
||||||
async def put(self, scope: str, key: str,
|
async def put(self, scope: str, key: str,
|
||||||
b64_data: str, **kwargs) -> Icon:
|
b64_data: str, **kwargs) -> Icon:
|
||||||
|
|
@ -252,10 +299,10 @@ class IconManager:
|
||||||
|
|
||||||
# size management is different for gif files
|
# size management is different for gif files
|
||||||
# as they're composed of multiple frames.
|
# as they're composed of multiple frames.
|
||||||
if mime == 'image/gif':
|
if 'size' in kwargs and mime == 'image/gif':
|
||||||
return await self._put_gif(scope, key, raw_data, **kwargs)
|
data_fd, raw_data = await self._resize_gif(
|
||||||
|
scope, key, raw_data, kwargs['size'])
|
||||||
if 'size' in kwargs:
|
elif 'size' in kwargs:
|
||||||
image = Image.open(data_fd)
|
image = Image.open(data_fd)
|
||||||
|
|
||||||
want = kwargs['size']
|
want = kwargs['size']
|
||||||
|
|
@ -280,6 +327,9 @@ class IconManager:
|
||||||
if scope != 'emoji'
|
if scope != 'emoji'
|
||||||
else None)
|
else None)
|
||||||
|
|
||||||
|
if scope == 'user' and mime == 'image/gif':
|
||||||
|
icon_hash = f'a_{icon_hash}'
|
||||||
|
|
||||||
await self.storage.db.execute("""
|
await self.storage.db.execute("""
|
||||||
INSERT INTO icons (scope, key, hash, mime)
|
INSERT INTO icons (scope, key, hash, mime)
|
||||||
VALUES ($1, $2, $3, $4)
|
VALUES ($1, $2, $3, $4)
|
||||||
|
|
|
||||||
|
|
@ -931,6 +931,13 @@ class GuildMemberList:
|
||||||
|
|
||||||
async def update_user(self, user_id: int):
|
async def update_user(self, user_id: int):
|
||||||
"""Called for user updates such as avatar or username."""
|
"""Called for user updates such as avatar or username."""
|
||||||
|
if not self.list:
|
||||||
|
return
|
||||||
|
|
||||||
|
if user_id not in self.list.members:
|
||||||
|
log.warning('lazy: ignoring unknown uid {}',
|
||||||
|
user_id)
|
||||||
|
return
|
||||||
|
|
||||||
# update user information inside self.list.members
|
# update user information inside self.list.members
|
||||||
self.list.members[user_id]['user'] = \
|
self.list.members[user_id]['user'] = \
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue