nsm/README.md
2026-07-13 09:31:20 +03:00

22 KiB
Raw Blame History

nsm

Терминальный музыкальный плеер на Rust — что-то вроде cmus, но с нуля, с эквалайзером, поддержкой cue-листов, интернет-радио и SMB-шар.

Read this in English

Возможности

  • Библиотека: рекурсивное сканирование вложенных папок, поиск (нечёткий, по названию/исполнителю/альбому), группировка по альбомам
  • Воспроизведение: открыть файл напрямую (весь альбом подряд) или добавить в плейлист; несколько плейлистов с возможностью переставлять и удалять треки
  • Транспорт: shuffle, повтор (выкл/один трек/всё), изменение скорости воспроизведения (0.25×1.75×), mute, честный seek (перемотка) с сохранением позиции
  • .cue-листы: один физический файл (например, целый альбом одним FLAC) корректно распознаётся как несколько треков с правильными названиями и границами
  • Эквалайзер: 8 полос, свои биквадратные фильтры
  • Обложка: отображается прямо в терминале (kitty/sixel/iTerm2-протоколы с fallback на блочную графику)
  • Анализатор спектра: настоящий FFT в реальном времени (не анимация) — переключение обложка/спектр клавишей v, разворот на весь экран клавишей z (транспорт — пробел/next/prev/seek/mute — работает и в полноэкранном режиме)
  • Затухание в конце трека: последние 4 секунды перед концом плавно уводятся в тишину — включается клавишей F, по умолчанию выключено
  • Выравнивание громкости: тег ReplayGain в приоритете (если есть в файле), иначе — адаптивный левеллер (AGC), подстраивающий громкость на лету — клавиша G, по умолчанию выключено
  • Интернет-радио: свои станции по URL, встроенный список (SomaFM, Radio Paradise), поиск станций через открытый Radio Browser API (с автопереключением между несколькими зеркалами, если одно недоступно), ICY-метаданные ("сейчас играет" прямо из потока)
  • SMB-шары: подключение к сетевым папкам (в т.ч. анонимно/гостевой доступ) через системный gvfs — без реализации протокола внутри плеера
  • История прослушиваний: при первом запуске спрашивает, запоминать ли прослушанное; статистика (артист/трек/дата) — диаграммой по часам за сегодня, по дням за неделю/месяц; включается/выключается в любой момент, сбрасывается с подтверждением
  • Новые релизы: проверка новых альбомов/EP/синглов у артистов, уже имеющихся в твоей библиотеке, через открытую базу MusicBrainz (без ключей API) — не глобальный чарт, а именно "что вышло нового у тех, кого ты слушаешь"
  • Раскладка: хоткеи работают одинаково что на английской, что на русской раскладке

Форматы

Поддерживается всё, что умеет декодировать symphonia: MP3, FLAC, ALAC/AAC (в .m4a), WAV, AIFF, OGG (Vorbis), MKV/WebM.

Не поддерживается: Opusу symphonia нет собственного Opus-декодера ни в одном наборе фич (только сторонние биндинги к libopus, которых в проекте нет). .opus-файлы не индексируются сканером библиотеки.

Сборка

Системные зависимости

Linux — нужны заголовки ALSA (для аудио-вывода через cpal):

# Debian/Ubuntu
sudo apt-get install libasound2-dev pkg-config

# ALT Linux
sudo apt-get install libalsa-devel

# Fedora
sudo dnf install alsa-lib-devel

macOS (не проверено — собиралось и тестировалось только на Linux) — отдельных аудио-зависимостей не нужно, cpal использует системный CoreAudio напрямую. Нужны стандартные инструменты сборки:

xcode-select --install

Windows (не проверено — собиралось и тестировалось только на Linux) — отдельных аудио-зависимостей тоже не нужно (cpal использует WASAPI), достаточно обычной установки Rust через rustup (MSVC toolchain). Для нормального отображения обложки в терминале нужен современный эмулятор с поддержкой true color — рекомендуется Windows Terminal; протоколы отображения картинок (kitty/sixel/iTerm2) там, скорее всего, не поддерживаются, так что обложка будет через block-графику как fallback.

SMB-фича (c в Library) работает только на Linux — она целиком построена на gio/gvfs (GNOME-стек), которого нет ни на macOS, ни на Windows. На этих системах при попытке подключить SMB-шару через nsm просто вернётся понятная ошибка ("is gvfs installed?"), без краша — сетевые папки там штатно монтируются через Finder/Проводник, не через nsm.

Если получаешь ошибку "volume doesn't implement mount" / "том не поддерживает монтирование" — это gvfs-специфичная ошибка, при том что сама SMB-шара доступна и работает. Обычно значит, что не хватает именно SMB-бэкенда gvfs (сам gio может быть установлен как чужая зависимость, а конкретно SMB-плагин к нему — нет). Проверить и починить:

