telegram gif fix

This commit is contained in:
esef committed 2026-06-23 19:35:44 +03:00
1 parent 4a3e1b7029
commit 0aeaf60e86
1 file changed
+35 -31
+35 -31
View File
@@ -110,6 +110,8 @@ class TelegramForwarder:
prefix = self._forward_prefix(message) prefix = self._forward_prefix(message)
caption = message.caption or '' caption = message.caption or ''
# Log incoming message media types for debugging
print(f"[HANDLER] Types - photo:{bool(message.photo)} video:{bool(message.video)} animation:{bool(message.animation)} document:{bool(message.document)}", flush=True)
try: try:
if message.photo: if message.photo:
photo = message.photo[-1] photo = message.photo[-1]
@@ -117,24 +119,36 @@ class TelegramForwarder:
text = f'{prefix}{caption}'.strip() text = f'{prefix}{caption}'.strip()
await self._send_to_webhook(webhook_url, sender, text, file_data, filename) await self._send_to_webhook(webhook_url, sender, text, file_data, filename)
elif message.video: # video and document handling are now managed by the enhanced GIF block below
file_data, filename = await self._download_file(message.video.file_id)
text = f'{prefix}{caption}'.strip()
await self._send_to_webhook(webhook_url, sender, text, file_data, filename)
elif message.document: # --- START OF ENHANCED GIF HANDLING BLOCK ---
file_data, filename = await self._download_file(message.document.file_id) # Telegram may send a GIF as an `animation` (the normal case) *or*
text = f'{prefix}{caption}'.strip() # as a generic `document`/`video` with MIME type `video/mp4`. We want to
await self._send_to_webhook(webhook_url, sender, text, file_data, filename) # treat any of those as a potential GIF and try to convert it to a true
# GIF for Discord auto‑looping.
elif getattr(message, "animation", None) or (
getattr(message, "document", None) and message.document.mime_type.startswith("video")
) or message.video:
# Identify the file source
if message.animation:
file_id = message.animation.file_id
elif message.video:
file_id = message.video.file_id
else:
file_id = message.document.file_id
elif message.animation: print(f"[GIF-HANDLER] Detected potential media (file_id={file_id})", flush=True)
file_data, filename = await self._download_file(message.animation.file_id) file_data, filename = await self._download_file(file_id)
# convert mp4 animation to true gif so discord auto-loops it # Attempt conversion if it's a video/animation
try: try:
import asyncio import asyncio
import tempfile import tempfile
import os import os
import shutil
if not shutil.which('ffmpeg'):
raise RuntimeError("ffmpeg binary not found in PATH")
with tempfile.NamedTemporaryFile(suffix='.mp4', delete=False) as temp_mp4: with tempfile.NamedTemporaryFile(suffix='.mp4', delete=False) as temp_mp4:
temp_mp4.write(file_data) temp_mp4.write(file_data)
@@ -142,8 +156,6 @@ class TelegramForwarder:
temp_gif_path = temp_mp4_path + '.gif' temp_gif_path = temp_mp4_path + '.gif'
# Use a palette to preserve quality, but drop fps to 15 and scale width to 320 to keep file size low.
# Discord webhooks reject files > 25MB (sometimes > 8MB), and GIFs are massively unoptimized compared to MP4s.
process = await asyncio.create_subprocess_exec( process = await asyncio.create_subprocess_exec(
'ffmpeg', '-y', '-i', temp_mp4_path, 'ffmpeg', '-y', '-i', temp_mp4_path,
'-filter_complex', 'fps=15,scale=320:-1:flags=lanczos,split[s0][s1];[s0]palettegen[p];[s1][p]paletteuse', '-filter_complex', 'fps=15,scale=320:-1:flags=lanczos,split[s0][s1];[s0]palettegen[p];[s1][p]paletteuse',
@@ -152,38 +164,30 @@ class TelegramForwarder:
stderr=asyncio.subprocess.PIPE stderr=asyncio.subprocess.PIPE
) )
# Add a 30 second timeout so the bot doesn't hang if ffmpeg gets stuck stdout, stderr = await asyncio.wait_for(process.communicate(), timeout=30.0)
try:
_, stderr_data = await asyncio.wait_for(process.communicate(), timeout=30.0)
except asyncio.TimeoutError:
process.kill()
_, stderr_data = await process.communicate()
print("ffmpeg gif conversion timed out after 30 seconds.", flush=True)
if process.returncode == 0 and os.path.exists(temp_gif_path): if process.returncode == 0 and os.path.exists(temp_gif_path):
gif_size = os.path.getsize(temp_gif_path) gif_size = os.path.getsize(temp_gif_path)
# Fallback to MP4 if GIF is > 10 MiB, to prevent Discord from rejecting the webhook post
if gif_size < 10 * 1024 * 1024: if gif_size < 10 * 1024 * 1024:
with open(temp_gif_path, 'rb') as f: with open(temp_gif_path, 'rb') as f:
file_data = f.read() file_data = f.read()
filename = filename.rsplit('.', 1)[0] + '.gif' filename = filename.rsplit('.', 1)[0] + '.gif'
print(f"ffmpeg conversion succeeded: GIF size {gif_size / 1024 / 1024:.2f} MiB", flush=True)
else:
print(f"GIF was too large for Discord webhook ({gif_size / 1024 / 1024:.2f} MiB). Falling back to MP4.", flush=True)
else: else:
print(f"ffmpeg failed with code {process.returncode}. Stderr: {stderr_data.decode('utf-8', errors='ignore')}", flush=True) print(f"[GIF-HANDLER] ffmpeg error: {stderr.decode()}", flush=True)
try: if os.path.exists(temp_mp4_path): os.remove(temp_mp4_path)
if os.path.exists(temp_mp4_path): os.remove(temp_mp4_path) if os.path.exists(temp_gif_path): os.remove(temp_gif_path)
if os.path.exists(temp_gif_path): os.remove(temp_gif_path)
except Exception:
pass
except Exception as e: except Exception as e:
print(f'ffmpeg gif conversion threw an exception: {e}', flush=True) print(f'ffmpeg conversion skipped/failed: {e}', flush=True)
text = f'{prefix}{caption}'.strip() text = f'{prefix}{caption}'.strip()
await self._send_to_webhook(webhook_url, sender, text, file_data, filename) await self._send_to_webhook(webhook_url, sender, text, file_data, filename)
elif message.document:
file_data, filename = await self._download_file(message.document.file_id)
text = f'{prefix}{caption}'.strip()
await self._send_to_webhook(webhook_url, sender, text, file_data, filename)
elif message.sticker: elif message.sticker:
sticker_text = message.sticker.emoji or '(sticker)' sticker_text = message.sticker.emoji or '(sticker)'
text = f'{prefix}{sticker_text}' text = f'{prefix}{sticker_text}'