fix(youtube): correct extractor-args separator, soften worker recycling, retry video upload
All checks were successful
CI/CD Pipeline / build-and-deploy (push) Successful in 1m53s

EXTRACTOR_ARGS used ':' instead of ';' between player_client and skip
fields, so yt-dlp parsed "android:skip=translated_subs" and "hls" as
bogus player_client values and silently fell back to the web client
only, which fails the n-challenge and returns no formats.

--max-requests=1 fully restarted the gunicorn worker after every
single request, opening a race window that could drop the response
connection right after a successful download; raised to 20 with
jitter to keep the periodic-recycle safety net without doing it on
every request.

reply_video() to Telegram had no retry, so a transient httpx
transport error during upload discarded an already-downloaded video;
added the same 3-attempt retry pattern already used for the
downloader services.
This commit is contained in:
vrubelroman 2026-06-30 20:24:46 +00:00
parent ab703e4bd6
commit 67dba21ff9
3 changed files with 29 additions and 18 deletions

42
bot.py
View file

@ -576,25 +576,37 @@ async def process_queue_item(item: QueueItem):
# Отправляем файл пользователю # Отправляем файл пользователю
await item.status_message.edit_text(get_text(item.locale, 'sending')) await item.status_message.edit_text(get_text(item.locale, 'sending'))
video_file = open(video_path, 'rb')
caption = get_text(item.locale, 'caption', bot_username=TELEGRAM_BOT_USERNAME) caption = get_text(item.locale, 'caption', bot_username=TELEGRAM_BOT_USERNAME)
caption += f"\n\n{item.url}" caption += f"\n\n{item.url}"
# Определяем имя файла для отправки # Определяем имя файла для отправки
video_filename = Path(video_path).name video_filename = Path(video_path).name
# Отправляем как видео со streaming — встроенный плеер Telegram # Отправляем как видео со streaming — встроенный плеер Telegram.
await item.original_message.reply_video( # Загрузка большого файла в Telegram API иногда обрывается транспортной
video=video_file, # ошибкой httpx (ReadError/RemoteProtocolError) — ретраим, открывая файл заново.
filename=video_filename, send_retries = 3
caption=caption, for attempt in range(send_retries):
supports_streaming=True, video_file = open(video_path, 'rb')
read_timeout=600, try:
write_timeout=600, await item.original_message.reply_video(
connect_timeout=60, video=video_file,
pool_timeout=60 filename=video_filename,
) caption=caption,
video_file.close() supports_streaming=True,
read_timeout=600,
write_timeout=600,
connect_timeout=60,
pool_timeout=60
)
break
except Exception as send_error:
if attempt == send_retries - 1:
raise
logger.warning(f"Отправка видео: попытка {attempt + 1}/{send_retries} не удалась: {send_error}")
await asyncio.sleep((attempt + 1) * 2)
finally:
video_file.close()
# Увеличиваем счетчик скачанных видео # Увеличиваем счетчик скачанных видео
increment_downloads() increment_downloads()

View file

@ -6,4 +6,4 @@ RUN pip install --no-cache-dir -r requirements.txt
COPY . . COPY . .
RUN mkdir -p downloads RUN mkdir -p downloads
ENV PYTHONUNBUFFERED=1 ENV PYTHONUNBUFFERED=1
CMD sh -c "gunicorn --workers=1 --timeout=600 --preload --max-requests=1 --bind=0.0.0.0:\${PORT:-5000} app:app" CMD sh -c "gunicorn --workers=1 --timeout=600 --preload --max-requests=20 --max-requests-jitter=5 --bind=0.0.0.0:\${PORT:-5000} app:app"

View file

@ -133,8 +133,7 @@ YTDLP_CMD = 'yt-dlp'
DOWNLOAD_TIMEOUT = 300 DOWNLOAD_TIMEOUT = 300
INFO_TIMEOUT = 60 INFO_TIMEOUT = 60
PLAYER_CLIENTS = 'web,android' EXTRACTOR_ARGS = 'youtube:player_client=web,android;skip=translated_subs,hls'
EXTRACTOR_ARGS = 'youtube:player_client=web,android:skip=translated_subs,hls'
def _build_ytdlp_base_cmd() -> list: def _build_ytdlp_base_cmd() -> list: