import asyncio import tempfile import os import logging import glob import json import dropbox import sys import io import unicodedata import uuid from urllib.parse import unquote from config import Config # Настройка кодировки для всего приложения sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8') sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding='utf-8') logger = logging.getLogger(__name__) DROPBOX_TOKEN = Config.Token LIMIT = 2 * 1024 * 1024 * 1024 # 2 ГБ async def safe_filename(name: str) -> str: """Создает безопасное имя файла""" # Нормализуем Unicode символы normalized = unicodedata.normalize('NFKD', name) # Убираем акценты и специальные символы, оставляем только ASCII ascii_name = normalized.encode('ascii', 'ignore').decode('ascii') # Заменяем проблемные символы safe_name = "".join(c if c.isalnum() or c in (' ', '-', '_', '.') else '_' for c in ascii_name) # Убираем множественные подчеркивания и обрезаем длину safe_name = '_'.join(filter(None, safe_name.split('_'))) return safe_name[:100] or f"video_{uuid.uuid4().hex[:8]}" async def get_video_info(url: str) -> dict: """Получает информацию о видео через yt-dlp""" try: process = await asyncio.create_subprocess_exec( 'yt-dlp', '--dump-json', '--no-playlist', url, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE ) stdout, stderr = await process.communicate() if process.returncode == 0: result = json.loads(stdout.decode('utf-8', errors='ignore')) logger.info(f"Информация о видео получена: {result.get('title', 'Unknown')}") return result else: error_msg = stderr.decode('utf-8', errors='ignore') logger.warning(f"yt-dlp ошибка: {error_msg}") except Exception as e: logger.error(f"Не удалось получить информацию о видео: {e}") return None async def download_mp4_to_dropbox(url: str) -> tuple[str, dict]: """Скачивает MP4 в среднем качестве БЫСТРО, загружает в Dropbox""" with tempfile.TemporaryDirectory() as temp_dir: output_template = os.path.join(temp_dir, "video.%(ext)s") try: logger.info(f"Запускаю yt-dlp для: {url}") video_info = await get_video_info(url) title = "Unknown_Title" uploader = "Unknown_Uploader" duration = 0 if video_info: title = await safe_filename(video_info.get('title', 'Unknown_Title')) uploader = await safe_filename(video_info.get('uploader', 'Unknown_Uploader')) duration = video_info.get('duration', 0) logger.info(f"Обработано видео: {title}") # ОПТИМИЗИРОВАННЫЕ НАСТРОЙКИ ДЛЯ СКОРОСТИ download_process = await asyncio.create_subprocess_exec( 'yt-dlp', '-f', 'bestvideo[height<=720][filesize<800M]+bestaudio/best[height<=720][filesize<800M]', '--no-playlist', '-o', output_template, '--ignore-errors', '--no-warnings', '--format-sort', 'quality,res:720,size:800M', '--concurrent-fragments', '4', url, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE ) stdout, stderr = await asyncio.wait_for(download_process.communicate(), timeout=600) # Уменьшил таймаут # Остальной код без изменений... if download_process.returncode != 0: error_msg = stderr.decode('utf-8', errors='ignore') if stderr else "Unknown error" logger.error(f"Ошибка скачивания: {error_msg}") raise Exception(f"Ошибка скачивания: {error_msg}") mp4_files = glob.glob(os.path.join(temp_dir, "*.mp4")) if not mp4_files: video_files = glob.glob(os.path.join(temp_dir, "*.*")) video_files = [f for f in video_files if f.lower().endswith(('.mp4', '.mkv', '.avi', '.mov', '.webm'))] if video_files: mp4_files = [video_files[0]] if mp4_files: actual_file = mp4_files[0] size = os.path.getsize(actual_file) if size > LIMIT: raise Exception("Файл превышает лимит 2 ГБ") safe_title = await safe_filename(title) final_filename = f"{safe_title}.mp4" dbx = dropbox.Dropbox(DROPBOX_TOKEN) dropbox_path = f"/{final_filename}" logger.info(f"Загружаем файл в Dropbox: {dropbox_path} (размер: {size / (1024 * 1024):.1f} MB)") with open(actual_file, "rb") as f: file_data = f.read() dbx.files_upload( file_data, dropbox_path, mode=dropbox.files.WriteMode("overwrite") ) shared_link = dbx.sharing_create_shared_link_with_settings(dropbox_path) link = shared_link.url.replace("?dl=0", "?dl=1") metadata = { 'title': title, 'uploader': uploader, 'duration': duration, 'filesize': size, 'quality': 'optimized for speed' } logger.info(f"Успешно загружено в Dropbox: {link}") return link, metadata raise Exception("Видео файл не найден после скачивания") except asyncio.TimeoutError: raise Exception("Таймаут загрузки (10 минут)") except Exception as e: logger.error(f"Общая ошибка: {e}") raise e