Aggregates liked tracks into top artists, genres, decades and top albums with fixed size ceilings instead of a per-track dump, so it can be pasted into an LLM chat regardless of library size. Fully offline - reads only the existing cache, no Client/network needed. Adds album_title/year to the track cache (one-time re-fetch) to support the decade and album views.
12 KiB
Sort YM
Раскладывает лайкнутые треки Яндекс.Музыки по плейлистам вида Жанр — RU / Жанр — INT.
$ python -m sort_ym report
Распределение по плейлистам:
Рок — INT: 143
Рок — RU: 287
Рэп — RU: 512
...
Отчёт сохранён: out/report.csv
Установка
Требования: Python 3.11+ (используется tomllib из стандартной библиотеки), Windows/Linux/macOS, доступ в интернет.
python -m venv .venv
.venv\Scripts\pip install -r requirements.txt
Для разработки (тесты):
.venv\Scripts\pip install -r requirements-dev.txt
Что делает
.venv\Scripts\python -m sort_ym auth # войти в аккаунт Яндекса через device-flow и сохранить токен в .token
.venv\Scripts\python -m sort_ym fetch # скачать лайкнутые треки (cache/tracks.json) и жанры их артистов (cache/artist_genres.json); аккаунт не меняет
.venv\Scripts\python -m sort_ym report # посчитать жанр и язык каждого трека, сохранить out/report.csv (аккаунт не меняет)
.venv\Scripts\python -m sort_ym apply --limit 20 --yes # пробный запуск: создать плейлисты и добавить в них первые 20 треков
.venv\Scripts\python -m sort_ym apply --yes # создать плейлисты и добавить в них все треки (меняет аккаунт)
.venv\Scripts\python -m sort_ym digest # сводка библиотеки (топ-исполнители, жанры) в out/digest.md - чтобы вставить в чат с LLM; сеть не используется
При повторных запусках fetch докачивает только новые треки, а apply не дублирует уже добавленные треки.
Полная очистка кэша
Если требуется начать с чистого листа (переполучить все данные с нуля), удалите папку cache/:
Remove-Item -Recurse -Force cache
Кэш состоит из четырёх файлов:
cache/tracks.json— список лайкнутых треков и их метаданные (включая id артистов);cache/artist_genres.json— жанровые теги каждого артиста (может быть несколько на артиста);cache/genre_catalog.json— снимок дерева жанров Яндекса на момент последнего запуска;cache/lyrics_lang.json— определённые языки текстов для каждого трека.
Удаление cache/ заставит fetch и report переполучить всё с нуля при следующем запуске.
Также можно удалить сгенерированные отчёты (out/report.csv, out/digest.md):
Remove-Item -Recurse -Force out
Это просто результаты анализа, их удаление не повлияет на следующий запуск report/digest.
Важно: файл .token (OAuth-токен) не входит в кэш и не удаляется при очистке cache/. Очистка кэша вас не разлогирует. Если нужна повторная авторизация, удалите .token отдельно:
Remove-Item .token
Как определяется жанр и язык
-
Жанр определяется на под-жанровом уровне (Рок, Панк, Инди, Альтернатива, Метал, ..., не только 11 крупных групп), комбинируя два сигнала:
- Жанр альбома трека (
genre_raw) — сигнал по конкретному треку. - Жанры артиста (
Artist.genres, может быть несколько тегов на артиста, точнее одного альбомного жанра) — сигнал по исполнителю в целом.
Если сигналы совпадают — берётся под-жанр как есть. При конфликте: если альбомный тег общий ("зонтичный", вроде
rock/alternative/pop— типичный дефолт Яндекса низкой уверенности) — побеждает жанр артиста; если альбомный тег точный/специфичный (numetal,dnb,shanson) — побеждает он, потому что это, скорее всего, реальный эксперимент артиста в другом жанре, а не шум. Так один и тот же артист с разными по духу треками (например, рОк-трек и панк-трек) раскладывается по разным плейлистам, а не весь скопом по одному "основному" жанру.Под-жанровые группы меньше
small_group_min(по умолчанию 12 треков,config.toml) схлопываются в родительскую крупную корзину (Поп, Рок, Метал, Рэп, Электроника, Джаз и блюз, Классика, Фолк и world, Инди и альтернатива, Саундтреки, Разное), чтобы не плодить плейлисты на 1-2 трека. Реализация —sort_ym/genres.py(classify_track,ROOT_BUCKET) — таблица корней легко дополняется, если Яндекс вернёт незнакомый корневой жанр. - Жанр альбома трека (
-
Язык — гибрид:
- Если у трека есть текст песни — реальный язык из API (
ru→ RU, иначе → INT). - Иначе — подсказка по жанру (
rusrap,shanson,bard, ...). - Иначе — по алфавиту названия/исполнителя (кириллица/латиница).
- Если ничего не сработало — трек попадает в
... — Не определено.
- Если у трека есть текст песни — реальный язык из API (
После report в консоли также печатается список нераспознанных жанров (попавших в «Разное»),
чтобы можно было дополнить таблицу в sort_ym/genres.py.
Дайджест для LLM
Полный построчный список из ~2000 треков LLM переварить не может так же качественно, как
компактную сводку — поэтому digest не выгружает треки построчно, а агрегирует библиотеку в
несколько разделов с жёстко ограниченным размером: не более 11 строк по крупным жанрам, ~34 по
под-жанрам, 3 по языкам, ~13 по десятилетиям, а топ-исполнителей и топ-альбомов ограничен
config.toml, а не размером библиотеки. Всё, что не попало в топ, схлопывается в одну фразу с
суммарными числами. Итоговый файл — не больше пары сотен строк независимо от того, 500 треков в
библиотеке или 10000.
Команда полностью офлайн: она не обращается к сети и не требует .token, читает только уже
загруженный cache/. Нужно, чтобы report был запущен хотя бы раз (для cache/genre_catalog.json).
$ python -m sort_ym digest
Жанры:
Инди и альтернатива: 680
Рок: 590
...
Дайджест сохранён: out/digest.md
Пример out/digest.md (сокращённо):
## Жанры (крупные корзины)
- Инди и альтернатива — 680 (34%): INT 571 / RU 108
- Рок — 590 (29%): INT 518 / RU 72
...
## Десятилетия
- 2010-е — 620 (31%): Рок 280, Инди и альтернатива 200, Метал 90
...
## Топ-40 исполнителей
1. Billy Talent — 66 (Рок; rock, punk)
...
Ещё 1140 исполнителей с 1-4 треками, 1737 упоминаний суммарно; из них 835 — по одному треку.
## Топ-15 альбомов
1. Slipknot — Iowa (2001) — 12
...
Ещё 1401 альбом с 1-3 лайкнутыми треками. Треков с альбомов, где лайкнуто 2 и более: 732 из 2005 (37%).
Важно: поля год выхода и название альбома появились в кэше треков не с самого начала —
если cache/tracks.json был создан старой версией fetch, разделы «Десятилетия» и «Топ альбомов»
покажут «Неизвестно» до первого перезапуска python -m sort_ym fetch (он один раз доперекачает
весь кэш, дальше — как обычно, только новые треки).
Handy things
- Первый прогон
applyделайте с--limit 20 --yesи проверьте результат в приложении, прежде чем запускать на все ~2000 треков. - Настройки задержек между запросами — в
config.toml([throttle]). - Минимальный размер под-жанрового плейлиста —
config.toml([classify] small_group_min). - Сколько исполнителей/альбомов показывать поимённо в дайджесте —
config.toml([digest] top_artists,top_albums) или флаги--top/--top-albumsкомандыdigest.
Риски (неофициальный API)
Официального API для личной библиотеки Яндекс.Музыки нет — инструмент использует
неофициальную библиотеку yandex-music. OAuth-токен в .token даёт полный доступ к аккаунту
Яндекса — храните его локально и не публикуйте. Инструмент выполняет только безопасные действия
(чтение лайков, создание плейлистов, добавление треков) и ничего не удаляет.
For developers
Python 3.11+, yandex-music==3.0.0, стандартная библиотека (tomllib, csv, unicodedata).
.venv\Scripts\python -m pytest tests/ -v
.venv\Scripts\python -m sort_ym auth
.venv\Scripts\python -m sort_ym fetch
.venv\Scripts\python -m sort_ym report
.venv\Scripts\python -m sort_ym apply --limit 20 --yes
.venv\Scripts\python -m sort_ym digest
Структура кода и поток данных — в ARCHITECTURE.ru.md.
Credits
Andrew Pimenov