Как это устроено
Обзор архитектуры
Три части Nuvo, как они общаются и где хранится каждый вид данных.
В Nuvo три части.
| Часть | Технология | Задача |
|---|---|---|
| Окно | Рендерер Electron, React, TypeScript | Экраны, движок воспроизведения, эквалайзер, показ текстов |
| Основной процесс | Electron main | Запускает демон, отвечает за трей, Notch, файловые диалоги и политику безопасности |
Демон nuvo-core |
Rust, axum, SQLCipher | Медиатека, плейлисты, история, кэши, тексты, ранжирование, импорт, локальные файлы |
Окно рендерер Electron, React
Рендерер (React) состоит из трёх частей: экраны, движок (деки, эквалайзер, очередь) и доступ к YouTube (youtubei.js). Экраны передают данные движку. Экраны общаются с основным процессом Electron по IPC в обе стороны и обращаются к демону через протокол nuvo-core://, который обрабатывает основной процесс. Основной процесс Electron общается с nuvo-core (axum) в обе стороны, по HTTP через unix-сокет с токеном bearer. Демон пишет в базу медиатеки library.db (SQLCipher), кэш аудио (XChaCha20-Poly1305) и кэш обложек и связан с хранилищем ключей. Он обращается к внешним сервисам (YouTube Music, SoundCloud, LRCLIB и API для ссылок) и запускает необязательные помощники yt-dlp и ffmpeg. Доступ к YouTube в рендерере обращается к тем же сервисам напрямую.
Запуск
- Основной процесс создаёт случайный токен и запускает
nuvo-coreс ним. - Демон привязывает unix-сокет в закрытом каталоге и отвечает на
/healthстатусомstarting. - Он читает ключ медиатеки из хранилища ключей и открывает базу данных. Когда это закончено,
/healthотвечаетok. - Если для существующей базы ключа нет, демон стартует заблокированным и ждёт фразу восстановления.
Основной процесс сначала опрашивает состояние каждые 50 мс. Если хранилище ключей показывает окно разблокировки, демон продолжает отвечать starting, а основной процесс ждёт до двух минут. Демон, который не отвечает совсем, считается мёртвым через пять секунд. После сбоя основной процесс перезапускает его через 0,5, 2 и 5 секунд и сдаётся после четвёртого сбоя за минуту. Пока демон перезапускается, воспроизведение продолжается.
Когда Electron завершается, демон замечает, что его стандартный ввод закрыт, и выходит.
Как окно обращается к демону
Окно никогда не видит токен. Оно вызывает URL nuvo-core://. Основной процесс проверяет их и пересылает в unix-сокет вместе с токеном. См. API демона.
Где выполняется работа
- Воспроизведение идёт в рендерере. Два элемента audio (деки) питают один аудиограф: усиление выравнивания, предусилитель, десять фильтров, посыл на реверберацию и выход.
- Демон загружает потоки только с разрешённых хостов воспроизведения и хранит их в зашифрованном кэше. См. кэш аудио.
- Поиск, связанные треки и URL потоков для YouTube приходят от клиента YouTube в рендерере. Публичный каталог SoundCloud читает демон.
- Ранжирование рекомендаций выполняется в демоне по кандидатам, которых получил рендерер.
- Тексты приходят из LRCLIB через демон.
yt-dlpиffmpeg- необязательные помощники, которые демон запускает для разбора ссылок, распознавания и сохранения. Для воспроизведения они не нужны.
Крейты демона
| Крейт | Назначение |
|---|---|
nuvo-server |
HTTP API, состояние, планирование |
nuvo-store |
База данных SQLCipher, миграции, запросы |
nuvo-crypto |
Вывод ключей, шифрование частей, фраза восстановления, хранилище ключей |
nuvo-cache |
Зашифрованные аудиофайлы |
nuvo-covers |
Обложки в размере для показа |
nuvo-lyrics |
Клиент LRCLIB |
nuvo-recs |
Награды, ранжирование, миксы, скрытое |
nuvo-match |
Решает, тот ли это каталожный трек, что и нужная запись |
nuvo-import |
Парсеры и экспортёры файлов плейлистов |
nuvo-local |
Обход папок, теги, сохранённые копии |
nuvo-fetch |
yt-dlp, ffmpeg, работа с прокси, безопасность ссылок |
nuvo-soundcloud |
Публичный каталог и потоки SoundCloud |
nuvo-recognize |
Сигнатуры Shazam и план прослушивания |
nuvo-lastfm |
Клиент Last.fm |
nuvo-net, nuvo-contracts |
Сетевые помощники, общие типы |