This guide covers the architecture, configuration, build/distribution, security model, and internals of LX Music Desktop (洛雪音乐助手 桌面版) — a cross-platform Electron + Vue 3 music search-and-audition application. It is written for power users, packagers, integrators building on the Open API or scheme URLs, custom-source authors, and contributors. For installation and day-to-day usage, see the companion User Guide.
Note
LX Music is a desktop client with no server-side component of its own (aside from optional, locally-hosted services it starts: the Open API and the LAN sync server). It is a single-user application whose "deployment" is desktop packaging, not service operation.
1. Architecture Overview
1.1 What the system is
LX Music is an Electron application split across two renderer surfaces (the main UI and the desktop-lyric window), a main process that owns native integration and background services, and a shared common layer. The renderer is a Vue 3 single-page app built with webpack; the main process is TypeScript compiled separately. Music-platform adapters and user-supplied source scripts run inside the renderer with full Node.js access, which is central to both the feature set and the security posture (Section 7).
The application aggregates public catalog data from several music platforms and, critically, does not resolve audio URLs itself — it delegates that to a configurable source (a built-in interface source or a user "custom source" script). This separation is a deliberate design and legal boundary: LX Music passes track metadata to a source and treats whatever URL the source returns as the audio.
1.2 Process and component diagram
Electron Main Process (src/main)
┌───────────────────────────────────────────────────────────────────┐
│ app lifecycle (app.ts): single-instance, deeplink lxmusic://, │
│ env/param parsing, userData path, security listeners │
│ modules/ (registerModules): │
│ winMain ── BrowserWindow (frameless, transparent) │
│ winLyric ── desktop lyric BrowserWindow │
│ tray, appMenu, hotKey (global + local) │
│ userApi ── loads/runs custom-source scripts │
│ openApi ── local HTTP control server (:23330) │
│ sync ── LAN sync server/client over WebSocket (:23332) │
│ commonRenderers ── shared renderer-process logic │
│ worker/ ── better-sqlite3 dbService (lists, cache) in a thread │
│ utils/ ── Store (JSON config), settings, theme, migrate, logInit │
└───────────────┬──────────────────────────────────┬────────────────┘
IPC (mainIpc / rendererIpc, message2call) │
┌───────────────▼──────────────┐ ┌─────────────▼────────────────┐
│ Renderer: main UI │ │ Renderer: desktop lyric │
│ (src/renderer, Vue 3) │ │ (src/renderer-lyric, Vue 3) │
│ views/ Search, List, │ │ LyricHorizontal / │
│ Leaderboard, songList, │ │ LyricVertical │
│ Download, Setting │ └───────────────────────────────┘
│ core/ music, player │
│ store/ (Vue-reactive) │ ┌───────────────────────────────┐
│ utils/musicSdk/ │ │ src/common (shared) │
│ bd kg kw mg tx wy + user │ │ config, constants, ipc, │
│ worker/ download, main │ │ defaultSetting, theme, lang │
└──────────────────────────────┘ └───────────────────────────────┘
1.3 Component inventory
| Component | Path | Role |
|---|---|---|
| Main entry | src/main/index.ts, src/main/app.ts |
Boot sequence, single-instance, lifecycle, security listeners |
| Module registry | src/main/modules/index.ts |
Registers userApi, commonRenderers, winMain, hotKey, tray, appMenu, winLyric |
| Main window | src/main/modules/winMain/ |
Frameless BrowserWindow, taskbar thumb buttons, dialogs, auto-update |
| Lyric window | src/main/modules/winLyric/ |
Desktop lyrics BrowserWindow |
| Tray | src/main/modules/tray.ts |
Tray icon, context menu, status-bar lyric (macOS) |
| Hotkeys | src/main/modules/hotKey/ |
Global + local accelerator registration |
| Custom sources | src/main/modules/userApi/ |
Loads and sandboxes user source scripts |
| Open API | src/main/modules/openApi/index.ts |
Local HTTP control/status server |
| Sync | src/main/modules/sync/ |
LAN sync server and client (WebSocket) |
| DB service | src/main/worker/dbService/ |
better-sqlite3 store for lists, lyric cache, URL cache, downloads |
| Config store | src/main/utils/store.ts |
Atomic JSON persistence (Store class) |
| Renderer UI | src/renderer/ |
Vue 3 SPA: views, core player, stores, plugins |
| Lyric renderer | src/renderer-lyric/ |
Vue 3 SPA for the lyric window |
| Source adapters | src/renderer/utils/musicSdk/{bd,kg,kw,mg,tx,wy} |
Per-platform catalog/search/URL logic |
| Shared layer | src/common/ |
Config defaults, constants, IPC names, i18n (lang), themes |
| Build configs | build-config/ |
webpack configs (main, renderer, renderer-lyric, renderer-scripts) + electron-builder packaging |
1.4 Technology stack
| Layer | Technology | Version |
|---|---|---|
| App version | LX Music | 2.12.2 |
| Shell | Electron | 40.9.2 (Chromium target Electron 22.3.0 for browserslist) |
| UI framework | Vue | ~3.3.13 |
| Routing | vue-router | ~4.5.1 |
| Language | TypeScript | 5.9.3 |
| Build | webpack 5 + ts-loader + vue-loader + pug + less | — |
| Packaging | electron-builder + electron-updater | ^26.9.0 / 6.8.4 |
| Database | better-sqlite3 | ^12.9.0 |
| HTTP | needle (fork), undici | — |
| WebSocket (sync) | ws | ^8.20.0 |
| Worker RPC | comlink, message2call | — |
| Audio metadata | music-metadata, node-id3 | — |
| Crypto | crypto-js | ^4.2.0 |
| Misc | iconv-lite, jschardet, image-size, font-list, sortablejs, @simonwep/pickr | — |
| Runtime floor | Node | >= 22 (build), npm >= 8.5.2 |
Note
The desktop and mobile apps share design but not code; the mobile project (
lyswhut/lx-music-mobile) is the sync peer for the LAN sync feature.
1.5 Boot sequence
src/main/index.ts runs, in order:
initGlobalData()— buildsglobal.lx(settings, events, worker, hotkey/theme/player state) andglobal.envParamsfrom parsed CLI/deeplink params.initSingleInstanceHandle()—app.requestSingleInstanceLock(); a second instance forwards its deeplink/args to the running one.applyElectronEnvParams()— applies-dha,-dhmkh, Linux GL workaround,--disable-gpu-sandbox, and-proxy-server/-proxy-bypass-list.setUserDataPath()— selects theportablefolder on Windows if present, then ensuresuserData/LxDatas.registerDeeplink()— registerslxmusic://and anopen-urlhandler.listenerAppEvent()— installsweb-contents-createdsecurity listeners and lifecycle handlers.- On
app.whenReady()→initAppSetting()(init hotkeys, DB, settings, theme, migrations) →registerModules()→app_initedevent.
2. System Requirements
| Requirement | Detail |
|---|---|
| OS | Windows 7+, macOS, Linux |
| Arch | x64, x86 (32-bit), ARM64 (build-dependent) |
| Windows 7 | Aero/DWM for transparent windows; .NET Framework 4.7.1+ for display issues; PowerShell 5.1+ for desktop-lyric font enumeration |
| Build toolchain | Node >= 22, npm >= 8.5.2, Python + native build tools for better-sqlite3 (electron-builder install-app-deps runs postinstall) |
| Disk | Install footprint plus user data, downloads, and lyric cache |
3. Configuration Reference
3.1 Where configuration lives
LX Music has no system-wide config file and no environment-variable configuration for the desktop client itself. All user configuration is stored in JSON and SQLite files inside the per-user data directory.
| OS | Data directory |
|---|---|
| Windows | %APPDATA%/lx-music-desktop (or a portable/userData folder beside the exe) |
| Linux | $XDG_CONFIG_HOME/lx-music-desktop or ~/.config/lx-music-desktop |
| macOS | ~/Library/Application Support/lx-music-desktop |
Persistent stores (managed by the Store class in src/main/utils/store.ts, with the SQLite DB managed by the worker dbService):
| Store name | Holds |
|---|---|
config_v2 |
The application settings map (Section 3.3) |
data |
View/session state (last view, play info, search history, scroll positions, etc.) |
sync |
Sync server/client state |
hot_key |
Hotkey configuration |
user_api |
Imported custom-source scripts and metadata |
lyrics / lyrics_edited |
Raw and user-edited lyric cache |
theme |
Theme data |
sound_effect |
Equalizer / audio-effect state |
LxDatas/lx.data.db |
better-sqlite3 (WAL) database: lists, list items, download records, music-URL cache, other-source mappings |
Important
The list database
lx.data.db(with its-wal/-shmsidecars) is the irreplaceable user data. Back it up before sync experiments or major upgrades. On a failed schema verification at startup, the app backs up the bad DB tolx.data.db.<timestamp>.bakand reinitializes — data may be lost, so keep your own backups.
3.2 Settings groups
The settings map is namespaced by prefix. The complete default map is in src/common/defaultSetting.ts (config schema version 2.1.0). Groups:
| Prefix | Concern |
|---|---|
common.* |
Window size, font, language, interface source, animations, control-button position, auto-update, transparency |
player.* |
Quality, loop mode, volume, mute, playback rate, lyric display, audio-output device, audio visualization |
player.soundEffect.* |
10-band equalizer, convolution reverb, stereo panner, pitch shifter |
playDetail.* |
Full-window lyric view styling |
desktopLyric.* |
Desktop lyric window geometry, behavior, and styling |
list.* |
List behavior (click-to-play, source display, scroll memory, add-location) |
download.* |
Download enablement, path, filename, concurrency, lyric/cover embedding |
search.* |
Hot/history search, search-box focus |
network.proxy.* |
In-app interface proxy |
tray.* |
Tray enablement and icon theme |
sync.* |
LAN sync mode, port, snapshots, client host |
openAPI.* |
Open API enablement, port, LAN binding |
theme.* |
Active theme and light/dark theme IDs |
odc.* |
"Online detail capture" search-input behaviors |
3.3 Notable defaults
These are exact values from defaultSetting.ts (omitting purely cosmetic keys for brevity; the file is authoritative):
| Key | Default | Notes |
|---|---|---|
common.apiSource |
temp |
The interface source resolving playback URLs |
common.windowSizeId |
3 |
Window-size preset |
common.fontSize |
16 |
— |
common.langId |
null |
Falls back to system locale |
common.transparentWindow |
true (false on macOS) |
Drives the -dt default |
common.tryAutoUpdate |
true |
Auto-update on launch (Windows Setup builds) |
player.playQuality |
128k |
Default playback quality |
player.togglePlayMethod |
listLoop |
Loop mode |
player.volume |
1 |
0–1 range |
player.preservesPitch |
true |
Pitch preserved on rate change |
download.enable |
false |
Downloads off until enabled |
download.savePath |
~/Desktop |
Default download folder |
download.fileName |
歌名 - 歌手 |
Filename template |
download.maxDownloadNum |
3 |
Concurrent downloads |
network.proxy.enable |
false |
In-app proxy off |
tray.enable |
false |
Tray off by default |
sync.mode |
server |
server or client |
sync.server.port |
23332 |
LAN sync port |
sync.server.maxSsnapshotNum |
5 |
Retained list snapshots |
openAPI.enable |
false |
Open API off |
openAPI.port |
23330 |
Open API port |
openAPI.bindLan |
false |
false = bind 127.0.0.1; true = bind 0.0.0.0 |
theme.id |
green |
Active theme (happy_new_year in Jan–Feb) |
Important
openAPI.bindLan = trueexposes the Open API on all interfaces with no authentication andAccess-Control-Allow-Origin: *. Treat it as a deliberate, network-facing decision (Section 7.4).
3.4 Audio effects
player.soundEffect.* exposes a full DSP chain, all defaulting to neutral/off:
- Equalizer: 10 biquad bands —
hz31,hz62,hz125,hz250,hz500,hz1000,hz2000,hz4000,hz8000,hz16000(gain, default0). - Convolution reverb:
convolution.fileName,convolution.mainGain(default10),convolution.sendGain. - Stereo panner:
panner.enable,panner.soundR,panner.speed. - Pitch shifter:
pitchShifter.playbackRate.
4. Build & Distribution
4.1 Building from source
git clone https://github.com/lyswhut/lx-music-desktop.git
cd lx-music-desktop
npm install # postinstall runs `electron-builder install-app-deps`
npm run dev # development with HMR
Production builds compile each surface independently:
npm run build:main # main process
npm run build:renderer # main UI renderer
npm run build:renderer-lyric # desktop-lyric renderer
npm run build:renderer-scripts # injected renderer scripts
npm run build # full pack via build-config/pack.js
Note
Development uses webpack-dev-server on
http://localhost:9080for the renderer; the main process loads that URL in dev andfile://…/index.htmlin production. Contributions target thedevbranch.
4.2 Packaging matrix
electron-builder produces a large artifact matrix via build-config/build-pack.js (driven by target, arch, type). Representative npm scripts:
| Platform | Types | Architectures |
|---|---|---|
| Windows | setup (NSIS, auto-update), portable, green (7z), win7_setup, win7_green |
x64, x86, arm64, x86_64 (combined) |
| Linux | deb, appImage, rpm, pacman |
x64, arm64, armv7l (deb) |
| macOS | dmg |
x64, arm64 |
npm run pack:win # default: win setup x64
npm run pack:linux # deb + appImage + rpm + pacman
npm run pack:mac # dmg x64 + arm64
Publishing variants append publish=always (e.g. publish:win:setup:x64) and are wired through node publish / GitHub Releases.
Tip
The combined
x86_64Windows build ships both 32- and 64-bit payloads and selects the correct one at install time. Use it when you don't know the target architecture.
4.3 Auto-update behavior
electron-updater (configured in src/main/modules/winMain/autoUpdate.ts) checks GitHub on launch for new releases and prompts after download. Important constraints:
- Auto-update applies to Windows Setup builds only.
- Windows green/portable, macOS, and Linux builds do not auto-update — distribute and update them manually.
common.tryAutoUpdategates the check;common.showChangeLogcontrols the changelog prompt.
4.4 Code signing
The published binaries are unsigned. Expect Gatekeeper quarantine on macOS (xattr -rd com.apple.quarantine) and antivirus heuristics on Windows. Packagers redistributing builds should sign them with their own certificates and document that fact.
5. Operations
This is a desktop client; "operations" means the local maintenance and the optional local services it can host.
5.1 Routine maintenance checklist
- [ ] Back up
LxDatas/lx.data.db(+-wal/-shm) periodically and before upgrades - [ ] Keep the app updated (Setup builds auto-update; others manual)
- [ ] Clear the renderer cache from Settings if disk grows (the app exposes cache size/clear)
- [ ] Review imported custom sources; remove ones you no longer trust
- [ ] If using sync, keep snapshots within
sync.server.maxSsnapshotNum
5.2 Running the LAN sync server
When sync.mode = server and sync is enabled, the app starts a WebSocket sync server on sync.server.port (default 23332). A health check responds at GET /hello with Hello~::^-^::~v4~.
- Both peers must be on the same router/LAN with AP isolation off.
- The PC firewall must allow the sync port.
- Sync state, per-user device records, and list/dislike snapshots are stored under
sync/serverin the data directory (serverInfo.json,users/,list/snapshot/, etc., perconstants_sync.ts).
Warning
Sync traffic is plaintext. Use only on trusted networks, and back up lists before the first sync. First-time sync between two non-empty devices prompts for a merge mode.
Note
For always-on, internet-reachable sync, the maintainer publishes a separate standalone sync server project (
lx-music-sync-server) you can deploy to your own server; the desktop client then runs inclientmode pointing at it.
5.3 Running the Open API
When enabled, a Node http server starts on openAPI.port (default 23330), bound to 127.0.0.1 (or 0.0.0.0 when openAPI.bindLan is true). The status panel shows the reachable address(es). See Section 8.2 for the endpoint reference.
6. Performance
- Native SQLite via better-sqlite3 runs in a dedicated worker thread (
src/main/worker/dbService), keeping list operations off the UI thread. WAL mode is used. - Renderer workers (
src/renderer/worker/{download,main}) andcomlink/message2calloffload download and processing work from the main renderer thread. player.powerSaveBlocker(default on) prevents the OS from sleeping during playback.player.isMaxOutputChannelCountandplayer.mediaDeviceIdtune audio output;player.audioVisualizationadds CPU cost when on.- Transparency (
common.transparentWindow) is GPU-dependent; disabling it (-dt) reduces compositor load on weak GPUs / Windows 7. - Hardware acceleration can be disabled (
-dha) to work around driver issues at some CPU cost.
Tip
On low-spec machines or VMs, launching with
-dt -dhatrades visual polish for stability and lower resource use.
7. Security
LX Music makes a deliberate, atypical Electron security trade-off. Document and operate it with that in mind — it is not an oversight.
7.1 The renderer trust model (intentional inversion)
The main window is created with:
webPreferences: {
nodeIntegration: true,
nodeIntegrationInWorker: true,
contextIsolation: false,
sandbox: false,
webSecurity: false,
enableWebSQL: false,
webgl: false,
spellcheck: false,
}
This is the opposite of the hardened contextBridge playbook. It is intentional: the music-platform adapters and user custom-source scripts run in the renderer and require Node APIs — cross-origin HTTP (window.lx.request bypasses CORS, which is why webSecurity:false), Buffer, and crypto. The honest framing is that renderer code (including any imported source) is trusted code running with local privileges, not sandboxed web content.
Caution
Because imported custom sources execute with full Node access, a malicious source can read/write local files and make arbitrary network requests. Only import sources you can read and trust. In managed environments, treat the
user_apistore as a code allowlist.
7.2 What contains that trust
The renderer never loads remote web content in production. Several listeners in src/main/app.ts enforce this:
will-navigateis blocked unless the target matchesnavigationUrlWhiteList.setWindowOpenHandlerdenies allwindow.open;http(s)URLs are instead opened in the external browser viashell.openExternal.will-attach-webviewstrips any preload, forcesnodeIntegration = falseon webviews, and blocks non-whitelistedsrc.- The production window loads only local
file://…/index.html. - Single-instance lock prevents multiple copies; a second launch forwards its deeplink to the first.
Warning
The protective boundary is "only local, whitelisted content loads in the privileged renderer." Anything that could load untrusted content there (a bad webview src, a malicious custom source, a navigation past the whitelist) breaks the model. Changes to these listeners or the whitelist are security-sensitive.
7.3 Scheme URL / deeplink handling
lxmusic:// deeplinks are validated against URL_SCHEME_RXP (/^lxmusic:\/\//) before processing, and second-instance args are re-parsed through parseEnvParams. Deeplinks drive search/playback (Section 8.3) and cannot, by themselves, execute arbitrary code — but treat them as untrusted input.
7.4 Local network services
| Service | Default bind | Auth | Risk |
|---|---|---|---|
| Open API | 127.0.0.1:23330 |
None | Full playback control; bindLan exposes it to the LAN with Access-Control-Allow-Origin: * |
| LAN sync | 0.0.0.0:23332 (when enabled) |
Pairing code / auth handshake | Plaintext list data on the LAN |
Caution
Keep the Open API bound to localhost unless you need remote control. Use LAN sync only on trusted networks; the payload is unencrypted.
7.5 Privacy notes
- The audio-output-switch feature calls
MediaDevices.enumerateDevices(), which some security tools misreport as camera access. The camera is not used; the indicator light does not turn on. - Spell-check dictionary downloads are disabled (the dictionary URL is pointed at
0.0.0.0). - The maintainer states the first-party code contains no malicious or telemetry behavior; binaries are built by public CI from open source. Supply-chain risk from third-party dependencies is acknowledged and mitigated by favoring widely-used packages.
8. Developer Internals
8.1 Repository structure
src/
common/ Shared: config.ts, constants.ts, constants_sync.ts,
defaultSetting.ts, defaultHotKey.ts, hotKey.ts,
ipcNames.ts, mainIpc.ts, rendererIpc.ts, theme/, lang/, utils/
main/ Main process
app.ts Lifecycle, security, data path, deeplink
index.ts Boot entry (index-dev.ts for dev tooling)
modules/ winMain, winLyric, tray, appMenu, hotKey, userApi,
openApi, sync, commonRenderers
worker/ dbService (better-sqlite3) + modules per table
utils/ Store, settings, theme, migrate, logInit
renderer/ Main UI (Vue 3)
views/ Search, List (MyList), Leaderboard, songList, Download, Setting
core/ music, player, useApp (composition)
store/ Vue-reactive state per domain
plugins/ Dialog, Tips, SvgIcon, player
utils/musicSdk/ bd, kg, kw, mg, tx, wy source adapters
worker/ download, main renderer workers
renderer-lyric/ Desktop-lyric renderer (LyricHorizontal/Vertical)
lang/ i18n message catalogs
build-config/ webpack + electron-builder packaging
publish/ release/changelog tooling
8.2 Open API reference
Base URL: http://<host>:<openAPI.port> (default http://127.0.0.1:23330). All endpoints are HTTP GET, return Access-Control-Allow-Origin: *, and are unauthenticated. Source: src/main/modules/openApi/index.ts.
| Endpoint | Query | Returns |
|---|---|---|
/status |
filter=<csv of status keys> |
JSON subset of player status (default keys: status,name,singer,albumName,lyricLineText,duration,progress,playbackRate) |
/lyric |
— | Raw current lyric text |
/lyric-all |
— | JSON {lyric, tlyric, rlyric, lxlyric} |
/play |
— | Start playback |
/pause |
— | Pause |
/skip-next |
— | Next track |
/skip-prev |
— | Previous track |
/seek |
offset=<seconds> |
Seek (validated 0 ≤ offset ≤ duration, else 400 Invalid offset) |
/collect |
— | Favorite current track |
/uncollect |
— | Unfavorite |
/volume |
volume=<0–100> |
Set volume (else 400 Invalid volume) |
/mute |
mute=true|false |
Mute/unmute (else 400 Invalid mute value) |
/subscribe-player-status |
filter=<csv> |
SSE stream; each status field is an event (event: <key>\ndata: <json>) |
Unknown paths return 401 Forbidden. The SSE stream pushes an initial snapshot of the filtered keys, then live updates as player status changes.
# Live status stream, only name + lyric line
curl -N "http://127.0.0.1:23330/subscribe-player-status?filter=name,lyricLineText"
event: name
data: "突然的自我"
event: lyricLineText
data: "让我掉下眼泪的"
Note
The status keys correspond to
LX.Player.Status(e.g.status,name,singer,albumName,picUrl,progress,duration,playbackRate,lyricLineText,lyric,tlyric,rlyric,lxlyric,collect,volume,mute).filterselects a subset; an empty/invalid filter falls back to the default key set.
8.3 Scheme URL reference
URLs begin with lxmusic://. Two parameter styles exist:
data style — URL-encoded JSON payload, for complex calls: lxmusic://<path>?data=<urlencoded-json>
| Action | Path | Key parameters |
|---|---|---|
| Open song list | songlist/open |
source (req), one of id/url (req) |
| Play song list | songlist/play |
source (req), one of id/url (req), index (opt, 0-based) |
| Search | music/search |
keywords (req), source (opt) |
| Play song | music/play |
name, singer, source, songmid (all req), plus types array and platform-specific keys (hash for kg, strMediaMid for tx, copyrightId for mg, …) |
URL style — simple positional calls (since v1.18.0), still URL-encoded:
| Action | Path |
|---|---|
| Search | music/search/{source}/{keywords} (source optional) |
| Open song list | `songlist/open/{source}/{id |
Conventions: source values are kw/kg/tx/wy/mg; quality values are 128k/320k/flac/flac24bit. A companion userscript (lx-music-script) integrates these into music websites.
8.4 Custom-source scripting API (window.lx)
Custom sources are UTF-8 JavaScript (ES6+) files whose job is to resolve playback URLs. They communicate with the app through an event-style API exposed as window.lx. A source must begin with a metadata header comment:
/**
* @name Example Source
* @description Demo source
* @version 1.0.0
* @author you
* @homepage http://example.com
*/
@name is required (≤24 chars recommended); @description must remain present even if empty; the others are optional.
API surface:
| Member | Purpose |
|---|---|
window.lx.version |
Custom-source API version (changes on API breaks) |
window.lx.EVENT_NAMES |
Event-name constants: inited, request, updateAlert |
window.lx.on(event, handler) |
Register a handler — must return a Promise |
window.lx.send(event, data) |
Send an event to the app |
window.lx.request(url, options, cb) |
HTTP request not subject to CORS; options: method/headers/body/form/formData/timeout; returns a cancel function |
window.lx.utils.buffer.from / bufToString |
Node Buffer.from / buffer→string |
window.lx.utils.crypto.{aesEncrypt,md5,randomBytes,rsaEncrypt} |
Crypto helpers |
Lifecycle:
- Register a handler for
EVENT_NAMES.request. The handler receives{ source, action, info }; foraction === 'musicUrl',info = { type, musicInfo }wheretypeis the quality (128k/320k/flac/flac24bit) andmusicInfocarries track identifiers. It must return aPromiseresolving to the audio URL. - After setup, send
EVENT_NAMES.initedwith{ status, openDevTools, sources }, declaring which platforms (kw/kg/tx/wy/mg) the script supports and each one'sactions(currently['musicUrl']) andqualitys. - Optionally send
EVENT_NAMES.updateAlert(once per run) with{ log, updateUrl }to prompt users to update the source.
const { EVENT_NAMES, request, on, send } = window.lx
on(EVENT_NAMES.request, ({ source, action, info }) => {
if (action === 'musicUrl') {
return resolveUrl(source, info.musicInfo, info.type) // returns a Promise<string>
}
})
send(EVENT_NAMES.inited, {
status: true,
openDevTools: false,
sources: {
kw: { name: '酷我音乐', type: 'music', actions: ['musicUrl'], qualitys: ['128k', '320k', 'flac', 'flac24bit'] },
},
})
Caution
This API gives sources cross-origin HTTP and crypto/Buffer access by design. The trust boundary in Section 7.1 applies directly — a source is fully-trusted local code.
8.5 IPC and events
Main↔renderer messaging uses helpers in src/common/mainIpc.ts / rendererIpc.ts with names declared in ipcNames.ts, layered on message2call. Within the main process, an EventEmitter-based app event bus (global.lx.event_app, plus event_list and event_dislike in src/main/event/) coordinates modules — e.g. updated_config notifies tray, theme, and proxy logic of setting changes; player_status fans out to the tray, Open API SSE subscribers, and taskbar.
Note
There are no decorator-based HTTP routes, no REST controllers, and no AST-discoverable command registry in this codebase. The "API surfaces" are (a) the Open API HTTP endpoints, (b) the
lxmusic://scheme, (c) thewindow.lxsource API, and (d) the internal IPC/event buses. Don't look for an OpenAPI spec or a CLI — there is none.
8.6 Data model (better-sqlite3)
The dbService worker manages tables for lists and list items, downloads, lyric cache, music-URL cache, "other source" mappings, and dislike lists (src/main/worker/dbService/modules/*). Built-in list IDs (constants.ts): default, love, temp, download, and a null-keyed play-later queue. Download status values: run, waiting, pause, error, completed.
8.7 Internationalization & theming
- i18n: message catalogs in
src/lang/(and a small inline catalog intray.tsforen-us/zh-cn/zh-tw).common.langIdselects the language;nullfollows the system. - Themes: built-in themes are generated by
src/common/theme/createThemes.js(npm run build:theme); the live theme can follow the system via theautotheme, with separatetheme.lightId/theme.darkId. The color picker uses@simonwep/pickr.
8.8 Things that do NOT exist (negative knowledge)
To prevent false assumptions:
- No command-line interface for end-user operations (only Electron startup flags / scheme URLs).
- No environment-variable configuration of the desktop client; no
/etc/lx-music/, no registry config tree. - No user accounts, no cloud login in the desktop app itself (NetEase "我喜欢" uses a manually-injected cookie token, not an app account).
- No first-party telemetry or analytics.
- No HTTP REST API beyond the optional, local, unauthenticated Open API.
- No auto-update for macOS, Linux, or Windows green/portable builds.
- No audio hosting — the app never owns audio; sources resolve URLs.
- The codebase is not a monorepo of multiple products; it is one desktop app with two renderer surfaces.
8.9 Testing & quality
The project uses ESLint with eslint-config-standard-with-typescript, eslint-plugin-vue, and pug linting (npm run lint / lint:fix). There is no separate unit-test suite in the build scripts; verification is via building each surface and manual QA. Contributions go to the dev branch and should reference an issue for new features.
8.10 Potential improvements
- The renderer's
webSecurity:false/nodeIntegration:trueposture is necessary for the current source model but is the single largest attack surface. A longer-term path is moving source execution into an isolated process or acontextBridge-mediated sandbox with a capability-limitedwindow.lx. - The Open API has no auth or rate limiting; an optional token (even a shared secret in the URL/header) would make
bindLansafer. - Sync transmits in plaintext; optional TLS or symmetric encryption would let it cross untrusted networks.
- macOS/Linux lack auto-update; integrating platform updaters would close the manual-update gap.
8.11 Contributing
- Set up the dev environment per the project's "use source code" docs and
npm install. - Branch from and target
dev. - For features, open an issue first to confirm scope; for fixes, include before/after behavior and reproduction steps.
- Run
npm run lint:fixbefore submitting.
9. Quick Reference
9.1 Local services
| Service | Port (default) | Bind | Auth | Setting |
|---|---|---|---|---|
| Open API | 23330 |
localhost / LAN | none | openAPI.enable, openAPI.bindLan |
| LAN sync | 23332 |
LAN | pairing/auth | sync.enable, sync.mode, sync.server.port |
9.2 Default hotkeys
| Action | In-app | Global (off by default) |
|---|---|---|
| Play/pause | Ctrl+F5 |
Ctrl+Alt+F5 |
| Previous | Ctrl+← |
Ctrl+Alt+← |
| Next | Ctrl+→ |
Ctrl+Alt+→ |
| Volume up/down | — | Ctrl+Alt+↑ / Ctrl+Alt+↓ |
| Focus search | F1 |
— |
| Toggle/lock/top desktop lyrics | — | Ctrl+Alt+0 / Ctrl+Alt+- / Ctrl+Alt+= |
(macOS: Ctrl → Command.)
9.3 Key paths
| Item | Path |
|---|---|
| Data dir (Win) | %APPDATA%/lx-music-desktop (or portable/userData) |
| Data dir (Linux) | ~/.config/lx-music-desktop |
| Data dir (macOS) | ~/Library/Application Support/lx-music-desktop |
| List database | <data>/LxDatas/lx.data.db (+ -wal, -shm) |
| Sync server data | <data>/sync/server/ |
9.4 Source codes
kw Kuwo · kg Kugou · tx QQ Music (Tencent) · wy NetEase · mg Migu · bd Baidu (adapter present)