Files

155 lines
5.0 KiB
Python

"""Tests for per-language subtitle selection and dict-typed ``Config.languages``.
These tests are pure: no network, no yt-dlp, no DB. They cover the policy
logic that decides which subtitle track to pick for a given
``info`` dict produced by yt-dlp, plus the legacy/back-compat shims that
let older YAML configs still load.
"""
from __future__ import annotations
import pytest
from yt_scraper.config import Config, parse_languages
from yt_scraper.extract import pick_subtitle
# ---------- fixtures ----------
def _track(ext: str = "json3", url: str = "u") -> dict:
return {"ext": ext, "url": url}
def _info(manual: dict | None = None, auto: dict | None = None) -> dict:
return {"subtitles": manual or {}, "automatic_captions": auto or {}}
# ---------- pick_subtitle: mixed per-language mode ----------
def test_mixed_manual_and_auto_per_language() -> None:
"""`es: auto` only takes auto; iterating `es` first means it wins.
With ``{"es": "auto", "en": "manual"}`` the iteration matches `es`
against the auto dict and picks the auto track; for `en` only the
manual dict is consulted and the manual track is returned. The two
policies do NOT cross-pollinate between languages.
"""
info = _info(
manual={"en": [_track(url="man-en")]},
auto={"en": [_track(url="auto-en")], "es": [_track(url="auto-es")]},
)
pick = pick_subtitle(info, {"es": "auto", "en": "manual"}, prefer_manual=True)
assert pick is not None
assert pick.lang == "es"
assert pick.source == "auto"
def test_manual_only_skips_auto_even_when_present() -> None:
info = _info(manual={}, auto={"es": [_track(url="auto")]})
pick = pick_subtitle(info, {"es": "manual"}, prefer_manual=True)
assert pick is None
def test_auto_only_skips_manual_even_when_present() -> None:
info = _info(manual={"es": [_track(url="man")]}, auto={})
pick = pick_subtitle(info, {"es": "auto"}, prefer_manual=True)
assert pick is None
def test_any_defer_to_prefer_manual_default_true() -> None:
"""`any` honours the legacy prefer_manual=True default."""
info = _info(
manual={"es": [_track(url="man")]},
auto={"es": [_track(url="auto")]},
)
pick = pick_subtitle(info, {"es": "any"}, prefer_manual=True)
assert pick is not None
assert pick.source == "manual"
def test_any_defer_to_prefer_manual_default_false() -> None:
info = _info(
manual={"es": [_track(url="man")]},
auto={"es": [_track(url="auto")]},
)
pick = pick_subtitle(info, {"es": "any"}, prefer_manual=False)
assert pick is not None
assert pick.source == "auto"
def test_picks_best_format_within_track() -> None:
info = _info(manual={"en": [_track(ext="ttml", url="t"), _track(ext="json3", url="j")]})
pick = pick_subtitle(info, {"en": "manual"}, prefer_manual=True)
assert pick.url == "j"
# ---------- language normalisation ----------
def test_language_base_match() -> None:
"""`es-419` preference matches the `es` caption track."""
info = _info(manual={"es": [_track(url="u")]})
pick = pick_subtitle(info, {"es-419": "manual"}, prefer_manual=True)
assert pick is not None
assert pick.lang == "es"
# ---------- legacy list input ----------
def test_legacy_list_input_uses_prefer_manual() -> None:
info = _info(
manual={"es": [_track(url="m")]},
auto={"es": [_track(url="a")]},
)
pick = pick_subtitle(info, ["es"], prefer_manual=True)
assert pick.source == "manual"
pick = pick_subtitle(info, ["es"], prefer_manual=False)
assert pick.source == "auto"
def test_unknown_mode_falls_back_to_any() -> None:
info = _info(manual={"es": [_track()]}, auto={"es": [_track()]})
pick = pick_subtitle(info, {"es": "garbage"}, prefer_manual=True)
assert pick is not None
assert pick.source == "manual"
# ---------- parse_languages config helper ----------
def test_parse_languages_dict_pass_through() -> None:
assert parse_languages({"en": "manual", "es": "auto"}, True) == {
"en": "manual", "es": "auto",
}
def test_parse_languages_dict_unknown_mode_to_any() -> None:
assert parse_languages({"en": "garbage"}, True) == {"en": "any"}
def test_parse_languages_legacy_list_to_manual_by_default() -> None:
assert parse_languages(["es", "en"], prefer_manual=True) == {
"es": "manual", "en": "manual",
}
def test_parse_languages_legacy_list_to_auto_when_prefer_manual_false() -> None:
assert parse_languages(["es", "en"], prefer_manual=False) == {
"es": "auto", "en": "auto",
}
def test_parse_languages_none_returns_empty() -> None:
assert parse_languages(None, True) == {}
# ---------- Config dataclass default shape ----------
def test_config_languages_defaults_to_dict() -> None:
cfg = Config()
assert isinstance(cfg.languages, dict)
# "any", not "manual": the default must not exclude auto-generated captions.
assert cfg.languages == {"es": "any", "en": "any"}
def test_config_prefer_manual_defaults_true() -> None:
cfg = Config()
assert cfg.prefer_manual is True