muzical
v0.0.13
Published
A Node.JS CLI Music Player
Maintainers
Readme
Muzical
Muzical is a terminal music browser and launcher built with
Ink and React. It indexes audio files
under a configurable folder, reads tags with
music-metadata, and hands playback
off to whichever common CLI media player it finds first on your PATH.
Summary. Five views — a three-column library browser, a playlist queue, a "now playing" pane, a Soulseek search/download view, and a live config editor — all driven by vim-style keybindings.
Table of contents
- Features
- Requirements
- Install
- Configuration
- Usage
- Views
- Keyboard reference
- CLI commands
- Playback backends
- How it works
- Development
- Documentation
- Contributing
- License
- Links
Features
- Five dedicated views — library browser, playlist queue, now playing,
Soulseek client, and an in-app config editor, switchable with
1–5. - Three-pane library UI — browse artists, albums, and songs with vim-style
(
hjkl) and arrow-key navigation. - Per-column search — filter the focused column with
/; queries are independent per pane (artist, album, song, playlist). - Playlist queue — build an ad-hoc queue with
space; remove entries or clear the whole list from the playlist view. - Now playing pane — full-pane track readout with a live progress bar, elapsed / total time, track number, and player state.
- Soulseek search & download — connect with stored credentials, search the
network, and stream downloads directly into
musicDir. - In-app config editor — edit
musicDir, extensions, and Soulseek credentials without leaving the TUI; changes persist to disk immediately. - Metadata-aware — albums and tracks come from embedded tags (via
music-metadata). - Sensible sorting — artists and albums alphabetically; tracks ordered for listening.
- Live volume control —
+/-adjusts master volume; mpv volume is updated over the IPC socket while a track is playing. - External playback — no embedded decoder; uses
mpg123,mpv,ffplay, orvlcif available. - Resize-aware — list viewport adapts to terminal height.
- Rich CLI surface — subcommands to scan, list, and print resolved config/player without opening the TUI.
Requirements
- Node.js
>= 16(seeenginesinpackage.json). - pnpm (recommended) or another package manager compatible with this repo.
- At least one playback binary on your
PATH(see Playback backends). - A config file pointing at your music directory (see Configuration).
Install
From the repository
git clone https://github.com/f3rnox/muzical.git
cd muzical
pnpm install
pnpm buildRun locally without installing globally:
pnpm start
# or, during development:
pnpm devLink the CLI globally (optional):
pnpm link --global
muzicalPublished package
When published to npm, install with:
pnpm add -g muzical
# or: npm install -g muzicalConfiguration
Muzical reads config.json from the OS config directory for the muzical
application name (via env-paths):
- Linux:
~/.config/muzical/config.json - macOS:
~/Library/Preferences/muzical/config.json - Windows:
%APPDATA%\muzical\config.json
Pass --config <path> to use a file at an arbitrary location. If the default
config directory does not exist it is created on startup. If config.json is
missing and --music-dir is not supplied, the program exits with a message
showing the expected path.
config.json shape
{
"musicDir": "/absolute/path/to/your/music",
"songExtensions": [".mp3", ".flac"],
"soulseekUsername": "",
"soulseekPassword": ""
}Fields:
musicDir(required) — Root directory scanned recursively for audio files.songExtensions(optional) — Allowed suffixes; defaults to[".mp3", ".flac"].soulseekUsername(optional) — Used by the Soulseek view to connect. Empty by default.soulseekPassword(optional) — Stored in plaintext alongside the username. Empty by default.
The Soulseek fields can also be edited live from the in-app Config view
(press 5).
Usage
muzicalOn launch, Muzical clears the screen, loads the library (this can take a moment
on large collections), renders the TUI, and clears again on exit. Use
--no-clear-screen to keep terminal scrollback.
Quick navigation
- Press
1–5to switch between Library, Playlist, Now Playing, Soulseek, and Config views. - Use left/right (or
h/l) to move focus between Artists, Albums, and Songs (library view only). - Use up/down (or
j/k), Page Up/Down, or g / G to move through the focused list. - Press Enter or p on a selected track to toggle playback. Space also toggles playback in the Now Playing view.
- Press s to stop playback; + / - adjust volume by 5%.
- Press
/to search within the focused column; Enter accepts, Esc cancels and clears that column's query. - In the library view, space appends the focused artist/album/song to the playlist. In the playlist view, space removes the focused entry.
- Press c to clear the focused column's filter (or clear the entire playlist in the playlist view).
- Press q or Esc to quit.
Views
Muzical is organized as five top-level views. Switching views never interrupts playback.
1Library — three-column artist → album → song browser with per-pane search.2Playlist — ad-hoc play queue built withspacefrom the library view.3Now Playing — full-pane readout with track metadata and a live progress bar.4Soulseek — connect, search, and download tracks intomusicDir.5Config — live editor forconfig.jsonfields (music dir, extensions, Soulseek credentials).
The Soulseek and Config views own their own keyboard handling; global
bindings (other than 1–5 view switches and q to quit) are suspended while
either is active. The Now Playing view hides the bottom playback bar since
its own progress bar is already present.
Keyboard reference
Global (Library, Playlist, Now Playing)
| Mode | Key(s) | Action |
| ------ | ---------------------- | --------------------------------- |
| Normal | 1–5 | Switch view |
| Normal | ↑ ↓ / j k | Move selection in focused list |
| Normal | PgUp / PgDn | Page by visible list height |
| Normal | g / G | Jump to first / last item |
| Normal | Enter / p | Toggle playback for selected song |
| Normal | s | Stop playback |
| Normal | + / = / - | Raise / lower volume (5% steps) |
| Normal | / | Start search in focused column |
| Normal | q / Esc | Quit |
| Search | type | Append to query |
| Search | Backspace / Delete | Delete last character |
| Search | ↑ ↓ | Move selection while searching |
| Search | Enter | Leave search mode (keep query) |
| Search | Esc | Cancel search and clear the query |
Library view
| Key | Action |
| ----------------- | ----------------------------------------- |
| ← → / h l | Focus previous / next column |
| space | Append focused artist/album/song to queue |
| c | Clear the focused column's search query |
Playlist view
| Key | Action |
| ------- | ---------------------------------------- |
| space | Remove the selected entry from the queue |
| c | Clear the entire playlist |
Now Playing view
| Key | Action |
| ----------------------- | --------------- |
| Enter / p / space | Toggle playback |
| s | Stop playback |
| + / - | Adjust volume |
Soulseek view (view-local bindings)
| Mode | Key(s) | Action |
| ----- | ----------------------- | ---------------------------- |
| Input | type | Append to query |
| Input | Backspace | Delete last character |
| Input | Enter | Run search |
| Input | Esc | Return to results list |
| List | / / i | Open search input |
| List | c | Connect (or reconnect) |
| List | ↑ ↓ / j k | Move selection |
| List | PgUp / PgDn | Page through results |
| List | g / G | Jump to first / last result |
| List | Enter / d / space | Download the selected result |
| List | x | Clear results |
| List | 1–5 | Switch view |
| List | q | Quit |
Config view (view-local bindings)
| Mode | Key(s) | Action |
| ---- | ---------------------- | ---------------------------------- |
| Nav | ↑ ↓ / j k | Move between fields |
| Nav | Enter / i | Begin editing the focused field |
| Nav | 1–5 | Switch view |
| Nav | q / Esc | Quit |
| Edit | type | Append to draft |
| Edit | Backspace / Delete | Delete last character |
| Edit | Enter | Save the draft and persist to disk |
| Edit | Esc | Cancel edit, keep existing value |
CLI commands
The binary accepts global options that apply to every mode and a handful of
subcommands for non-interactive use. Run muzical --help (or muzical
<subcommand> --help) for the authoritative list.
Global options
| Flag | Purpose |
| ------------------------ | --------------------------------------------- |
| -d, --music-dir <path> | Override musicDir from config |
| -e, --extension <ext> | Add / override a song extension (repeatable) |
| -p, --player <name> | Force a backend (mpg123 / mpv / ffplay / vlc) |
| -c, --config <path> | Use a custom config.json location |
| --no-clear-screen | Do not clear terminal on start/exit |
| -v, --version | Print version and exit |
Subcommands
muzical— launch the TUI (default).muzical config— print the resolved configuration as JSON and exit.muzical player [--player <name>] [--all]— print the detected playback backend (or check a specific one / list all candidates).muzical scan [--json]— scan the music library and print a summary without launching the UI.muzical list <artists|albums|songs> [--artist ...] [--album ...] [--json]— list library contents with optional filters.
Examples
muzical
muzical --music-dir ~/Music --player mpv
muzical scan --json
muzical list artists
muzical list songs --artist 'Radiohead' --album 'OK Computer'
muzical player --allPlayback backends
The first available binary from this list wins (in order):
mpg123mpvffplayvlc
Install any one of them and ensure it is on your PATH, or use
--player <name> to force a specific backend. Arguments are chosen for quiet,
non-interactive playback suitable for a TUI (e.g. mpv with --no-video).
Per-backend volume handling:
mpv— initial--volume=<n>; while playing, volume changes are sent over--input-ipc-serverfor instant updates without a restart.mpg123—-f <scale>wherescaleisvolume / 100 * 256.ffplay—-af volume=<ratio>.vlc— backend-level volume flags are not used;+/-still track master volume state for display.
How it works
- Config —
load_configresolvesconfig.json(or--config <path>), merges in CLI overrides, and validatesmusicDir/songExtensionsplus optional Soulseek credentials. - Discovery —
load_music_dirwalksmusicDirfor matching extensions. - Library —
load_libraryparses each file's metadata into in-memoryLibrarySongentries. - UI shell —
App(src/app.tsx) composesStatusBar, one of the five view components,PlaybackBar, andHelpBar; keyboard input for the Library / Playlist / Now Playing views is centralized inuse_app_input. - Views (
src/views/)library_view.tsx— three-column artist/album/song browser.playlist_view.tsx— single-column play queue.now_playing_view.tsx— full-pane readout with progress bar.soulseek_view.tsx— search/download UI backed byuseSoulseek.config_view.tsx— liveconfig.jsoneditor, writes viawriteConfig.
- Player —
resolvePlayer/detectPlayerpicks a backend fromPLAYER_CANDIDATES;usePlayerspawns it for the selected file path and forwards volume changes (including mpv IPC updates).
Development
pnpm install
pnpm dev # run from TypeScript source
pnpm build # TypeScript + bundled CLI + TypeDoc
pnpm test # Mocha
pnpm test:vitest # Vitest
pnpm lint # markdownlint (README) + ESLint
pnpm lint:eslint # ESLint only (src + package.json)
pnpm format # PrettierRelease and docs scripts are defined in package.json (prepare-release,
serve:docs, etc.) for maintainers.
Documentation
TypeDoc output is produced under docs/ when you run pnpm build (or
pnpm build:docs). Serve locally:
pnpm serve:docsContributing
Issues and pull requests are welcome at https://github.com/f3rnox/muzical/issues.
- Match existing TypeScript style (no semicolons, single quotes, explicit types, one function per file).
- Run
pnpm lintandpnpm testbefore opening a PR when you touch code or this README.
License
This project is licensed under the MIT License — see LICENSE.md.
Links
- Repository: https://github.com/f3rnox/muzical
- Issues: https://github.com/f3rnox/muzical/issues
- Homepage: https://github.com/f3rnox/muzical (see
package.jsonhomepage)
