diff --git a/universal.py b/universal.py index 02c9185..580cd23 100644 --- a/universal.py +++ b/universal.py @@ -1,12 +1,14 @@ # meta developer: @origsama +# requires: yt-dlp from .. import loader, utils import requests import re import os -import subprocess +import asyncio import tempfile import urllib.parse import json +import shutil @loader.tds class SocialDownloaderMod(loader.Module): @@ -39,7 +41,6 @@ class SocialDownloaderMod(loader.Module): ) return - # Определяем источник source = self.detect_source(args) if not source: await utils.answer(message, "❌ Неподдерживаемая ссылка") @@ -48,14 +49,12 @@ class SocialDownloaderMod(loader.Module): status = await utils.answer(message, f"📥 Скачиваю с {source}...") try: - # Скачиваем видео video_path, info = await self.download_video(args, source) if not video_path: await status.edit("❌ Не удалось скачать видео") return - # Формируем описание caption = f"📹 Видео с {source}\n\n" if info.get('title'): caption += f"📝 {info['title']}\n" @@ -66,7 +65,6 @@ class SocialDownloaderMod(loader.Module): caption += f"\n🔗 Оригинал" - # Отправляем await status.delete() await message.client.send_file( @@ -76,16 +74,13 @@ class SocialDownloaderMod(loader.Module): reply_to=message.reply_to_msg_id or message.id ) - # Чистим os.remove(video_path) except Exception as e: await status.edit(f"❌ Ошибка: {str(e)}") def detect_source(self, url): - """Определяет источник по ссылке""" url = url.lower() - sources = { 'tiktok': ['tiktok.com', 'vm.tiktok.com'], 'instagram': ['instagram.com', 'instagr.am'], @@ -96,54 +91,45 @@ class SocialDownloaderMod(loader.Module): '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): - """Скачивает видео через yt-dlp""" try: - # Создаем временную папку with tempfile.TemporaryDirectory() as temp_dir: output_template = os.path.join(temp_dir, '%(title)s.%(ext)s') - # Формируем команду cmd = [ 'yt-dlp', - '-f', 'best[filesize<50M]', # Лучшее качество до 50 МБ + '-f', 'best[filesize<50M]', '--max-filesize', f"{self.config['max_size']}M", '-o', output_template, url ] - # Добавляем специфичные опции if source.lower() == 'tiktok': - cmd.extend(['--cookies', 'tiktok_cookies.txt']) # Опционально + cmd.extend(['--cookies', 'tiktok_cookies.txt']) elif source.lower() == 'instagram': cmd.extend(['--cookies', 'instagram_cookies.txt']) - # Запускаем - result = subprocess.run(cmd, capture_output=True, text=True) + process = await asyncio.create_subprocess_exec( + *cmd, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE + ) + stdout, stderr = await process.communicate() - if result.returncode != 0: + if process.returncode != 0: return None, {} - # Ищем скачанный файл for file in os.listdir(temp_dir): file_path = os.path.join(temp_dir, file) if os.path.isfile(file_path): - # Копируем в постоянное место - import shutil final_path = os.path.join(tempfile.gettempdir(), file) shutil.copy2(file_path, final_path) - # Получаем информацию info = await self.get_video_info(url) - return final_path, info return None, {} @@ -153,19 +139,15 @@ class SocialDownloaderMod(loader.Module): return None, {} async def get_video_info(self, url): - """Получает информацию о видео""" try: - cmd = [ - 'yt-dlp', - '--dump-json', - '--no-playlist', - url - ] + cmd = ['yt-dlp', '--dump-json', '--no-playlist', url] + process = await asyncio.create_subprocess_exec( + *cmd, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE + ) + stdout, stderr = await process.communicate() - result = subprocess.run(cmd, capture_output=True, text=True) - - if result.returncode == 0: - data = json.loads(result.stdout) + if process.returncode == 0: + data = json.loads(stdout.decode('utf-8')) duration = data.get('duration', 0) minutes = duration // 60 @@ -176,7 +158,6 @@ class SocialDownloaderMod(loader.Module): 'author': data.get('uploader', data.get('channel', '')), 'duration': f"{minutes}:{seconds:02d}" if duration else '' } - return {} except: return {} @@ -187,7 +168,6 @@ class SocialDownloaderMod(loader.Module): if not args: await utils.answer(message, "❌ Укажи ссылку на TikTok") return - await self.dlcmd(message) async def instacmd(self, message): @@ -196,7 +176,6 @@ class SocialDownloaderMod(loader.Module): if not args: await utils.answer(message, "❌ Укажи ссылку на Instagram") return - await self.dlcmd(message) async def ytcmd(self, message): @@ -206,7 +185,6 @@ class SocialDownloaderMod(loader.Module): await utils.answer(message, "❌ Укажи ссылку на YouTube") return - # Разбираем качество если указано parts = args.split() url = parts[0] quality = parts[1] if len(parts) > 1 else 'best' @@ -225,16 +203,18 @@ class SocialDownloaderMod(loader.Module): url ] - result = subprocess.run(cmd, capture_output=True, text=True) + process = await asyncio.create_subprocess_exec( + *cmd, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE + ) + stdout, stderr = await process.communicate() - if result.returncode != 0: + if process.returncode != 0: await utils.answer(message, "❌ Ошибка скачивания") return for file in os.listdir(temp_dir): file_path = os.path.join(temp_dir, file) if os.path.isfile(file_path): - import shutil final_path = os.path.join(tempfile.gettempdir(), file) shutil.copy2(file_path, final_path) @@ -267,5 +247,4 @@ class SocialDownloaderMod(loader.Module): if not args: await utils.answer(message, "❌ Укажи ссылку на VK видео") return - await self.dlcmd(message) \ No newline at end of file