Ластфм для приватного комьюнити: скробблинг, статистика, Last.fm-совместимое API и вход только через OAuth-провайдер из MKbot.
- Скробблинг по двум протоколам: Last.fm API 2.0 (
/2.0/) и Audioscrobbler 1.2 (/scrobble/), ответы в XML и JSON. - Статистика: лента комьюнити, «сейчас играет», профили, чарты исполнителей, альбомов и треков за 7 дней / месяц / 3 / 6 / 12 месяцев / всё время, активность по часам и дням недели, календарь за 90 дней, страницы исполнителей, альбомов, треков с топом слушателей, любимые треки.
- Аккаунты создаются сами при первом входе через OAuth. Ник берётся из Telegram-ника и меняется в настройках.
- Приватность: при
PRIVATE_SITE=trueсайт целиком закрыт от анонимов. - Акцентный цвет каждый выбирает себе сам в настройках (8 вариантов).
python -m venv venv && source venv/bin/activate
pip install -r requirements.txt
cp example.env .env # и заполнить
python manage.py migrate
python manage.py runserver- В MKbot добавьте клиента в
oauth/config.json:
"meowfm": {
"name": "mkfm",
"secret": "<длинный случайный секрет>",
"origins": ["https://meow.example.com"],
"allowed_callbacks": ["https://meow.example.com/auth/callback/"]
}- В
.envmkfm пропишите те же значения:
OIDC_ISSUER = "https://oauth.example.com" # = OAUTH_ENDPOINT бота
OIDC_CLIENT_ID = "meowfm"
OIDC_CLIENT_SECRET = "<тот же секрет>"
OIDC_REDIRECT_URI = "https://meow.example.com/auth/callback/"
redirect_uri сверяется с allowed_callbacks точным совпадением строки до ?,
поэтому слеш на конце важен.
/.well-known/jwks.jsonв MKbot отдаёт JSON-строку (двойное кодирование), аnиeлежат целыми числами вместо base64url — стандартные библиотеки такой JWKS не читают, поэтому вaccounts/oidc.pyсвой разбор.id_tokenне содержитnonce, защита строится наstate+ PKCE (S256).- Бот открывает
redirect_uriкнопкой в Telegram, и колбэк может прийти в другой браузер. Поэтомуstateиcode_verifierхранятся в БД (accounts.LoginRequest), а не только в сессии — вход завершится в том браузере, где открылась ссылка. - Кнопка в Telegram требует http(s)-ссылку,
localhostTelegram принимает не всегда: для отладки удобнее пробросить домен (или тестировать черезOIDC_VERIFY_ID_TOKEN=falseи фикстуры).
| Способ | Адрес | Как авторизуется |
|---|---|---|
| API 2.0, веб-подтверждение | https://<сайт>/2.0/ |
auth.getToken → страница /api/auth/ → auth.getSession |
| API 2.0, логин с паролем | https://<сайт>/2.0/ |
auth.getMobileSession с «паролем для скробблеров» |
| Audioscrobbler 1.2 | https://<сайт>/scrobble/ |
handshake с тем же паролем |
Пароль для скробблеров создаётся в настройках профиля и показывается один раз. Он нужен только протоколам, которые требуют пароль; хранится в виде md5, потому что именно md5 участвует в обеих схемах Last.fm.
Незнакомые api_key (скробблеры со своими ключами Last.fm) регистрируются
автоматически — подпись api_sig для них не проверяется, потому что секрет клиента
серверу неизвестен. Для своих приложений ключи заводятся в настройках, и тогда
подпись проверяется. Отключается флагами LFM_ALLOW_UNKNOWN_API_KEYS и
LFM_VERIFY_SIGNATURES.
auth.getToken, auth.getSession, auth.getMobileSession, track.updateNowPlaying,
track.scrobble (в том числе пачкой до 50), track.love, track.unlove,
track.getInfo, artist.getInfo, album.getInfo, user.getInfo,
user.getRecentTracks, user.getTopArtists, user.getTopAlbums, user.getTopTracks,
user.getLovedTracks, user.getFriends.
Каждый переносит себя сам на странице /settings/import/: вводит свой ник на last.fm
и жмёт «Начать перенос». Выкачка идёт в фоновом потоке, прогресс виден на той же
странице, уйти со страницы можно. Повторный запуск ничего не задвоит — скроббы
уникальны по паре «трек + время».
Чтобы участникам не пришлось заводить ключ Last.fm, пропишите общий в .env:
LASTFM_API_KEY = "<ключ с last.fm/api/account/create>"
Верхняя граница времени фиксируется на старте задания, иначе новые прослушивания на Last.fm сдвигали бы страницы прямо во время выкачки. Место остановки хранится в задании: если сервер перезапустить посреди переноса, на странице появится кнопка «Продолжить» — работа возобновится с той же страницы, а не с начала.
Из консоли то же самое:
python manage.py import_lastfm <ник в mkfm> --lastfm-user <ник на last.fm> --api-key <ключ>
python manage.py import_lastfm <ник> --lastfm-user <ник> --resume <id задания>python manage.py test51 тест: протоколы скробблинга, подписи, OIDC-разбор нестандартного JWKS,
защита от повторного использования state, перенос с Last.fm, отрисовка страниц.
Тема одна — тёмная, меняется только акцентный цвет. В CSS задаётся единственная
переменная --accent (её подставляет base.html из настроек пользователя), а полоски
чартов, календарь активности, гистограммы и кнопки выводятся из неё через color-mix.
Чтобы добавить цвет в список, достаточно дописать строку в web/theme.py — CSS править
не нужно. Выбор анонимов (страница входа) запоминается в куке accent.
accounts/ пользователи, OIDC-клиент, middleware приватности
music/ Artist/Album/Track/Scrobble/NowPlaying/Love, приём скробблов, агрегации
lfmapi/ Last.fm API 2.0, страница подтверждения токена, протокол 1.2
web/ страницы сайта и настройки
DEBUG=false, заполнитьALLOWED_HOSTS,CSRF_TRUSTED_ORIGINS,SITE_URL,SECRET_KEY.python manage.py collectstatic(статику отдаёт whitenoise).- Postgres включается переменными
POSTGRES_*, иначе используется SQLite. - Запуск:
gunicorn meowfm.wsgiза nginx.