mirror of
https://github.com/Horizontal42/Sort_YM.git
synced 2026-11-10 15:42:02 +03:00
Раньше жанр брался только с альбома (одна строка, часто общая: rock/alternative/pop) - разные по звучанию артисты сваливались в одну крупную корзину. Теперь classify_track сводит жанр трека и жанры артиста (client.artists(), несколько тегов на артиста) на уровне под-жанра: при согласии сигналов или отсутствии одного из них берём имеющийся; при конфликте - если альбомный тег зонтичный (GENERIC_SLUGS) доверяем артисту, если точный - треку (вероятный осознанный эксперимент артиста в другом жанре). Один и тот же артист с разными по духу треками теперь раскладывается по разным плейлистам. Под-жанровые группы меньше config.small_group_min схлопываются в родительскую крупную корзину (report.build_rows, двухпроходно, без циклов - coarse_of детерминирована от fine). Попутно: artist_ids трека может быть None (артист без привязки к каталогу Яндекса, встречается у стороннего/пиратского аплоада) - раньше это улетало в client.artists() как строка "None" и валилось BadRequestError на батче; теперь такие id отфильтровываются на записи и дополнительно на чтении кэша.
130 lines
8 KiB
Markdown
130 lines
8 KiB
Markdown
# 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 # создать плейлисты и добавить в них все треки (меняет аккаунт)
|
||
```
|
||
|
||
При повторных запусках `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` переполучить всё с нуля при следующем запуске.
|
||
|
||
Также можно удалить сгенерированный отчёт:
|
||
|
||
```
|
||
Remove-Item -Recurse -Force out
|
||
```
|
||
|
||
Это просто результаты анализа, их удаление не повлияет на следующий запуск `report`.
|
||
|
||
**Важно:** файл `.token` (OAuth-токен) не входит в кэш и не удаляется при очистке `cache/`. Очистка кэша вас не разлогирует. Если нужна повторная авторизация, удалите `.token` отдельно:
|
||
|
||
```
|
||
Remove-Item .token
|
||
```
|
||
|
||
## Как определяется жанр и язык
|
||
|
||
- **Жанр** определяется на **под-жанровом** уровне (Рок, Панк, Инди, Альтернатива, Метал, ...,
|
||
не только 11 крупных групп), комбинируя два сигнала:
|
||
1. Жанр **альбома** трека (`genre_raw`) — сигнал по конкретному треку.
|
||
2. Жанры **артиста** (`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`) — таблица корней легко
|
||
дополняется, если Яндекс вернёт незнакомый корневой жанр.
|
||
|
||
- **Язык** — гибрид:
|
||
1. Если у трека есть текст песни — реальный язык из API (`ru` → RU, иначе → INT).
|
||
2. Иначе — подсказка по жанру (`rusrap`, `shanson`, `bard`, ...).
|
||
3. Иначе — по алфавиту названия/исполнителя (кириллица/латиница).
|
||
4. Если ничего не сработало — трек попадает в `... — Не определено`.
|
||
|
||
После `report` в консоли также печатается список нераспознанных жанров (попавших в «Разное»),
|
||
чтобы можно было дополнить таблицу в `sort_ym/genres.py`.
|
||
|
||
## Handy things
|
||
|
||
- Первый прогон `apply` делайте с `--limit 20 --yes` и проверьте результат в приложении, прежде
|
||
чем запускать на все ~2000 треков.
|
||
- Настройки задержек между запросами — в `config.toml` (`[throttle]`).
|
||
- Минимальный размер под-жанрового плейлиста — `config.toml` (`[classify] small_group_min`).
|
||
|
||
## Риски (неофициальный 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
|
||
```
|
||
|
||
Структура кода и поток данных — в [ARCHITECTURE.md](ARCHITECTURE.md).
|