MusicBox 架构说明
本文描述 MusicBox 当前代码结构和主要运行链路。它以仓库现状为准:主进程和渲染进程都已经从早期全局对象模式迁移到更明确的 controller/service/composition 分层,渲染进程旧 core/、services/ 目录现在仅作为兼容转发层保留。
总览
MusicBox 是一个 Electron 桌面应用,由四个主要部分组成:
Electron main process
src/main/main.ts
src/main/core/Application.ts
src/main/controllers/*.ts
src/main/services/**
|
| contextBridge + ipcRenderer/ipcMain
v
Preload boundary
src/main/preload.ts exposes window.electronAPI
|
v
Renderer process
src/renderer/src/app/**
src/renderer/src/features/**
src/renderer/src/ui/**
src/renderer/src/infrastructure/electron/**
|
v
Native/helper modules
native/ Rust N-API audio engine
src/main/metadata_editor.py packaged as metadata_editor executable核心原则:
- 主进程负责系统能力、窗口、文件、网络磁盘、音乐库缓存、插件安装、原生音频和安全校验。
- preload 只暴露白名单 API 到
window.electronAPI,renderer 不直接访问 Node/Electron。 - renderer 负责 UI、播放运行时、功能编排和插件 host。
- Rust N-API 模块提供 WASAPI shared/exclusive 等原生音频能力,Web Audio 作为 renderer 内可用的播放引擎。
- Python helper 只处理音频元数据写入相关任务,构建时打包到
dist/main。
启动流程
src/main/main.ts创建Application,在app.whenReady()后启动。Application.applyConfiguration()根据用户配置处理硬件加速和 GC 标志。Application.initializeCoreServices()注册核心服务,但尽量延迟实例化重型服务。Application.registerStartupControllers()在窗口创建前注册 IPC controller,确保 renderer 首屏加载时 preload API 可用。WindowManager.createMainWindow()创建无边框主窗口并加载src/renderer/public/index.html。- 主窗口显示后,后台加载音乐库缓存并向 renderer 发送
library:updated。 - renderer 入口
src/renderer/src/app/bootstrap/main.ts加载基础工具、API、插件框架、UI component 和应用启动代码。 createAppComposition()组装生命周期、路由、播放、音乐库、插件、快捷键和 UI facade。
主进程结构
src/main/ 是 Electron main process 的 TypeScript 源码。
src/main/
main.ts Electron app lifecycle 入口
preload.ts contextBridge 白名单 API
controllers/ IPC controller
core/ 应用生命周期、配置、窗口、服务容器
services/ 主进程业务服务
utils/ 路径安全、文件搜索、元数据等工具
metadata_editor.py 元数据编辑 helper 源码core
Application.ts:启动、关闭、服务注册、controller 注册、后台初始化。WindowManager.ts:主窗口和桌面歌词窗口创建、窗口尺寸持久化、托盘关闭行为、窗口间消息转发。ServiceContainer.ts:轻量服务容器,支持延迟实例化。ConfigManager.ts:读取和保存app.getPath('userData')下的 JSON 配置。
controllers
controller 使用 @Controller、@IpcHandle、@IpcOn 装饰器声明 IPC 通道,并继承 BaseController 统一注册和注销。
典型 controller:
AppController、WindowController、DialogController:应用、窗口、对话框能力。LibraryController:本地/网络音乐扫描、缓存、歌单、元数据。NativeAudioController:Rust N-API 音频引擎桥接。NetworkController:SMB / WebDAV 挂载、状态、目录结构。ExtensionsController:插件安装、启用禁用、文件读取、插件存储。DesktopLyricsController、TrayController、GlobalShortcutsController:桌面歌词、托盘、全局快捷键。
services
services/library/LibraryCacheManager.ts管理music-library-cache.json、歌单、忽略列表和缓存校验。services/library/MetadataHandler.ts负责元数据读取/写入相关处理。services/network/NetworkDriveManager.ts和NetworkFileAdapter.ts抽象 SMB / WebDAV 文件访问。services/extensions/ExtensionInstaller.ts管理外部 ZIP 插件安装到用户数据目录。services/extensions/ExtensionStorageService.ts为插件提供global/workspaceJSON 存储。
Preload 与 IPC 边界
src/main/preload.ts 通过 contextBridge.exposeInMainWorld('electronAPI', ...) 暴露能力。BrowserWindow 配置中 nodeIntegration: false、contextIsolation: true、webSecurity: true。
当前暴露的能力按命名空间组织:
app、window、dialog、tray、hardwareAccelerationlibrary、settings、userdatamedia、lyrics、covers、equalizerPresetsaudio、nativeAudio、onNativeAudioEventnetworkDrive、globalShortcuts、desktopLyricsextensions、httpServer、benchmark、memory
通用 Node API 已默认关闭。只有设置 MUSICBOX_ENABLE_LEGACY_NODE_APIS=1 时,preload 才会暴露有限的兼容 fs、os、path 方法。新代码应使用领域 API 或 src/renderer/src/infrastructure/electron/*Gateway.ts,不要重新引入通用文件系统访问。
渲染进程结构
渲染进程位于 src/renderer/,由 Vite 构建。Vite root 是 src/renderer/src,构建产物输出到 src/renderer/public,入口包括主窗口 index.html 和桌面歌词窗口 DesktopLyrics.html。
src/renderer/src/
app/ 应用启动、组合根、生命周期、运行时协调
features/ 按业务域拆分的功能模块
ui/ 页面、弹窗、组件和基础 Component
infrastructure/electron/ Electron API gateway
api/ MusicBoxAPI、播放运行时 API 和类型
extensions/ 插件 host、API、内置插件、示例和文档
shared/ 跨功能共享的缓存、歌词、网络、类型
styles/ SCSS 分层样式
assets/ 图标和图片
core/ deprecated 兼容转发层
services/ deprecated 兼容转发层app 层
app/bootstrap/main.ts 是 renderer 的真实入口。它先加载基础设施和 UI component,再导入 ./app 创建应用实例。
app/composition/AppCompositionRoot.ts 是重构后的组合根,负责把运行时 controller、feature service、UI facade 和 legacy app bridge 接到一起。新增跨功能协作时,应优先在组合根显式注入依赖,而不是让模块互相访问全局对象。
app/runtime/MusicBoxApp.ts 保留应用级 facade,兼容现有插件和 UI 调用,同时把具体行为转发到 feature controller/service。
features 层
features/ 按业务域组织 renderer 逻辑,例如:
playback:播放状态、队列、播放服务、Web Audio / WASAPI engine adapter。library:音乐库数据、扫描入口、元数据编辑、UI 绑定。mediaAssets:歌词、封面、本地/内嵌/在线资源解析。equalizer:图形均衡器、参量均衡器、预设文件。desktopLyrics:桌面歌词窗口同步和渲染控制。settings:设置页面 controller、renderer、service。extensions:插件管理 UI 和插件 host service。networkDrive:网络磁盘管理和详情页数据。
infrastructure/electron 层
infrastructure/electron/ElectronBridge.ts 是 window.electronAPI 的类型化访问基础。各 *Gateway.ts 将 preload API 包装成领域方法,例如 libraryGateway、nativeAudioGateway、desktopLyricsGateway、settingsSystemGateway。
Renderer feature 不应直接散落调用 window.electronAPI。新增 IPC 后,推荐链路是:
main controller -> preload namespace -> infrastructure/electron gateway -> feature service/controller -> UI兼容层
src/renderer/src/core/** 和 src/renderer/src/services/** 已标记 @deprecated。这些文件只能包含注释和 import/export forwarding,由 src/renderer/scripts/check-architecture-boundaries.mjs 保护。新增代码不要从 @/core、@/services 或旧 @js 路径导入。
音频架构
MusicBox 当前有两类播放实现:
- Web Audio engine:位于
features/playback/service/audioEngine/webAudio/,处理浏览器侧解码、播放、预加载、进度、可见性和对象 URL 生命周期。 - WASAPI/native engine:renderer 通过
WasapiEngine.ts和nativeAudioGateway调用主进程NativeAudioController,再桥接dist/main/NativeAudio.node。
PlaybackService 和 AudioEngineAdapter 负责切换引擎、统一播放控制、播放列表、进度、音量、播放模式、均衡器和桌面歌词同步。native/ 中的 Rust crate 使用 N-API 暴露原生音频能力,并在 npm run build:rs 时复制到 dist/main/NativeAudio.node。
音乐库与元数据
音乐库数据由主进程缓存,renderer 通过 libraryGateway 和 LibraryBridge 获取。缓存文件存放在 Electron userData 目录:
music-library-cache.json:歌曲、歌单、扫描目录、忽略列表、统计信息。music-folders-settings.json:音乐文件夹和自动扫描设置。window-config.json:主窗口尺寸和桌面歌词位置。extensions.json、extensions/、extension-storage/:外部插件和插件存储。
元数据读取主要依赖 music-metadata 等 Node 侧库;写入和复杂处理由 metadata_editor.py helper 支持,完整打包时通过 npm run build:python 生成平台可执行文件。
插件系统
插件系统位于 src/renderer/src/extensions/,参考 VS Code 扩展模型:
extensions/core/:生命周期、事件、注册表、激活器、插件服务和 sandbox runtime。extensions/api/:播放器、音乐库、UI、存储、设置、命令、快捷键、网络、窗口等标准 API。extensions/builtin/:内置插件和extensions.json索引。extensions/examples/:插件示例。extensions/docs/:插件开发文档。
外部插件通过 ZIP 安装。主进程 ExtensionInstaller 负责 manifest 校验、路径安全和文件落盘,renderer 插件 host 负责扫描、加载、激活和停用。插件应通过 context.api 访问能力,不能依赖 renderer 内部模块或 preload 的通用 Node API。
安全边界
- Renderer 禁用 Node integration,并启用 context isolation。
- preload 只暴露显式白名单能力。
- 文件系统、插件安装和扩展文件读取必须在主进程校验路径,复用
src/main/utils/pathSecurity.ts。 - 外部链接通过主进程
app:openExternal打开。 - 插件安装会阻止 ZIP path traversal,并拒绝覆盖内置插件 ID。
- 旧通用
fs/path/ospreload 能力默认关闭,仅用于迁移期兼容。
构建与发布链路
npm run build
-> npm run build:python
-> npm run build:renderer
-> npm run build:rs
-> npm run build:ts
-> electron-builder打包配置在 electron-builder.yml。应用文件包含:
dist/main/**/*:主进程编译产物、Python helper、NativeAudio.node。src/renderer/public/**/*:Vite 构建后的 renderer 静态资源。node_modules/**/*:运行时依赖,按配置排除测试、文档和无关文件。
asarUnpack 和 extraResources 会处理 NativeAudio.node 与 metadata_editor,因为它们需要作为独立二进制资源加载。
新增功能建议
新增主进程能力:
- 在
src/main/controllers/增加或扩展 controller。 - 在
src/main/preload.ts中只暴露必要方法。 - 在
src/renderer/src/infrastructure/electron/增加 gateway。 - 在对应
features/*/service中封装业务行为。 - 由 UI component 或 app composition 调用 feature service。
新增 renderer 功能:
- 优先放在对应
features/<domain>/。 - UI 放在
ui/pages、ui/widgets、ui/dialogs或ui/modals。 - 跨功能共享的纯逻辑放到
shared/。 - 不要从 deprecated
core/、services/引入新依赖。 - 运行
npm run typecheck:renderer和cd src/renderer && npm run lint。