yt-dlp-gui/tests/conftest.py
loop-uh 7564df4f95 YouTube: сторож прямого пути — при блокировке по SNI сразу через egress, а не «Проверяем…» навсегда
13.10.2026 провайдер начал молча глотать TLS с именем youtube.com. Ошибки
при этом нет: yt-dlp ждёт по 30 с на запрос и повторяет, /api/info не
отвечал дольше 90 с, запасной путь через egress включался только по тексту
бана. YouTube лежал около 16 часов, /readyz всё это время был «ок».

- downloader.yt_direct_ok: короткий запрос HEAD /generate_204 к
  www.youtube.com с YTG_YT_SOURCE_IP (таймаут 4 с, отказ — две неудачи
  подряд), результат кэшируется, обновляет его уборщик. Пока путь молчит,
  разбор ролика и списка идут через egress сразу; путь загрузки выбирает
  сервер (yt_egress_fallback), а не признак от клиента.
- В журнале строка на смену состояния ([youtube] напрямую не отвечает /
  снова отвечает); /readyz остаётся «ок», но несёт warnings и состояние в
  метриках. Выключатель: YTG_YT_REACH_CHECK=0.
- Пустой ответ через egress в режиме обхода повторяется один раз: туннель
  бывает медленным.
- Туннель пула для разбора через egress выбирается живой и случайный
  (egress_pool_proxy), а не первый: первый оказался «живым трупом» (ssh
  держит связь, данные не идут), и загрузка висела на подготовке при 31
  рабочем туннеле.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 19:11:36 +03:00

81 lines
3.4 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""Общая подготовка тестов.
ВАЖНО: переменные окружения выставляются ДО импорта config и app.
`import app` на уровне модуля создаёт менеджер загрузок, который при старте
подметает каталог загрузок. Если не перенаправить пути заранее, прогон
тестов удалит файлы работающего сервиса — так уже случалось.
"""
import os
import sys
import tempfile
from pathlib import Path
_TMP = Path(tempfile.mkdtemp(prefix="ytg-tests-"))
os.environ["YTG_DOWNLOAD_DIR"] = str(_TMP / "downloads")
os.environ["YTG_DATA_DIR"] = str(_TMP / "data")
os.environ["YTG_TRUST_PROXY"] = "0"
os.environ["YTG_JANITOR_INTERVAL_SEC"] = "3600" # уборщик не мешает тестам
os.environ["YTG_MAX_CONCURRENT"] = "2"
os.environ["YTG_QUEUE_MAX"] = "4"
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
import pytest # noqa: E402
import config # noqa: E402
@pytest.fixture(scope="session", autouse=True)
def _guard_paths():
"""Страховка: тесты не должны работать с боевыми каталогами."""
assert str(config.DOWNLOAD_DIR).startswith(str(_TMP)), config.DOWNLOAD_DIR
assert str(config.DATA_DIR).startswith(str(_TMP)), config.DATA_DIR
yield
@pytest.fixture(autouse=True)
def _youtube_direct_ok(monkeypatch):
"""Тесты не ходят в сеть: «YouTube напрямую отвечает» — заглушка, состояние
проверки у каждого теста своё."""
import downloader as dl
monkeypatch.setattr(dl, "_yt_direct_check", lambda: True)
monkeypatch.setattr(dl, "_yt_reach", dl._YtReach())
@pytest.fixture
def client():
import app as app_module
app_module.app.config["TESTING"] = True
app_module._hits.clear() # чистый rate limit на каждый тест
with app_module.app.test_client() as c:
yield c
@pytest.fixture
def fake_info():
"""Метаданные в том виде, в каком их отдают экстракторы.
Специально смешаны три случая: кодеки известны, кодеков нет совсем
("none") и кодеки неизвестны (None) — последний ломался.
"""
return {
"id": "abc123",
"title": "Тестовый ролик",
"uploader": "Автор",
"duration": 120,
"thumbnail": "https://example.com/t.jpg",
"webpage_url": "https://example.com/watch?v=abc123",
"extractor_key": "Test",
"formats": [
{"format_id": "137", "ext": "mp4", "vcodec": "avc1.640028",
"acodec": "none", "height": 1080, "tbr": 4000},
{"format_id": "140", "ext": "m4a", "vcodec": "none",
"acodec": "mp4a.40.2", "height": None, "abr": 128},
{"format_id": "hls-480", "ext": "mp4", "vcodec": None,
"acodec": None, "height": 480, "tbr": 1500},
{"format_id": "mp4-low", "ext": "mp4", "vcodec": None,
"acodec": None, "height": None, "tbr": 500},
{"format_id": "sb0", "ext": "mhtml", "vcodec": "none",
"acodec": "none", "format_note": "storyboard"},
],
}