mirror of
https://github.com/mautrix/telegram.git
synced 2026-05-17 07:25:46 +03:00
Deduplicate lottieconverter calls in tgs_converter
Also fix finding first frame file Fixes #690 Closes #728
This commit is contained in:
@@ -6,6 +6,7 @@
|
||||
* Fixed syncing contacts throwing an error for new accounts.
|
||||
* Fixed migrating from the legacy database if the database schema had been
|
||||
corrupted (e.g. by using 3rd party tools for SQLite -> Postgres migration).
|
||||
* Fixed converting animated stickers to webm with >33 FPS.
|
||||
|
||||
# v0.11.0 (2021-12-28)
|
||||
|
||||
|
||||
@@ -19,12 +19,15 @@ from __future__ import annotations
|
||||
from typing import Any, Awaitable, Callable
|
||||
import asyncio.subprocess
|
||||
import logging
|
||||
import os
|
||||
import os.path
|
||||
import shutil
|
||||
import tempfile
|
||||
|
||||
from attr import dataclass
|
||||
|
||||
from mautrix.util import ffmpeg
|
||||
|
||||
log: logging.Logger = logging.getLogger("mau.util.tgs")
|
||||
|
||||
|
||||
@@ -48,61 +51,49 @@ def abswhich(program: str | None) -> str | None:
|
||||
|
||||
|
||||
lottieconverter = abswhich("lottieconverter")
|
||||
ffmpeg = abswhich("ffmpeg")
|
||||
|
||||
|
||||
async def _run_lottieconverter(args: tuple[str, ...], input_data: bytes) -> bytes:
|
||||
proc = await asyncio.create_subprocess_exec(
|
||||
lottieconverter,
|
||||
*args,
|
||||
stdout=asyncio.subprocess.PIPE,
|
||||
stderr=asyncio.subprocess.PIPE,
|
||||
stdin=asyncio.subprocess.PIPE,
|
||||
)
|
||||
stdout, stderr = await proc.communicate(input_data)
|
||||
if proc.returncode == 0:
|
||||
return stdout
|
||||
else:
|
||||
err_text = stderr.decode("utf-8") if stderr else f"unknown ({proc.returncode})"
|
||||
raise ffmpeg.ConverterError(f"lottieconverter error: {err_text}")
|
||||
|
||||
|
||||
if lottieconverter:
|
||||
|
||||
async def tgs_to_png(file: bytes, width: int, height: int, **_: Any) -> ConvertedSticker:
|
||||
frame = 1
|
||||
proc = await asyncio.create_subprocess_exec(
|
||||
lottieconverter,
|
||||
"-",
|
||||
"-",
|
||||
"png",
|
||||
f"{width}x{height}",
|
||||
str(frame),
|
||||
stdout=asyncio.subprocess.PIPE,
|
||||
stdin=asyncio.subprocess.PIPE,
|
||||
)
|
||||
stdout, stderr = await proc.communicate(file)
|
||||
if proc.returncode == 0:
|
||||
return ConvertedSticker("image/png", stdout)
|
||||
else:
|
||||
log.error(
|
||||
"lottieconverter error: "
|
||||
+ (
|
||||
stderr.decode("utf-8")
|
||||
if stderr is not None
|
||||
else f"unknown ({proc.returncode})"
|
||||
)
|
||||
try:
|
||||
converted_png = await _run_lottieconverter(
|
||||
args=("-", "-", "png", f"{width}x{height}", str(frame)),
|
||||
input_data=file,
|
||||
)
|
||||
return ConvertedSticker("image/png", converted_png)
|
||||
except ffmpeg.ConverterError as e:
|
||||
log.error(str(e))
|
||||
return ConvertedSticker("application/gzip", file)
|
||||
|
||||
async def tgs_to_gif(
|
||||
file: bytes, width: int, height: int, fps: int = 25, **_: Any
|
||||
) -> ConvertedSticker:
|
||||
proc = await asyncio.create_subprocess_exec(
|
||||
lottieconverter,
|
||||
"-",
|
||||
"-",
|
||||
"gif",
|
||||
f"{width}x{height}",
|
||||
str(fps),
|
||||
stdout=asyncio.subprocess.PIPE,
|
||||
stdin=asyncio.subprocess.PIPE,
|
||||
)
|
||||
stdout, stderr = await proc.communicate(file)
|
||||
if proc.returncode == 0:
|
||||
return ConvertedSticker("image/gif", stdout)
|
||||
else:
|
||||
log.error(
|
||||
"lottieconverter error: "
|
||||
+ (
|
||||
stderr.decode("utf-8")
|
||||
if stderr is not None
|
||||
else f"unknown ({proc.returncode})"
|
||||
)
|
||||
try:
|
||||
converted_gif = await _run_lottieconverter(
|
||||
args=("-", "-", "gif", f"{width}x{height}", str(fps)),
|
||||
input_data=file,
|
||||
)
|
||||
return ConvertedSticker("image/gif", converted_gif)
|
||||
except ffmpeg.ConverterError as e:
|
||||
log.error(str(e))
|
||||
return ConvertedSticker("application/gzip", file)
|
||||
|
||||
converters["png"] = tgs_to_png
|
||||
@@ -115,62 +106,24 @@ if lottieconverter and ffmpeg:
|
||||
) -> ConvertedSticker:
|
||||
with tempfile.TemporaryDirectory(prefix="tgs_") as tmpdir:
|
||||
file_template = tmpdir + "/out_"
|
||||
proc = await asyncio.create_subprocess_exec(
|
||||
lottieconverter,
|
||||
"-",
|
||||
file_template,
|
||||
"pngs",
|
||||
f"{width}x{height}",
|
||||
str(fps),
|
||||
stdout=asyncio.subprocess.PIPE,
|
||||
stdin=asyncio.subprocess.PIPE,
|
||||
)
|
||||
_, stderr = await proc.communicate(file)
|
||||
if proc.returncode == 0:
|
||||
with open(f"{file_template}00.png", "rb") as first_frame_file:
|
||||
try:
|
||||
await _run_lottieconverter(
|
||||
args=("-", file_template, "pngs", f"{width}x{height}", str(fps)),
|
||||
input_data=file,
|
||||
)
|
||||
first_frame_name = sorted(os.listdir(tmpdir))[0]
|
||||
with open(f"{tmpdir}/{first_frame_name}", "rb") as first_frame_file:
|
||||
first_frame_data = first_frame_file.read()
|
||||
proc = await asyncio.create_subprocess_exec(
|
||||
ffmpeg,
|
||||
"-hide_banner",
|
||||
"-loglevel",
|
||||
"error",
|
||||
"-framerate",
|
||||
str(fps),
|
||||
"-pattern_type",
|
||||
"glob",
|
||||
"-i",
|
||||
file_template + "*.png",
|
||||
"-c:v",
|
||||
"libvpx-vp9",
|
||||
"-pix_fmt",
|
||||
"yuva420p",
|
||||
"-f",
|
||||
"webm",
|
||||
"-",
|
||||
stdout=asyncio.subprocess.PIPE,
|
||||
stdin=asyncio.subprocess.PIPE,
|
||||
)
|
||||
stdout, stderr = await proc.communicate()
|
||||
if proc.returncode == 0:
|
||||
return ConvertedSticker("video/webm", stdout, "image/png", first_frame_data)
|
||||
else:
|
||||
log.error(
|
||||
"ffmpeg error: "
|
||||
+ (
|
||||
stderr.decode("utf-8")
|
||||
if stderr is not None
|
||||
else f"unknown ({proc.returncode})"
|
||||
)
|
||||
)
|
||||
else:
|
||||
log.error(
|
||||
"lottieconverter error: "
|
||||
+ (
|
||||
stderr.decode("utf-8")
|
||||
if stderr is not None
|
||||
else f"unknown ({proc.returncode})"
|
||||
)
|
||||
webm_data = await ffmpeg.convert_path(
|
||||
input_args=("-framerate", str(fps), "-pattern_type", "glob"),
|
||||
input_file=f"{file_template}*.png",
|
||||
output_args=("-c:v", "libvpx-vp9", "-pix_fmt", "yuva420p", "-f", "webm"),
|
||||
output_path_override="-",
|
||||
output_extension=None,
|
||||
)
|
||||
return ConvertedSticker("video/webm", webm_data, "image/png", first_frame_data)
|
||||
except ffmpeg.ConverterError as e:
|
||||
log.error(str(e))
|
||||
return ConvertedSticker("application/gzip", file)
|
||||
|
||||
converters["webm"] = tgs_to_webm
|
||||
|
||||
Reference in New Issue
Block a user