完成网络音乐、工具页面与安装器体验升级

This commit is contained in:
2026-08-18 13:54:33 +08:00
parent 337390f53e
commit 149c28082d
1082 changed files with 26442 additions and 162838 deletions
+126
View File
@@ -0,0 +1,126 @@
/**
* @fileoverview KuGouMusicApi 程序化 API 入口模块
*
* 本模块是项目的编程式调用入口(区别于 index.js 的 HTTP 服务入口),
* 负责:
* 1. 扫描 `module/` 目录下所有 API 模块文件并动态加载
* 2. 为每个模块创建统一的包装函数,自动处理 Cookie 格式转换
* 3. 将所有 API 函数与服务器工具、请求工具合并后统一导出
*
* 导出结构为扁平对象,模块文件名(去 .js 后缀)即为 API 函数名。
* 例如:`module/search.js` → `api.search(params)`
*
* @module app
* @requires node:fs
* @requires path
* @requires ./util - 工具函数(cookieToJson 等)
* @requires ./server - 服务器管理(startService、getModulesDefinitions
* @requires ./util/request - 底层 HTTP 请求工具(createRequest
*
* @example
* // 作为库使用(编程式调用,不启动 HTTP 服务)
* const api = require('./app');
*
* // 手机登录
* const loginRes = await api.login_cellphone({
* mobile: '13800138000',
* code: '123456'
* });
*
* // 搜索音乐(携带认证 Cookie)
* const searchRes = await api.search({
* keywords: '海阔天空',
* cookie: `token=${loginRes.body.token};userid=${loginRes.body.userid}`
* });
*/
const fs = require('node:fs');
const path = require('path');
const { cookieToJson } = require('./util');
/**
* 动态注册的 API 函数集合
*
* 键为模块文件名(去 .js 后缀),值为对应的包装函数。
* 例如:`{ search: [Function], login_cellphone: [Function], ... }`
*
* @type {Record<string, (data?: Record<string, any>) => Promise<any>>}
*/
let obj = {};
/**
* ============================================================
* 动态扫描并加载 module/ 目录下的所有 API 模块
* ============================================================
*
* 扫描流程:
* 1. 使用 `fs.readdirSync` 同步读取 `module/` 目录下所有文件
* 2. 对文件列表进行倒序排列(与 server.js 中的路由注册顺序保持一致)
* 3. 过滤出以 `.js` 结尾的文件
* 4. 对每个模块文件:
* a. 通过 `require` 加载模块,获取模块处理函数
* b. 从文件名提取 API 函数名(去掉 `.js` 后缀,如 `search.js` → `search`
* c. 创建包装函数并注册到 `obj` 对象中
*
* 每个包装函数的作用:
* - 将调用者传入的 cookie 字符串自动转换为 JSON 对象
* - 合并调用参数与默认 cookie
* - 调用原始模块处理函数,同时注入请求工厂函数(lazy 加载 createRequest
*/
fs.readdirSync(path.join(__dirname, 'module'))
.reverse()
.forEach((file) => {
// 跳过非 .js 文件(如 .json、.map、目录等)
if (!file.endsWith('.js')) return;
// 加载模块,获取其导出的处理函数
let fileModule = require(path.join(__dirname, 'module', file));
// 提取 API 函数名:取文件名中第一个 `.` 之前的部分
// 例如:`search.js` → `search``login_cellphone.js` → `login_cellphone`
let fn = file.split('.').shift() || '';
/**
* 为当前模块创建包装函数
*
* @param {Record<string, any>} [data={}] - 调用者传入的请求参数
* @returns {Promise<any>} 模块处理函数的返回结果
*/
obj[fn] = (data = {}) => {
// 自动将 cookie 字符串转换为 JSON 对象
// 调用者可以传入 cookie 字符串(如 "token=xxx;userid=xxx")或对象格式
if (typeof data.cookie === 'string') data.cookie = cookieToJson(data.cookie);
// 调用原始模块处理函数,传入两个参数:
// 1. 合并后的参数对象(确保 cookie 始终为对象格式,默认为空对象)
// 2. 请求工厂函数:延迟加载 createRequest,避免循环依赖
return fileModule({ ...data, cookie: data.cookie ? data.cookie : {} }, (...args) => {
// 延迟加载:仅在实际需要发起请求时才 require createRequest
// 这样可以避免模块加载阶段的循环依赖问题
const { createRequest } = require('./util/request');
return createRequest(...args);
});
};
});
/**
* ============================================================
* 统一导出
* ============================================================
*
* 最终导出一个扁平对象,包含三部分内容:
*
* 1. `...require('./server')` — 服务器管理工具
* - `startService()`: 启动 HTTP 服务
* - `getModulesDefinitions()`: 动态扫描模块定义
*
* 2. `...require('./util/request')` — 底层请求工具
* - `createRequest()`: 创建 HTTP 请求
*
* 3. `...obj` — 所有 API 模块函数
* - 每个 module/*.js 文件对应一个同名函数
* - 如 `search`、`login_cellphone`、`song_url` 等
*
* @type {Record<string, any> & import("./server")}
*/
module.exports = { ...require('./server'), ...require('./util/request'), ...obj };