fix: prevent VK downloader from blocking queue
This commit is contained in:
parent
5d3cd92a03
commit
a9d1ffc864
3 changed files with 38 additions and 7 deletions
31
bot.py
31
bot.py
|
|
@ -15,9 +15,10 @@ from telegram.request import HTTPXRequest
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
# Таймаут для HTTP запросов
|
# Таймаут для HTTP запросов к downloader-сервисам.
|
||||||
# Все таймауты убраны - видео может качаться и отправляться очень долго
|
# Без ограничений один зависший микросервис навсегда блокирует единственный
|
||||||
HTTP_TIMEOUT = httpx.Timeout(connect=None, read=None, write=None, pool=None)
|
# queue_worker и вся очередь перестает двигаться.
|
||||||
|
HTTP_TIMEOUT = httpx.Timeout(connect=10, read=300, write=30, pool=30)
|
||||||
|
|
||||||
# Таймаут для запроса форматов (не такой критичный, но не должен висеть вечно)
|
# Таймаут для запроса форматов (не такой критичный, но не должен висеть вечно)
|
||||||
FORMATS_TIMEOUT = httpx.Timeout(connect=15, read=30, write=15, pool=15)
|
FORMATS_TIMEOUT = httpx.Timeout(connect=15, read=30, write=15, pool=15)
|
||||||
|
|
@ -110,6 +111,7 @@ TEXTS = {
|
||||||
'caption': "Видео скачано с @{bot_username}",
|
'caption': "Видео скачано с @{bot_username}",
|
||||||
'error': "❌ Произошла ошибка при обработке видео:\n{error}",
|
'error': "❌ Произошла ошибка при обработке видео:\n{error}",
|
||||||
'error_unknown_source': "Пардон, не умеем работать с этим источником",
|
'error_unknown_source': "Пардон, не умеем работать с этим источником",
|
||||||
|
'error_vk_not_video': "❌ Это ссылка VK, но не на видео. Пришлите ссылку вида vk.com/video... или vk.com/clip...",
|
||||||
'error_file_too_large': "❌ Видео слишком большое ({size_mb:.1f} МБ, max = 50)",
|
'error_file_too_large': "❌ Видео слишком большое ({size_mb:.1f} МБ, max = 50)",
|
||||||
'queue_position': "🕐 Ваше видео #{position} в очереди\nВаш запрос очень важен для нас!",
|
'queue_position': "🕐 Ваше видео #{position} в очереди\nВаш запрос очень важен для нас!",
|
||||||
'queue_first': "⬇️ Скачиваю видео...",
|
'queue_first': "⬇️ Скачиваю видео...",
|
||||||
|
|
@ -167,6 +169,7 @@ TEXTS = {
|
||||||
'caption': "Video downloaded via @{bot_username}",
|
'caption': "Video downloaded via @{bot_username}",
|
||||||
'error': "❌ Error processing video:\n{error}",
|
'error': "❌ Error processing video:\n{error}",
|
||||||
'error_unknown_source': "Sorry, this source is not supported",
|
'error_unknown_source': "Sorry, this source is not supported",
|
||||||
|
'error_vk_not_video': "❌ This is a VK link, but not a video. Send a vk.com/video... or vk.com/clip... link.",
|
||||||
'error_file_too_large': "❌ Video is too large ({size_mb:.1f} MB, max = 50)",
|
'error_file_too_large': "❌ Video is too large ({size_mb:.1f} MB, max = 50)",
|
||||||
'queue_position': "🕐 Your video is #{position} in queue\nYour request is very important to us!",
|
'queue_position': "🕐 Your video is #{position} in queue\nYour request is very important to us!",
|
||||||
'queue_first': "⬇️ Downloading video...",
|
'queue_first': "⬇️ Downloading video...",
|
||||||
|
|
@ -396,6 +399,23 @@ def detect_video_source(url: str) -> str:
|
||||||
return 'unknown'
|
return 'unknown'
|
||||||
|
|
||||||
|
|
||||||
|
def is_vk_video_url(url: str) -> bool:
|
||||||
|
"""Проверяет, что VK URL ведёт именно на видео/клип, а не на группу/профиль."""
|
||||||
|
parsed = urlparse(url)
|
||||||
|
domain = parsed.netloc.lower()
|
||||||
|
if not ('vk.com' in domain or 'vk.ru' in domain or 'vkontakte.ru' in domain):
|
||||||
|
return False
|
||||||
|
|
||||||
|
path = parsed.path.lower().strip('/')
|
||||||
|
query = parsed.query.lower()
|
||||||
|
return (
|
||||||
|
path.startswith('video')
|
||||||
|
or path.startswith('clip')
|
||||||
|
or 'z=video' in query
|
||||||
|
or 'z=clip' in query
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def extract_urls_from_text(text: str) -> list[str]:
|
def extract_urls_from_text(text: str) -> list[str]:
|
||||||
"""Извлекает все URL из текста сообщения"""
|
"""Извлекает все URL из текста сообщения"""
|
||||||
url_pattern = r'https?://[^\s<>"{}|\\^`\[\]]+'
|
url_pattern = r'https?://[^\s<>"{}|\\^`\[\]]+'
|
||||||
|
|
@ -1202,6 +1222,11 @@ async def handle_message(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
||||||
await update.message.reply_text(get_text(locale, 'unsupported_source'))
|
await update.message.reply_text(get_text(locale, 'unsupported_source'))
|
||||||
return
|
return
|
||||||
|
|
||||||
|
if source == 'vk' and not is_vk_video_url(url):
|
||||||
|
if chat_type == 'private':
|
||||||
|
await update.message.reply_text(get_text(locale, 'error_vk_not_video'))
|
||||||
|
return
|
||||||
|
|
||||||
# Отправляем сообщение о начале обработки
|
# Отправляем сообщение о начале обработки
|
||||||
status_message = await update.message.reply_text(get_text(locale, 'processing'))
|
status_message = await update.message.reply_text(get_text(locale, 'processing'))
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,8 @@ RUN mkdir -p downloads
|
||||||
|
|
||||||
ENV PYTHONUNBUFFERED=1
|
ENV PYTHONUNBUFFERED=1
|
||||||
|
|
||||||
# Gunicorn: 1 worker (последовательная обработка), без таймаута
|
# Gunicorn: несколько worker-ов и конечные таймауты, чтобы один зависший
|
||||||
# Порт берется из переменной окружения PORT (по умолчанию 5000)
|
# клиент или запрос не блокировал весь VK downloader.
|
||||||
CMD sh -c "gunicorn --workers=1 --timeout=0 --bind=0.0.0.0:${PORT:-5000} app:app"
|
# Порт берется из переменной окружения PORT (по умолчанию 5000).
|
||||||
|
CMD sh -c "gunicorn --workers=${GUNICORN_WORKERS:-2} --threads=${GUNICORN_THREADS:-4} --timeout=${GUNICORN_TIMEOUT:-360} --graceful-timeout=${GUNICORN_GRACEFUL_TIMEOUT:-30} --keep-alive=${GUNICORN_KEEP_ALIVE:-5} --bind=${BIND_HOST:-0.0.0.0}:${PORT:-5000} app:app"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,13 @@ services:
|
||||||
build: .
|
build: .
|
||||||
container_name: vk_downloader_service
|
container_name: vk_downloader_service
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
network_mode: host
|
ports:
|
||||||
|
- "127.0.0.1:5555:5555"
|
||||||
volumes:
|
volumes:
|
||||||
- ./downloads:/app/downloads
|
- ./downloads:/app/downloads
|
||||||
environment:
|
environment:
|
||||||
- PORT=5555
|
- PORT=5555
|
||||||
|
- GUNICORN_WORKERS=2
|
||||||
|
- GUNICORN_THREADS=4
|
||||||
|
- GUNICORN_TIMEOUT=360
|
||||||
|
- GUNICORN_KEEP_ALIVE=5
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue