Jesse A. Green / notebook

LX Music Desktop Administrator & Developer Guide

Generated June 2026 from lyswhut/lx-music-desktop at 9c364b4

AI-generated documentation. The library is checked by sampling (three repositories so far); this guide was not individually reviewed. Verify against the project before relying on it.

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:

  1. initGlobalData() — builds global.lx (settings, events, worker, hotkey/theme/player state) and global.envParams from parsed CLI/deeplink params.
  2. initSingleInstanceHandle() — app.requestSingleInstanceLock(); a second instance forwards its deeplink/args to the running one.
  3. applyElectronEnvParams() — applies -dha, -dhmkh, Linux GL workaround, --disable-gpu-sandbox, and -proxy-server/-proxy-bypass-list.
  4. setUserDataPath() — selects the portable folder on Windows if present, then ensures userData/LxDatas.
  5. registerDeeplink() — registers lxmusic:// and an open-url handler.
  6. listenerAppEvent() — installs web-contents-created security listeners and lifecycle handlers.
  7. On app.whenReady() → initAppSetting() (init hotkeys, DB, settings, theme, migrations) → registerModules() → app_inited event.

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/-shm sidecars) 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 to lx.data.db.<timestamp>.bak and 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 = true exposes the Open API on all interfaces with no authentication and Access-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, default 0).
  • Convolution reverb: convolution.fileName, convolution.mainGain (default 10), 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:9080 for the renderer; the main process loads that URL in dev and file://…/index.html in production. Contributions target the dev branch.

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_64 Windows 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.tryAutoUpdate gates the check; common.showChangeLog controls 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/server in the data directory (serverInfo.json, users/, list/snapshot/, etc., per constants_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 in client mode 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}) and comlink/message2call offload download and processing work from the main renderer thread.
  • player.powerSaveBlocker (default on) prevents the OS from sleeping during playback.
  • player.isMaxOutputChannelCount and player.mediaDeviceId tune audio output; player.audioVisualization adds 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 -dha trades 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_api store 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-navigate is blocked unless the target matches navigationUrlWhiteList.
  • setWindowOpenHandler denies all window.open; http(s) URLs are instead opened in the external browser via shell.openExternal.
  • will-attach-webview strips any preload, forces nodeIntegration = false on webviews, and blocks non-whitelisted src.
  • 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.

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). filter selects 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:

  1. Register a handler for EVENT_NAMES.request. The handler receives { source, action, info }; for action === 'musicUrl', info = { type, musicInfo } where type is the quality (128k/320k/flac/flac24bit) and musicInfo carries track identifiers. It must return a Promise resolving to the audio URL.
  2. After setup, send EVENT_NAMES.inited with { status, openDevTools, sources }, declaring which platforms (kw/kg/tx/wy/mg) the script supports and each one's actions (currently ['musicUrl']) and qualitys.
  3. 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) the window.lx source 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 in tray.ts for en-us/zh-cn/zh-tw). common.langId selects the language; null follows 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 the auto theme, with separate theme.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:true posture 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 a contextBridge-mediated sandbox with a capability-limited window.lx.
  • The Open API has no auth or rate limiting; an optional token (even a shared secret in the URL/header) would make bindLan safer.
  • 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

  1. Set up the dev environment per the project's "use source code" docs and npm install.
  2. Branch from and target dev.
  3. For features, open an issue first to confirm scope; for fixes, include before/after behavior and reproduction steps.
  4. Run npm run lint:fix before 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)