# проверить руками, минуя nsm
gio mount smb://сервер/шара

# на ALT/Fedora-подобных — убедиться, что бэкенд установлен
sudo apt-get install gvfs-backends   # (или свой пакетный менеджер)

# проверить, что D-Bus сессия вообще есть (без неё gvfs не активируется)
echo $DBUS_SESSION_BUS_ADDRESS

Если это не помогло — причина может быть глубже, в согласовании версии протокола SMB между клиентом и сервером (см. journalctl на предмет строк вида smbXcli_negprot от gvfsd); в этом случае поможет client min protocol в /etc/samba/smb.conf, как описано выше.

Для интернет-радио (SomaFM, Radio Paradise и т.д.) и MusicBrainz дополнительных системных зависимостей не нужно ни на одной ОС — TLS обеспечивает rustls (чистый Rust, без OpenSSL).

Сборка проекта

git clone <URL-этого-репозитория>
cd nsm
cargo build --release

Бинарник появится в target/release/nsm (на Windows — target\release\nsm.exe).

Сборка Windows-бинарника с Linux (кросс-компиляция)

# 1. Добавить Windows-таргет (нужен rustup — обычная установка Rust с rustup.rs)
rustup target add x86_64-pc-windows-gnu

# 2. Поставить mingw-w64 — кросс-компилятор/линкер под Windows
sudo apt-get install gcc-mingw-w64-x86-64  # Debian/Ubuntu; на других — свой пакетный менеджер

# 3. Указать cargo, каким линкером пользоваться для этого таргета
mkdir -p .cargo
cat >> .cargo/config.toml <<EOF
[target.x86_64-pc-windows-gnu]
linker = "x86_64-w64-mingw32-gcc"
EOF

# 4. Собрать
cargo build --release --target x86_64-pc-windows-gnu

Бинарник появится в target/x86_64-pc-windows-gnu/release/nsm.exe.

Проверено частично: сам линкер (mingw-w64) реально протестирован — собрал им тестовую C-программу, получился настоящий Windows PE-исполняемый файл. Полную сборку nsm под Windows-таргет от начала до конца проверить не удалось (нужен rustup для загрузки Windows std-библиотеки, а в этом окружении к нему нет доступа) — но по ходу проверки нашёлся и уже исправлен реальный баг: код использовал Unix-специфичный способ определения размера ячейки терминала для обложки, из-за чего сборка под Windows не прошла бы вообще. Теперь на не-Unix платформах используется разумное значение по умолчанию.

Запуск

./target/release/nsm /путь/к/музыке

Если путь не указан, по умолчанию используется ~/Music.

Управление

Глобальные клавиши (любая вкладка)

Клавиша Действие
Space play/pause
N / P следующий / предыдущий трек
s shuffle вкл/выкл
r repeat (выкл → всё → один трек → выкл)
m mute
[ / ] скорость воспроизведения вниз/вверх
/ перемотка 5с / +5с
e фокус на эквалайзер
h включить/выключить запись истории прослушиваний
v переключить обложка/спектр
z развернуть обложку/спектр на весь экран
F затухание в конце трека вкл/выкл
G выравнивание громкости вкл/выкл
I переключить протокол отображения обложки (если автоопределение ошиблось)
Tab следующая вкладка
q выход

Эквалайзер (после e)

Клавиша Действие
/ выбор полосы
/ ±1dB на выбранной полосе
0 сброс текущей полосы
R сброс всех полос
Esc / e выйти из фокуса

Library

Клавиша Действие
j/k, / навигация
Enter раскрыть альбом / сыграть
Esc свернуть альбом
/ поиск
a добавить в плейлист
c подключить SMB-шару
L добавить ещё одну папку к библиотеке
C очистить библиотеку (не трогает файлы на диске)

Playlists

Клавиша Действие
j/k навигация
Enter открыть плейлист / играть с этого места
n новый плейлист
J/K переместить трек вниз/вверх
d удалить трек из плейлиста
Esc назад к списку плейлистов

Queue

Клавиша Действие
j/k навигация
Enter играть с выбранного места

Radio

Клавиша Действие
j/k навигация
Enter играть станцию
u добавить свою станцию по URL
o поиск станций онлайн
d удалить (только свою) станцию

Stats

Клавиша Действие
p переключить период (день → неделя → месяц)
x сбросить историю (нужно нажать дважды подряд для подтверждения)

New Releases

Клавиша Действие
p переключить период (день → неделя → месяц)
f проверить новые релизы у всех артистов из библиотеки (может занять время, см. ниже)
a вручную проверить одного артиста по имени (не обязательно есть в библиотеке) — добавляется к уже показанному списку

