mirror of
https://github.com/metowolf/Meting.git
synced 2026-09-03 07:27:07 +08:00
* feat: migrate from PHP to Node.js implementation - Replace PHP implementation with Node.js version - Add Node.js package configuration (package.json, package-lock.json) - Add Rollup build configuration for browser compatibility - Update README with Node.js usage examples and API documentation - Add comprehensive test suite for all supported platforms - Add Claude Code development instructions (CLAUDE.md) - Remove PHP-specific files (composer.json, src/Meting.php) - Update GitHub workflows for Node.js environment This migration maintains API compatibility while providing: - Promise-based async/await support - ES6 class design with method chaining - Zero external dependencies (Node.js built-in modules only) - Support for all existing music platforms (netease, tencent, xiami, kugou, baidu, kuwo) 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]> * refactor: 重构为 Provider 模式架构 - 引入统一的 Provider 接口,实现平台解耦 - 将原有单体文件拆分为模块化 Provider 系统 - 实现真正的内部闭环设计,每个 Provider 独立处理编码/解码 - 优化构建系统,支持版本号注入和 TypeScript 定义生成 - 完善测试覆盖,支持独立平台测试 - 新增架构文档,详细说明设计模式和开发流程 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]> * fix: 修复 ES Module 导入问题,添加 package.json exports 配置 - 添加 module 字段指向 ESM 版本构建文件 - 添加 exports 字段支持双包发布模式 - 修正 main 字段路径格式 - 解决 import Meting from '@meting/core'; 导入失败问题 Generated with [Claude Code](https://claude.ai/code) via [Happy](https://happy.engineering) Co-Authored-By: Claude <[email protected]> Co-Authored-By: Happy <[email protected]> * refactor: 简化网易云音乐架构并恢复 search option 参数支持 - 移除 WebAPI 支持,统一使用 EAPI 架构,简化代码结构 - 删除复杂的选项管理系统和全局配置 - 恢复 search 接口的 option 参数支持(type, page, limit) - 优化构建配置和代码压缩设置 - 更新文档和测试以反映新的 API 结构 Breaking Changes: - 移除 setOption() 方法 - 删除 WebAPI (weapi) 相关代码 - 简化 netease provider 为纯 EAPI 实现 🤖 Generated with [Claude Code](https://claude.ai/code) via [Happy](https://happy.engineering) Co-Authored-By: Claude <[email protected]> Co-Authored-By: Happy <[email protected]> --------- Co-authored-by: Claude <[email protected]> Co-authored-by: Happy <[email protected]>
193 lines
4.5 KiB
JavaScript
193 lines
4.5 KiB
JavaScript
/**
|
|
* Meting music framework - Node.js version (重构版本)
|
|
* https://i-meto.com
|
|
* https://github.com/metowolf/Meting
|
|
*
|
|
* Copyright 2019, METO Sheel <[email protected]>
|
|
* Released under the MIT license
|
|
*/
|
|
|
|
import { URLSearchParams } from 'url';
|
|
import ProviderFactory from './providers/index.js';
|
|
|
|
class Meting {
|
|
constructor(server = 'netease') {
|
|
this.VERSION = '__VERSION__'; // 在构建时由 rollup 替换为实际版本号
|
|
this.raw = null;
|
|
this.info = null;
|
|
this.error = null;
|
|
this.status = null;
|
|
this.temp = {};
|
|
|
|
this.server = null;
|
|
this.provider = null;
|
|
this.isFormat = false;
|
|
this.header = {};
|
|
|
|
this.site(server);
|
|
}
|
|
|
|
// 设置音乐平台
|
|
site(server) {
|
|
if (!ProviderFactory.isSupported(server)) {
|
|
server = 'netease'; // 默认使用网易云音乐
|
|
}
|
|
|
|
this.server = server;
|
|
this.provider = ProviderFactory.create(server, this);
|
|
this.header = this.provider.getHeaders();
|
|
|
|
return this;
|
|
}
|
|
|
|
// 设置 Cookie
|
|
cookie(cookie) {
|
|
this.header['Cookie'] = cookie;
|
|
return this;
|
|
}
|
|
|
|
// 设置数据格式化
|
|
format(format = true) {
|
|
this.isFormat = format;
|
|
return this;
|
|
}
|
|
|
|
// 执行 API 请求的主方法
|
|
async _exec(api) {
|
|
// 让 Provider 自己处理完整的请求流程
|
|
return await this.provider.executeRequest(api, this);
|
|
}
|
|
|
|
// HTTP 请求方法 - 使用 fetch API
|
|
async _curl(url, payload = null, headerOnly = false) {
|
|
const requestOptions = {
|
|
method: payload ? 'POST' : 'GET',
|
|
headers: { ...this.header }
|
|
};
|
|
|
|
// 处理请求体
|
|
if (payload) {
|
|
if (typeof payload === 'object' && !Buffer.isBuffer(payload) && typeof payload !== 'string') {
|
|
payload = new URLSearchParams(payload).toString();
|
|
requestOptions.headers['Content-Type'] = 'application/x-www-form-urlencoded';
|
|
}
|
|
requestOptions.body = payload;
|
|
}
|
|
|
|
// 添加超时控制
|
|
const controller = new AbortController();
|
|
const timeoutId = setTimeout(() => controller.abort(), 20000);
|
|
requestOptions.signal = controller.signal;
|
|
|
|
let retries = 3;
|
|
const makeRequest = async () => {
|
|
try {
|
|
const response = await fetch(url, requestOptions);
|
|
|
|
clearTimeout(timeoutId);
|
|
|
|
// 存储响应信息
|
|
this.info = {
|
|
statusCode: response.status,
|
|
headers: Object.fromEntries(response.headers.entries())
|
|
};
|
|
|
|
// 获取响应数据
|
|
const data = await response.text();
|
|
this.raw = data;
|
|
this.error = null;
|
|
this.status = '';
|
|
|
|
return this;
|
|
} catch (err) {
|
|
clearTimeout(timeoutId);
|
|
|
|
// 处理错误
|
|
if (err.name === 'AbortError') {
|
|
this.error = 'TIMEOUT';
|
|
this.status = 'Request timeout';
|
|
} else {
|
|
this.error = err.code || err.name;
|
|
this.status = err.message;
|
|
}
|
|
|
|
// 重试机制
|
|
if (retries > 0) {
|
|
retries--;
|
|
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
return makeRequest();
|
|
} else {
|
|
return this;
|
|
}
|
|
}
|
|
};
|
|
|
|
return await makeRequest();
|
|
}
|
|
|
|
|
|
// ========== 公共 API 方法 ==========
|
|
|
|
// 搜索功能
|
|
async search(keyword, option = {}) {
|
|
const api = this.provider.search(keyword, option);
|
|
return await this._exec(api);
|
|
}
|
|
|
|
// 获取歌曲详情
|
|
async song(id) {
|
|
const api = this.provider.song(id);
|
|
return await this._exec(api);
|
|
}
|
|
|
|
// 获取专辑信息
|
|
async album(id) {
|
|
const api = this.provider.album(id);
|
|
return await this._exec(api);
|
|
}
|
|
|
|
// 获取艺术家作品
|
|
async artist(id, limit = 50) {
|
|
const api = this.provider.artist(id, limit);
|
|
return await this._exec(api);
|
|
}
|
|
|
|
// 获取播放列表
|
|
async playlist(id) {
|
|
const api = this.provider.playlist(id);
|
|
return await this._exec(api);
|
|
}
|
|
|
|
// 获取音频播放链接
|
|
async url(id, br = 320) {
|
|
this.temp.br = br;
|
|
const api = this.provider.url(id, br);
|
|
return await this._exec(api);
|
|
}
|
|
|
|
// 获取歌词
|
|
async lyric(id) {
|
|
const api = this.provider.lyric(id);
|
|
return await this._exec(api);
|
|
}
|
|
|
|
// 获取封面图片
|
|
async pic(id, size = 300) {
|
|
return await this.provider.pic(id, size);
|
|
}
|
|
|
|
// ========== 静态方法 ==========
|
|
|
|
// 获取支持的平台列表
|
|
static getSupportedPlatforms() {
|
|
return ProviderFactory.getSupportedPlatforms();
|
|
}
|
|
|
|
// 检查平台是否支持
|
|
static isSupported(platform) {
|
|
return ProviderFactory.isSupported(platform);
|
|
}
|
|
}
|
|
|
|
export default Meting;
|