# meta developer: @HoroshoVse # requires: yt-dlp from .. import loader, utils import os import asyncio import tempfile import json import shutil import sys @loader.tds class YIDownloaderMod(loader.Module): """Универсальный скачиватель видео""" strings = {"name": "YI downloader"} def __init__(self): self.config = loader.ModuleConfig( loader.ConfigValue( "max_size", 50, "Максимальный размер видео в МБ", validator=loader.validators.Integer() ) ) async def dlcmd(self, message): """<ссылка> - Скачать видео из TikTok/Instagram/YouTube/VK""" args = utils.get_args_raw(message) if not args: await utils.answer(message, "❌ Укажи ссылку на видео\n\n" "Поддерживаемые сайты:\n" "• TikTok: https://vm.tiktok.com/...\n" "• Instagram: https://www.instagram.com/p/...\n" "• YouTube: https://youtu.be/...\n" "• VK: https://vk.com/video...\n" "• Twitter/X: https://twitter.com/...\n" "• Reddit: https://reddit.com/..." ) return source = self.detect_source(args) if not source: await utils.answer(message, "❌ Неподдерживаемая ссылка") return status = await utils.answer(message, f"📥 Скачиваю с {source}...") try: video_path, info = await self.download_video(args, source) if not video_path: err_msg = info if isinstance(info, str) else "Неизвестная ошибка" await status.edit(f"❌ Не удалось скачать видео:\n{utils.escape_html(err_msg)}") return caption = f"📹 Видео с {source}\n\n" if info.get('title'): caption += f"📝 {info['title']}\n" if info.get('author'): caption += f"👤 {info['author']}\n" if info.get('duration'): caption += f"⏱ {info['duration']}\n" caption += f"\n🔗 Оригинал" try: await message.client.send_file( message.chat_id, video_path, caption=caption, reply_to=message.reply_to_msg_id or message.id ) await status.delete() except Exception as e: await status.edit(f"❌ Ошибка отправки в Telegram: {str(e)}") finally: if os.path.exists(video_path): os.remove(video_path) except Exception as e: try: await status.edit(f"❌ Ошибка: {str(e)}") except: pass def detect_source(self, url): url = url.lower() sources = { 'tiktok': ['tiktok.com', 'vm.tiktok.com'], 'instagram': ['instagram.com', 'instagr.am'], 'youtube': ['youtube.com', 'youtu.be', 'm.youtube.com'], 'vk': ['vk.com', 'vkontakte.ru'], 'twitter': ['twitter.com', 'x.com'], 'reddit': ['reddit.com', 'redd.it'], 'facebook': ['facebook.com', 'fb.com'], 'pinterest': ['pinterest.com', 'pin.it'] } for source, domains in sources.items(): for domain in domains: if domain in url: return source.capitalize() return None async def download_video(self, url, source): try: with tempfile.TemporaryDirectory() as temp_dir: output_template = os.path.join(temp_dir, '%(title)s.%(ext)s') cmd = [ sys.executable, '-m', 'yt_dlp', '-f', 'bestvideo+bestaudio/best', '--merge-output-format', 'mp4', '--max-filesize', f"{self.config['max_size']}M", '-o', output_template, url ] if source.lower() == 'tiktok': cmd.extend(['--cookies', 'tiktok_cookies.txt', '--downloader', 'urllib']) elif source.lower() == 'instagram': cmd.extend(['--cookies', 'instagram_cookies.txt', '--downloader', 'urllib']) process = await asyncio.create_subprocess_exec( *cmd, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE ) stdout, stderr = await process.communicate() if process.returncode != 0: return None, stderr.decode('utf-8', errors='ignore') for file in os.listdir(temp_dir): file_path = os.path.join(temp_dir, file) if os.path.isfile(file_path): final_path = os.path.join(tempfile.gettempdir(), file) shutil.copy2(file_path, final_path) info = await self.get_video_info(url, source) return final_path, info return None, "Файл не найден после скачивания" except Exception as e: return None, str(e) async def get_video_info(self, url, source): try: cmd = [sys.executable, '-m', 'yt_dlp', '--dump-json', '--no-playlist', url] if source.lower() in ['tiktok', 'instagram']: cmd.extend(['--downloader', 'urllib']) process = await asyncio.create_subprocess_exec( *cmd, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE ) stdout, stderr = await process.communicate() if process.returncode == 0: data = json.loads(stdout.decode('utf-8')) duration = data.get('duration', 0) minutes = duration // 60 seconds = duration % 60 return { 'title': data.get('title', ''), 'author': data.get('uploader', data.get('channel', '')), 'duration': f"{minutes}:{seconds:02d}" if duration else '' } return {} except: return {}