Migrated to rlottie utility
This commit is contained in:
+6
-11
@@ -163,18 +163,13 @@ bridge:
|
|||||||
image_as_file_size: 10
|
image_as_file_size: 10
|
||||||
# Maximum size of Telegram documents in megabytes to bridge.
|
# Maximum size of Telegram documents in megabytes to bridge.
|
||||||
max_document_size: 100
|
max_document_size: 100
|
||||||
# Format, animated sticker convert to (unstable).
|
# Format, animated sticker convert to.
|
||||||
# Supported values:
|
# Supported values:
|
||||||
# image - converts to png (fastest and preferred),
|
# png - converts to png (fastest and preferred),
|
||||||
# gif - converts to gif animation (requires gifski binary and takes a lot of time),
|
# gif - converts to gif animation, requires PIL, slow, sometimes loses transparency,
|
||||||
# video - video in mp4 container (requires gifski and ffmpeg binary and takes a lot of time, but less than gif)
|
# gifc - uses same utility as png, faster but without transparency at all
|
||||||
# To install library:
|
# mp4 - video in mp4 container (ffmpeg binary and takes a lot of time, but less than gif)
|
||||||
# source venv/bin/activate
|
animated_sticker_target_type: gif
|
||||||
# pip install nodeenv
|
|
||||||
# nodeenv -p
|
|
||||||
# source venv/bin/activate
|
|
||||||
# npm install -g puppeteer-lottie-cli
|
|
||||||
animated_sticker_target_type: image
|
|
||||||
|
|
||||||
# Whether to bridge Telegram bot messages as m.notices or m.texts.
|
# Whether to bridge Telegram bot messages as m.notices or m.texts.
|
||||||
bot_messages_as_notices: true
|
bot_messages_as_notices: true
|
||||||
|
|||||||
@@ -167,9 +167,9 @@ async def transfer_file_to_matrix(client: MautrixTelegramClient, intent: IntentA
|
|||||||
if not location_id:
|
if not location_id:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
db_file = DBTelegramFile.get(location_id)
|
#db_file = DBTelegramFile.get(location_id)
|
||||||
if db_file:
|
#if db_file:
|
||||||
return db_file
|
# return db_file
|
||||||
|
|
||||||
try:
|
try:
|
||||||
lock = transfer_locks[location_id]
|
lock = transfer_locks[location_id]
|
||||||
@@ -186,9 +186,9 @@ async def _unlocked_transfer_file_to_matrix(client: MautrixTelegramClient, inten
|
|||||||
thumbnail: TypeThumbnail, is_sticker: bool,
|
thumbnail: TypeThumbnail, is_sticker: bool,
|
||||||
tgs_convert_type: str
|
tgs_convert_type: str
|
||||||
) -> Optional[DBTelegramFile]:
|
) -> Optional[DBTelegramFile]:
|
||||||
db_file = DBTelegramFile.get(loc_id)
|
#db_file = DBTelegramFile.get(loc_id)
|
||||||
if db_file:
|
#if db_file:
|
||||||
return db_file
|
# return db_file
|
||||||
|
|
||||||
try:
|
try:
|
||||||
file = await client.download_file(location)
|
file = await client.download_file(location)
|
||||||
@@ -203,9 +203,8 @@ async def _unlocked_transfer_file_to_matrix(client: MautrixTelegramClient, inten
|
|||||||
|
|
||||||
image_converted = False
|
image_converted = False
|
||||||
if mime_type == "application/gzip" and is_sticker:
|
if mime_type == "application/gzip" and is_sticker:
|
||||||
mime_type, file, width, height = convert_tgs_to(file, tgs_convert_type, 256, 256)
|
mime_type, file, width, height, thumbnail = convert_tgs_to(file, tgs_convert_type, 256, 256)
|
||||||
image_converted = width is not None
|
image_converted = width is not None
|
||||||
thumbnail = None
|
|
||||||
|
|
||||||
if mime_type == "image/webp":
|
if mime_type == "image/webp":
|
||||||
new_mime_type, file, width, height = convert_image(
|
new_mime_type, file, width, height = convert_image(
|
||||||
|
|||||||
@@ -1,38 +1,137 @@
|
|||||||
# Generated by Netbeans
|
# Generated by Netbeans
|
||||||
# Author: Eramde
|
# Author: Eramde
|
||||||
# Date: 09.2019
|
# Date: 09.2019
|
||||||
import tempfile
|
|
||||||
import logging
|
import logging
|
||||||
import gzip
|
|
||||||
import subprocess
|
|
||||||
import os
|
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
from typing import Optional, Tuple
|
from typing import Optional, Tuple
|
||||||
|
|
||||||
|
|
||||||
LOG: logging.Logger = logging.getLogger("mau.util.tgs")
|
LOG: logging.Logger = logging.getLogger("mau.util.tgs")
|
||||||
|
|
||||||
TYPE_TO_MIME = {"image": "image/png", "gif": "image/gif", "video": "video/mp4"}
|
try:
|
||||||
TYPE_TO_FORMAT = {"image": ".png", "gif": ".gif", "video": ".mp4"}
|
import gzip
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
proc = subprocess.Popen(["lottieconverter"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||||
|
_, err = proc.communicate()
|
||||||
|
if err is not None and not err.decode("utf-8").startswith("Usage"):
|
||||||
|
raise ImportError(err)
|
||||||
|
|
||||||
|
|
||||||
|
def _tgs_to_png(file: bytes, width: int,
|
||||||
|
height: int, frame: int = None) -> Tuple[bytes, Optional[bytes]]:
|
||||||
|
if not frame:
|
||||||
|
frame = 1
|
||||||
|
p = subprocess.run(["lottieconverter", "-", "-", "png",
|
||||||
|
str.format(f"{width}x{height}"), str(frame)], stdout=subprocess.PIPE,
|
||||||
|
input=file, universal_newlines=False)
|
||||||
|
return p.stdout, None
|
||||||
|
|
||||||
|
|
||||||
|
TGS_CONVERTERS = {"png": _tgs_to_png}
|
||||||
|
|
||||||
|
def _tgs_to_gif(file: bytes, width: int, height: int) -> Tuple[bytes, Optional[bytes]]:
|
||||||
|
p = subprocess.run(["lottieconverter", "-", "-", "gif",
|
||||||
|
str.format(f"{width}x{height}"), "0", "0x202020"],
|
||||||
|
stdout=subprocess.PIPE,
|
||||||
|
input=file, universal_newlines=False)
|
||||||
|
return p.stdout, None
|
||||||
|
|
||||||
|
TGS_CONVERTERS.update({"gifc": _tgs_to_gif})
|
||||||
|
|
||||||
|
try:
|
||||||
|
from PIL import Image
|
||||||
|
|
||||||
|
def _tgs_to_gif(file: bytes, width: int, height: int) \
|
||||||
|
-> Tuple[bytes, Optional[bytes]]:
|
||||||
|
frames = []
|
||||||
|
first_frame = None
|
||||||
|
for i in range(1, 100):
|
||||||
|
frame, _ = _tgs_to_png(file, width, height, i)
|
||||||
|
if not first_frame:
|
||||||
|
first_frame = frame
|
||||||
|
image = Image.open(BytesIO(frame))
|
||||||
|
if image.mode not in ["RGBA", "RGBa"]:
|
||||||
|
image = image.convert("RGBA")
|
||||||
|
alpha = image.getchannel("A")
|
||||||
|
image = image.convert('P', palette=Image.ADAPTIVE, colors=255)
|
||||||
|
mask = Image.eval(alpha, lambda a: 255 if a <= 128 else 0)
|
||||||
|
image.paste(255, mask)
|
||||||
|
frames.append(image)
|
||||||
|
|
||||||
|
duration = 100
|
||||||
|
fo = BytesIO()
|
||||||
|
frames[0].save(
|
||||||
|
fo,
|
||||||
|
format='GIF',
|
||||||
|
append_images=frames[1:],
|
||||||
|
save_all=True,
|
||||||
|
duration=duration,
|
||||||
|
loop=0,
|
||||||
|
transparency=255,
|
||||||
|
disposal=2,
|
||||||
|
)
|
||||||
|
return fo.getvalue(), first_frame
|
||||||
|
|
||||||
|
TGS_CONVERTERS.update({"gif": _tgs_to_gif})
|
||||||
|
except ImportError:
|
||||||
|
LOG.warn("Unable to create tgs to gif converter, install PIL")
|
||||||
|
|
||||||
|
try:
|
||||||
|
import cv2
|
||||||
|
import numpy
|
||||||
|
import tempfile
|
||||||
|
import os
|
||||||
|
|
||||||
|
def _tgs_to_video(file: bytes, width: int, height: int) \
|
||||||
|
-> Tuple[bytes, Optional[bytes]]:
|
||||||
|
with tempfile.NamedTemporaryFile(mode="r+b", suffix=".mp4") as tmp:
|
||||||
|
video_tmp_file = tmp.name
|
||||||
|
video = None
|
||||||
|
first_frame = None
|
||||||
|
try:
|
||||||
|
video = cv2.VideoWriter(filename=video_tmp_file, apiPreference=cv2.CAP_ANY,
|
||||||
|
fourcc=cv2.VideoWriter_fourcc(*'vp09'),
|
||||||
|
fps=10,
|
||||||
|
frameSize=(width, height))
|
||||||
|
|
||||||
|
for i in range(1, 100):
|
||||||
|
frame, _ = _tgs_to_png(file, width, height, i)
|
||||||
|
if not first_frame:
|
||||||
|
first_frame = frame
|
||||||
|
video.write(cv2.cvtColor(numpy.array(Image.open(BytesIO(frame))),
|
||||||
|
cv2.COLOR_RGB2BGR))
|
||||||
|
|
||||||
|
finally:
|
||||||
|
if video:
|
||||||
|
video.release()
|
||||||
|
with open(video_tmp_file, "rb") as video_file:
|
||||||
|
out = video_file.read()
|
||||||
|
os.remove(video_tmp_file)
|
||||||
|
return out, first_frame
|
||||||
|
"""
|
||||||
|
It seems, that riot don't wont to play converted videos...
|
||||||
|
"""
|
||||||
|
TGS_CONVERTERS.update({"mp4": _tgs_to_video})
|
||||||
|
except ImportError:
|
||||||
|
LOG.warn("Unable to create tgs to video converter, "
|
||||||
|
"install PIL, numpy and opencv-python-headless")
|
||||||
|
|
||||||
|
except (ImportError, OSError):
|
||||||
|
LOG.exception("Unable to init tgs converters, possibly missing lottieconverter")
|
||||||
|
TGS_CONVERTERS = {}
|
||||||
|
|
||||||
|
|
||||||
|
TYPE_TO_MIME = {"png": "image/png", "gif": "image/gif", "gifc": "image/gif", "mp4": "video/mp4"}
|
||||||
|
|
||||||
|
|
||||||
def convert_tgs_to(file: bytes, convert_to: str, width: int = 200, height: int = 200) \
|
def convert_tgs_to(file: bytes, convert_to: str, width: int = 200, height: int = 200) \
|
||||||
-> Tuple[str, bytes, Optional[int], Optional[int]]:
|
-> Tuple[str, bytes, Optional[int], Optional[int], Optional[bytes]]:
|
||||||
if convert_to in TYPE_TO_FORMAT:
|
if convert_to in TGS_CONVERTERS:
|
||||||
file_ext = TYPE_TO_FORMAT[convert_to]
|
|
||||||
mime = TYPE_TO_MIME[convert_to]
|
mime = TYPE_TO_MIME[convert_to]
|
||||||
lottie = gzip.open(BytesIO(file))
|
converter = TGS_CONVERTERS[convert_to]
|
||||||
with tempfile.NamedTemporaryFile(mode="w+b", suffix=".json") as json_out:
|
out, preview = converter(file, width, height)
|
||||||
with tempfile.NamedTemporaryFile(mode="r+b", suffix=file_ext) as tmp:
|
return mime, out, width, height, preview
|
||||||
tmp_output_file = tmp.name
|
|
||||||
json_out.write(lottie.read())
|
|
||||||
json_out.flush()
|
|
||||||
subprocess.run(["puppeteer-lottie", "-q", "-i", json_out.name, "-o", tmp_output_file,
|
|
||||||
"-w", str(width), "-h", str(height)], capture_output=True)
|
|
||||||
with open(tmp_output_file, mode="r+b") as out_file:
|
|
||||||
out = out_file.read()
|
|
||||||
os.remove(tmp_output_file)
|
|
||||||
return mime, out, width, height
|
|
||||||
else:
|
else:
|
||||||
LOG.warning(f"Unable to convert animated sticker, type {convert_to} not supported")
|
LOG.warning(f"Unable to convert animated sticker, type {convert_to} not supported")
|
||||||
return "application/gzip", file, None, None
|
return "application/gzip", file, None, None, None
|
||||||
|
|||||||
Reference in New Issue
Block a user