A summon-anywhere command palette for NetEase Cloud Music. Hit ⌥Space, change what's playing, and it's gone again. No dock icon, no windows to manage — it lives in the menu bar and the whole UI is one palette.
When idle it's a small now-playing card: big cover, the title, a progress
line, and an accent color pulled live from the album art that washes the whole
surface. Start typing and it morphs into search. It holds the real <audio>
element and plays in the background; closing the window just hides it.
muse is the controller half of a two-app setup. Its companion echo is a floating-lyrics widget that reads muse's playback state over a local socket — see muse ↔ echo.
macOS (Apple Silicon), Electron. Built around my own liked-songs library; it's a personal tool, not a polished NetEase client.
Download — grab the latest .dmg from
Releases, drag to
Applications. The build is unsigned, so on first launch right-click → Open
(double-clicking shows a Gatekeeper warning). First run asks you to scan a QR
with the NetEase mobile app.
From source
git clone git@github.com:finns0309/muse.git
cd muse && npm install && npm startEverything happens in the palette. Type to search; prefix with / for commands.
| Input | Does |
|---|---|
<query> |
search your library (and NetEase cloud at ≥2 chars) |
/queue /queue <q> |
browse / filter the current queue |
/library /library <q> |
browse / filter your liked library |
/pl |
list saved playlists |
/pl <id or url> |
load a NetEase playlist and remember it |
/mode, /daily, /discover, … |
switch listening mode |
/light /dark |
appearance |
/accent #d8b35f |
override the accent color |
| Key | Action |
|---|---|
⌥Space |
summon from anywhere (also ⌘⇧Space) |
⌘K |
open palette |
⌘← / ⌘→ |
previous / next |
⌘P / Space |
play-pause |
Enter |
run the selection |
⌥Enter |
alternate action (usually: start a radio from this song) |
⌘D |
like / unlike the highlighted song |
Esc |
clear the input, then hide the window |
Library mirrors your NetEase Liked Songs playlist locally, plus lightweight per-song
metadata (playCount, lastPlayedAt). NetEase stays the source of truth —
songs you only play (search, discover, playlists) never pollute the library;
liking one (⌘D) calls /like and syncs the mirror.
Modes are queue builders — pure functions of (library, player, now) that
return a queue: all, daily, discover, dig up, immersive, similar,
single. A playback session tracks why the current queue exists
(mode / manual / radio / playlist) so picking a song knows whether to
interrupt the mode or extend it.
The runtime owns the single <audio> element and broadcasts accurate state
on two local channels, so echo (or anything) never has to guess:
GET http://127.0.0.1:10755/now authoritative now-playing (songId, position, state)
WS ws://127.0.0.1:10755/spectrum 24-band audio spectrum + RMS, ~30fps while playing
Wire format lives in NOW_PLAYING.md. A second local server
on 10754 is the NetEase API wrapper.
The split is deliberate: muse stays small, fast, and mostly-backgrounded; echo gets to be the visual, always-on lyrics layer. They talk one way only — muse publishes, echo reads:
NetEase → muse ──/now + /spectrum──► echo
(control, runtime) (lyrics, ambient)
echo can run standalone off nowplaying-cli, but that's lossy (sparse events,
stuck elapsed time, fuzzy title matching). When muse is running it gets the
exact songId and position instead.
Stored under Electron's userData, atomic writes (corrupt files quarantined as
.bad-<ts>.bak):
cookie.txt— NetEase logindata/library.json— liked-songs mirror + play metadatadata/history.json— play events (365-day retention)data/ui-prefs.json— mode, appearance, accentdata/saved-playlists.json— playlists remembered via/pl
main.js Electron app, window, tray, global shortcuts; NCM API +
/now + /spectrum servers; cookie / store / history
preload.js the window.muse IPC bridge
lib/store.js JSON store (one file per key, atomic, quarantine-on-corrupt)
renderer/
app.js boot: prefs → auth → mount cmdk → library sync → mode runner
cmdk.js the palette: DOM, render, cursor, keys, window resize
commands.js builders for queue / library / playlist / mode / search
player.js the <audio> element, queue, scrobble, /now + spectrum push
accent.js pull accent + ambient color from album art
transitions.js summon / hide / cover / title / track-change animations
glyphs.js the canonical Unicode glyph set (❯ ♥ ▶ ↳ ▸ ◆)
playlist.js NetEase playlist load + local bookmarks
store.js tiny pub/sub store (select / selectKey / race)
auth.js api.js QR login; NetEase API wrapper (retry, realIP, auth hooks)
modes/ queue builders + session.js
lib/ library mirror, similar, artist helpers
npm start to develop. npm run build for a local .dmg. npm run release
publishes to GitHub Releases. See RELEASING.md for the full
flow (token setup, Gatekeeper note, troubleshooting).