В полях ввода текста — Enter подтверждает, Esc отменяет, Backspace стирает.

Архитектура

Cargo-workspace из четырёх крейтов:

crates/
├── player/    — движок воспроизведения: декодирование (symphonia), вывод звука (cpal),
│                эквалайзер, ресемплинг для скорости, очередь, интернет-радио
├── library/   — сканирование библиотеки, теги (lofty), cue-листы, плейлисты,
│                радиостанции, SMB
├── tui/       — интерфейс на ratatui: вкладки, обложка, эквалайзер, event loop
└── nsm/       — тонкий бинарник, связывающий всё вместе

player и library не знают друг о друге — tui связывает их через player::QueueItem.

История прослушиваний

Хранится локально в ~/.config/nsm/history.jsonl (одна запись — одна строка JSON: артист, трек, дата/время). Никуда не отправляется. При первом запуске спрашивается разрешение; в любой момент можно включить/выключить клавишей h, а на вкладке Stats — посмотреть диаграмму по часам/дням, список конкретных прослушанных треков и сбросить всё (двойным нажатием x).

Кракозябры в старых тегах

Многие старые mp3 (особенно с тегами из 2000-х) хранят кириллицу в ID3v1 в кодировке CP1251, но она формально не объявлена — большинство библиотек (включая нашу) по умолчанию читают такие теги как Latin-1, получая "Ïåñíÿ" вместо "Песня". nsm определяет этот случай эвристически (по code points ≤0xFF и доле кириллических букв после перекодировки в CP1251) и чинит автоматически, не трогая уже нормальный UTF-8 текст.

Обложка

Протокол отображения обложки (kitty/sixel/iTerm2/halfblocks) определяется автоматически по терминалу. Если автоопределение угадало неверно (например, показывает низкое разрешение на терминале, который на самом деле поддерживает kitty-графику) — жми I, чтобы вручную перебрать протоколы; выбор запоминается и применяется при следующих запусках.

Новые релизы

Вкладка New Releases явно показывает релизы только для артистов, уже имеющихся в твоей библиотеке — это не глобальный чарт новинок (RYM активно блокирует ботов, и честно скрапить его не вышло — см. обсуждение). Вместо этого используется открытая база MusicBrainz, без ключей API.

Из-за ограничения MusicBrainz (не больше одного запроса в секунду) проверка большой библиотеки может занять время — по прогресс-бару видно, какой артист сейчас проверяется. Для очень плодовитых артистов (много синглов/переизданий за много лет) может понадобиться несколько запросов подряд на одного артиста — MusicBrainz не гарантирует сортировку результатов по дате, поэтому вычитывается вся дискография постранично, а не только первые 100 записей (иначе новый релиз мог случайно не попасть в выборку). Результаты кэшируются в ~/.config/nsm/new_releases_cache.json, повторный запуск nsm сразу покажет последний результат без обращения к сети; обновление — вручную клавишей f.

Важно понимать масштаб: вкладка показывает релизы только за последний день/неделю/месяц (переключение p) — это буквально "что вышло недавно", а не вся дискография артиста. Если твой любимый артист не выпускал ничего последние 30 дней (для большинства артистов это норма), список для него будет пустым — это ожидаемое поведение, не баг.

Выравнивание громкости

Клавиша G. Если в файле есть тег ReplayGain (REPLAYGAIN_TRACK_GAIN и аналоги — читается универсально через lofty для FLAC/MP3/OGG/MP4) — используется точное значение из тега. Если тега нет — включается адаптивный левеллер (AGC): он постоянно измеряет громкость последних 1-3 секунд и плавно подстраивает усиление к цели. Это не то же самое, что настоящий ReplayGain (посчитанный заранее по всему треку) — первые секунды трека выравниваются не идеально, дальше подстройка сходится.

Известные ограничения

  • Минимальный размер терминала — 70×18; на меньшем показывается предупреждение вместо сломанного интерфейса

  • Собиралось и тестировалось только на Linux — сборки под macOS/Windows не проверялись; SMB-фича на них принципиально не будет работать (см. выше)

  • Opus не декодируется (см. раздел «Форматы»)

  • Плейлисты не хранят диапазоны cue-треков — добавление cue-трека в плейлист сохраняет файл целиком, а не конкретный виртуальный трек

  • Подключение к SMB — блокирующий вызов в UI-потоке; интерфейс на секунду-другую подвиснет во время операции (поиск радиостанций и проверка новых релизов уже работают в фоне и не блокируют)

  • Мульти-файловые cue-листы (редкость) поддерживаются только по первому FILE в листе