вк вынесен в отдельный сервис

This commit is contained in:
vrubelroman 2025-12-10 16:14:26 +03:00
parent 39bf9d1933
commit d05fc6f522
9 changed files with 348 additions and 63 deletions

113
bot.py
View file

@ -9,6 +9,7 @@ from urllib.parse import urlparse
from datetime import datetime
import yt_dlp
import httpx
from telegram import Update
from telegram.ext import Application, MessageHandler, filters, ContextTypes, CommandHandler
@ -22,6 +23,8 @@ logger = logging.getLogger(__name__)
# Токен бота и имя бота из переменных окружения
TELEGRAM_BOT_TOKEN = os.getenv('TELEGRAM_BOT_TOKEN')
TELEGRAM_BOT_USERNAME = os.getenv('TELEGRAM_BOT_USERNAME', 'vrubelVideoDownload_bot')
# URL VK сервиса для скачивания видео
VK_DOWNLOADER_URL = os.getenv('VK_DOWNLOADER_URL', 'http://localhost:5555')
# Базовая директория проекта (абсолютный путь), чтобы не зависеть от рабочей директории процесса
BASE_DIR = Path(__file__).resolve().parent
@ -327,76 +330,64 @@ async def download_instagram_video(url: str, chat_id: int, max_retries: int = 3)
async def download_vk_video(url: str, chat_id: int, max_retries: int = 3) -> str:
"""Скачивает видео с VK"""
vk_user_agent = 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'
"""Скачивает видео с VK через внешний сервис"""
logger.info(f"VK: отправка запроса на внешний сервис {VK_DOWNLOADER_URL}")
last_error = None
for attempt in range(max_retries):
try:
# Получаем информацию о видео
ydl_opts_info = {
'quiet': False,
'no_warnings': False,
'user_agent': vk_user_agent,
'socket_timeout': 60, # Увеличенный таймаут для VK
'extractor_args': {
'vk': {},
},
'http_headers': {
'User-Agent': vk_user_agent,
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'Accept-Language': 'ru-RU,ru;q=0.9',
'Referer': 'https://vk.com/',
'Connection': 'keep-alive',
},
# Пробуем использовать более надежные настройки SSL
'nocheckcertificate': False,
}
with yt_dlp.YoutubeDL(ydl_opts_info) as ydl:
info = ydl.extract_info(url, download=False)
video_title = info.get('title', 'vk_video')
logger.info(f"VK: получена информация о видео: {video_title}")
# Скачиваем видео
ydl_opts_download = {
'format': 'best',
'outtmpl': _safe_filename(video_title, chat_id),
'quiet': False,
'no_warnings': False,
'user_agent': vk_user_agent,
'socket_timeout': 60, # Увеличенный таймаут для VK
'extractor_args': {
'vk': {},
},
'http_headers': {
'User-Agent': vk_user_agent,
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'Accept-Language': 'ru-RU,ru;q=0.9',
'Referer': 'https://vk.com/',
},
}
logger.info(f"VK: начинаем скачивание (попытка {attempt + 1}/{max_retries})")
with yt_dlp.YoutubeDL(ydl_opts_download) as ydl:
loop = asyncio.get_event_loop()
await loop.run_in_executor(None, lambda: ydl.download([url]))
# Находим скачанный файл
downloaded_files = list(DOWNLOADS_DIR.glob(f'{chat_id}_*'))
if downloaded_files:
downloaded_files.sort(key=lambda x: x.stat().st_mtime, reverse=True)
return str(downloaded_files[0])
else:
raise Exception("Файл не был найден после скачивания")
async with httpx.AsyncClient(timeout=600.0) as client: # Увеличенный таймаут для VK
# Отправляем запрос на VK сервис
response = await client.post(
f"{VK_DOWNLOADER_URL}/download/stream",
json={"url": url},
headers={"Content-Type": "application/json"}
)
if response.status_code != 200:
error_text = response.text
try:
error_json = response.json()
error_text = error_json.get('error', error_text)
except:
pass
raise Exception(f"VK сервис вернул ошибку {response.status_code}: {error_text}")
# Сохраняем видео во временный файл
video_data = response.content
video_ext = 'mp4' # По умолчанию mp4
# Пробуем определить расширение из заголовков
content_type = response.headers.get('Content-Type', '')
if 'video/' in content_type:
video_ext = content_type.split('/')[-1].split(';')[0]
# Получаем имя файла из заголовка или создаем случайное
filename = response.headers.get('Content-Disposition', '')
if filename and 'filename=' in filename:
video_filename = filename.split('filename=')[1].strip('"\'')
else:
video_filename = f'{chat_id}_vk_video.{video_ext}'
# Сохраняем файл
video_path = DOWNLOADS_DIR / video_filename
with open(video_path, 'wb') as f:
f.write(video_data)
logger.info(f"VK: видео скачано через внешний сервис: {video_path}")
return str(video_path)
except httpx.TimeoutException:
last_error = Exception(f"Таймаут при запросе к VK сервису (попытка {attempt + 1}/{max_retries})")
logger.warning(f"VK: таймаут при запросе к сервису: {last_error}")
except Exception as e:
last_error = e
logger.warning(f"VK: попытка {attempt + 1}/{max_retries} не удалась: {e}")
if attempt < max_retries - 1:
await asyncio.sleep((attempt + 1) * 2)
if attempt < max_retries - 1:
await asyncio.sleep((attempt + 1) * 2)
raise last_error or Exception("Неизвестная ошибка при скачивании с VK")
raise last_error or Exception("Неизвестная ошибка при скачивании с VK через внешний сервис")
async def download_video(url: str, chat_id: int, max_retries: int = 3) -> str: