lyrics --all-languages flag to opt out of the ru filter

This commit is contained in:
Andrew Pimenov 2026-08-16 19:50:35 +03:00
parent 2344889418
commit 1c0f6928bf
4 changed files with 62 additions and 18 deletions

View file

@ -71,16 +71,19 @@ def cmd_lyrics(args: argparse.Namespace) -> None:
if not tracks_cache:
raise SystemExit("Кэш треков пуст. Сначала запустите: python -m sort_ym fetch")
lang_cache: dict[str, str | None] = {}
if not args.all_languages:
# Тот же вызов, что в cmd_report: инкрементальный, на заполненном кэше почти no-op,
# но без него RU-гейт опирался бы только на алфавитную эвристику.
ids_needing_lang = [str(t["id"]) for t in tracks_cache.values() if t["lyrics_available"]]
lang_cache = language.fetch_api_languages(client, ids_needing_lang, cfg.cache_dir, cfg.lyrics_request_delay)
ids = lyrics.ru_lyric_track_ids(tracks_cache, lang_cache)
ids = lyrics.ru_lyric_track_ids(tracks_cache, lang_cache, all_languages=args.all_languages)
if args.limit is not None:
ids = ids[: args.limit]
print(f"RU-треков с текстом: {len(ids)}")
label = "треков" if args.all_languages else "RU-треков"
print(f"{label} с текстом: {len(ids)}")
lyrics.fetch_lyrics_text(client, ids, cfg.cache_dir, cfg.lyrics_request_delay)
@ -263,6 +266,11 @@ def main(argv: list[str] | None = None) -> None:
p_lyrics = sub.add_parser("lyrics", help="загрузить тексты песен RU-треков в cache/lyrics_text.json")
p_lyrics.add_argument("--limit", type=int, default=None, help="ограничить количество треков (для пробного запуска)")
p_lyrics.add_argument(
"--all-languages",
action="store_true",
help="не фильтровать по языку - загрузить тексты всех треков с лирикой, а не только RU",
)
p_analyze = sub.add_parser(
"analyze",

View file

@ -13,13 +13,19 @@ from .ymclient import with_retries
LYRICS_TEXT_CACHE_FILE = "lyrics_text.json"
def ru_lyric_track_ids(tracks_cache: dict[str, dict], lang_cache: dict[str, str | None]) -> list[str]:
"""Числовые id RU-треков, у которых вообще есть текст.
def ru_lyric_track_ids(
tracks_cache: dict[str, dict],
lang_cache: dict[str, str | None],
all_languages: bool = False,
) -> list[str]:
"""Числовые id треков, у которых вообще есть текст.
Гейт двойной: lyrics_available (иначе track_supplement заведомо вернёт запись без lyrics)
и RU - анализ INT-лирики вне скоупа фичи. Дедупликация нужна потому, что один и тот же
трек может быть лайкнут с разных альбомов: ключи tracks_cache при этом разные, а числовой
id (и, соответственно, ключ кэша текстов) - один.
По умолчанию - только RU: на незнакомом языке текст песни несёт для языковой модели меньше
сигнала, чем разбор трека, который слушатель понимает дословно. all_languages=True снимает
языковой фильтр, оставляя только lyrics_available (иначе track_supplement заведомо вернёт
запись без lyrics). Дедупликация нужна потому, что один и тот же трек может быть лайкнут с
разных альбомов: ключи tracks_cache при этом разные, а числовой id (и, соответственно, ключ
кэша текстов) - один.
"""
ids: list[str] = []
seen: set[str] = set()
@ -29,8 +35,10 @@ def ru_lyric_track_ids(tracks_cache: dict[str, dict], lang_cache: dict[str, str
tid = str(t["id"])
if tid in seen:
continue
if not all_languages:
lang = language.detect_language(t["title"], t["artists"], t["genre_raw"], lang_cache.get(tid))
if lang == "RU":
if lang != "RU":
continue
seen.add(tid)
ids.append(tid)
return ids

View file

@ -20,11 +20,22 @@ def test_apply_without_yes_fails_regardless_of_source():
def test_lyrics_command_is_wired(monkeypatch):
called = {}
monkeypatch.setattr(cli, "cmd_lyrics", lambda args: called.update(limit=args.limit))
monkeypatch.setattr(
cli, "cmd_lyrics", lambda args: called.update(limit=args.limit, all_languages=args.all_languages)
)
cli.main(["lyrics", "--limit", "5"])
assert called == {"limit": 5}
assert called == {"limit": 5, "all_languages": False}
def test_lyrics_command_all_languages_flag(monkeypatch):
called = {}
monkeypatch.setattr(cli, "cmd_lyrics", lambda args: called.update(all_languages=args.all_languages))
cli.main(["lyrics", "--all-languages"])
assert called == {"all_languages": True}
def test_analyze_command_is_wired(monkeypatch):

View file

@ -55,6 +55,23 @@ def test_ru_lyric_track_ids_deduplicates_same_numeric_id():
assert lyrics.ru_lyric_track_ids(tracks_cache, lang_cache={}) == ["100"]
def test_ru_lyric_track_ids_all_languages_includes_int():
tracks_cache = {
"100:10": track(100, title="Тоска", artists=["Гражданская Оборона"]),
"200:10": track(200, title="Come as You Are", artists=["Nirvana"]),
}
ids = lyrics.ru_lyric_track_ids(tracks_cache, lang_cache={}, all_languages=True)
assert ids == ["100", "200"]
def test_ru_lyric_track_ids_all_languages_still_gates_on_lyrics_available():
tracks_cache = {"100:10": track(100, lyrics_available=False)}
assert lyrics.ru_lyric_track_ids(tracks_cache, lang_cache={}, all_languages=True) == []
class FakeLyrics:
def __init__(self, full_lyrics):
self.full_lyrics = full_lyrics