mirror of
https://github.com/MuRuLOSE/limoka.git
synced 2026-06-17 23:04:17 +02:00
Added and updated repositories 2026-01-10 01:09:56
This commit is contained in:
@@ -24,82 +24,109 @@
|
||||
# meta developer: @hikka_mods
|
||||
# scope: Video2GIF
|
||||
# scope: Video2GIF 0.0.1
|
||||
# requires: moviepy
|
||||
# ---------------------------------------------------------------------------------
|
||||
|
||||
import asyncio
|
||||
import logging
|
||||
import os
|
||||
import subprocess
|
||||
import shutil
|
||||
import tempfile
|
||||
|
||||
from .. import loader, utils
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@loader.tds
|
||||
class Video2GIF(loader.Module):
|
||||
"""Converts video to GIF"""
|
||||
class Video2GIFMod(loader.Module):
|
||||
"""Convert video to high quality GIF"""
|
||||
|
||||
strings = {
|
||||
"name": "Video2GIF",
|
||||
"conversion_success": "🎉 The conversion is completed!",
|
||||
"conversion_error": "❌ An error occurred when converting video to GIF.",
|
||||
"not_video": "⚠️ Please reply to the message with the video or send the video in one message.",
|
||||
"loading": "⏳ Conversion is underway",
|
||||
"success": "✅ GIF created",
|
||||
"error": "❌ Conversion failed",
|
||||
"no_video": "❌ Reply to a video",
|
||||
"no_ffmpeg": "❌ FFmpeg not installed. Install: apt install ffmpeg",
|
||||
"processing": "🔄 Processing video...",
|
||||
"compressing": "📦 Optimizing GIF...",
|
||||
}
|
||||
|
||||
strings_ru = {
|
||||
"conversion_success": "🎉 Преобразование завершено!",
|
||||
"conversion_error": "❌ Произошла ошибка при преобразовании видео в GIF.",
|
||||
"not_video": "⚠️ Пожалуйста, ответьте на сообщение с видео или отправьте видео одним сообщением.",
|
||||
"loading": "⏳ Идет преобразование",
|
||||
"success": "✅ GIF создан",
|
||||
"error": "❌ Ошибка конвертации",
|
||||
"no_video": "❌ Ответьте на видео",
|
||||
"no_ffmpeg": "❌ FFmpeg не установлен. Установите: apt install ffmpeg",
|
||||
"processing": "🔄 Обрабатываю видео...",
|
||||
"compressing": "📦 Оптимизирую GIF...",
|
||||
}
|
||||
|
||||
def __init__(self):
|
||||
self._ffmpeg_check = None
|
||||
|
||||
async def client_ready(self, client, db):
|
||||
self._client = client
|
||||
self._db = db
|
||||
self._check_ffmpeg()
|
||||
|
||||
def _check_ffmpeg(self):
|
||||
self._ffmpeg_check = shutil.which("ffmpeg") is not None
|
||||
|
||||
@loader.command(
|
||||
ru_doc="[reply | в одном сообщении с видео] — конвертирует видео в GIF.",
|
||||
en_doc="[reply | in one message with video] — Converts video to GIF.",
|
||||
ru_doc="[ответ] [fps] [ширина] - конвертировать видео в GIF",
|
||||
en_doc="[reply] [fps] [width] - convert video to GIF",
|
||||
)
|
||||
async def gifc(self, message):
|
||||
video = await self.get_video_from_message(message)
|
||||
"""Convert video to GIF"""
|
||||
if not self._ffmpeg_check:
|
||||
return await utils.answer(message, self.strings["no_ffmpeg"])
|
||||
|
||||
if not video:
|
||||
await utils.answer(message, self.strings["not_video"])
|
||||
return
|
||||
reply = await message.get_reply_message()
|
||||
if not reply or not reply.video:
|
||||
return await utils.answer(message, self.strings["no_video"])
|
||||
|
||||
await utils.answer(message, self.strings["loading"])
|
||||
video_path = await self.client.download_media(video)
|
||||
gif_path = f"{os.path.splitext(video_path)[0]}.gif"
|
||||
args = utils.get_args_raw(message).split()
|
||||
fps = 15 if len(args) < 1 else min(int(args[0]), 30)
|
||||
width = 480 if len(args) < 2 else min(int(args[1]), 1024)
|
||||
|
||||
msg = await utils.answer(message, self.strings["processing"])
|
||||
|
||||
try:
|
||||
self.convert_video_to_gif(video_path, gif_path)
|
||||
await message.client.send_file(
|
||||
message.chat_id, gif_path, caption=self.strings["conversion_success"]
|
||||
gif_path = await self._convert_to_gif(reply, fps, width)
|
||||
|
||||
await self._client.send_file(
|
||||
message.chat_id,
|
||||
gif_path,
|
||||
caption=self.strings["success"],
|
||||
reply_to=reply.id,
|
||||
)
|
||||
except Exception as e:
|
||||
await utils.answer(message, self.strings["conversion_error"])
|
||||
print(f"Error during conversion: {e}")
|
||||
finally:
|
||||
self.cleanup_temp_files(video_path, gif_path)
|
||||
|
||||
async def get_video_from_message(self, message):
|
||||
"""Получает видео из сообщения."""
|
||||
if reply := await message.get_reply_message():
|
||||
return reply.video
|
||||
return message.video
|
||||
os.remove(gif_path)
|
||||
await msg.delete()
|
||||
|
||||
def convert_video_to_gif(self, video_path: str, gif_path: str) -> None:
|
||||
"""Конвертирует видео в GIF с улучшенными параметрами."""
|
||||
command = [
|
||||
"ffmpeg",
|
||||
"-i",
|
||||
video_path,
|
||||
"-vf",
|
||||
"fps=30,scale=640:-1:flags=lanczos",
|
||||
"-c:v",
|
||||
"gif",
|
||||
gif_path,
|
||||
]
|
||||
subprocess.run(command, check=True)
|
||||
except Exception:
|
||||
await utils.answer(message, self.strings["error"])
|
||||
|
||||
def cleanup_temp_files(self, video_path: str, gif_path: str) -> None:
|
||||
"""Удаляет временные файлы."""
|
||||
for temp_file in [video_path, gif_path]:
|
||||
if os.path.exists(temp_file):
|
||||
os.remove(temp_file)
|
||||
async def _convert_to_gif(self, reply, fps: int, width: int) -> str:
|
||||
"""Convert video to optimized GIF"""
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
video_path = os.path.join(tmpdir, "video.mp4")
|
||||
gif_path = os.path.join(tmpdir, "output.gif")
|
||||
|
||||
await reply.download_media(video_path)
|
||||
|
||||
cmd = [
|
||||
"ffmpeg",
|
||||
"-i",
|
||||
video_path,
|
||||
"-vf",
|
||||
f"fps={fps},scale={width}:-1:flags=lanczos",
|
||||
"-lavfi",
|
||||
"[0:v]split[s0][s1];[s0]palettegen[p];[s1][p]paletteuse",
|
||||
"-y",
|
||||
gif_path,
|
||||
]
|
||||
|
||||
proc = await asyncio.create_subprocess_exec(*cmd)
|
||||
await proc.communicate()
|
||||
|
||||
return gif_path
|
||||
|
||||
Reference in New Issue
Block a user