# CLAUDE.md 声匣:自托管音乐下载 Web 服务(网易云 + QQ 音乐),本机 `npm start` 或 NAS Docker 运行。UI、注释、日志均为中文。 ## 命令 - `npm run dev` — 后端 `node --watch server/src/main.ts`(:8066)+ Vite(:5173,代理 `/api`) - `npm start` — 生产模式(需先 `npm run build`) - `npm run typecheck` — `tsc`(server)+ `vue-tsc`(web)。改动后必须通过 - `npm run smoke -- [quality] [album|flat]` — 端到端跑一次下载流水线(会导入 QQ Cookie) 没有测试框架;用 smoke、curl `/api/*`,以及无头浏览器截图验证。测试下载请用 `MUSIC_DIR` / `DATA_DIR` 指向临时目录,不要污染 `./music` 与 `./data`。 ## 架构 单个 package.json: - `server/src/` — Koa 3,**Node 原生 TypeScript 类型剥离直接运行,无构建步骤** - 只能用可擦除语法(`erasableSyntaxOnly`):不能用 enum / namespace / 参数属性 - 相对导入必须写 `.ts`;类型导入用 `import type`(`verbatimModuleSyntax`) - `core/` 与平台无关:`pipeline.ts`(下载流水线)、`tagging.ts`、`lyrics.ts`、`layout.ts`、`quality.ts`、`net/http.ts` - `core/providers/{netease,qq}/` 实现 `MusicProvider`(`core/types.ts`),在 `providers/index.ts` 注册;`resolveLink` 处理分享短链 - QQ:`qq/client.ts` 的 `musicu()` 在 comm 中带 `uin` + `authst` 并附 Cookie —— 缺这一步拿不到 VIP 音源 - `queue.ts` — 任务存 SQLite `tasks` 表,进度只在内存,经 `events.ts` 的 bus 推送;`routes/api.ts` 的 `/api/events` 是 SSE - `playlists.ts` — 自建歌单(`playlists` / `playlist_items` 表);条目要么是在线曲目(source + track_id)要么是曲库文件(path),`item_key` 去重。读取时给在线曲目补 `localFile`、给文件补 `missing`。`importPlaylist` 经 `resolveLink` 把网易云 / QQ 歌单或专辑链接导入为新歌单(`POST /api/playlists/import`) - `accounts.ts`(Cookie 用 `secret.ts` AES-GCM 加密存储)、`settings.ts`(运行时设置)、`auth.ts`(可选访问密码)、`library.ts`(曲库工具)、`stream.ts`(在线播放代理,音质取设置 `playQuality`;`/:source/stream-alt` 按歌名 + 歌手 + 时长到另一个平台找同一首歌) - `db.ts` — `node:sqlite`;history / tasks / settings / playlists / playlist_items 表 - `web/` — Vite 8 + Vue 3.5 + Vue Router 5,无 UI 库 - `lib/store.ts` 全局状态 + SSE;`lib/api.ts` 类型与请求 - `lib/player.ts` 播放器:单个 `