179 lines
6.7 KiB
Python
179 lines
6.7 KiB
Python
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
|