import asyncio import tempfile import os import logging import glob import json import dropbox import sys import io import unicodedata import uuid 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