diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index ae3733c..84754d9 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -15,7 +15,6 @@ src/ ├── base.js # 基础 Provider 接口 ├── netease.js # 网易云音乐 Provider ├── tencent.js # 腾讯音乐 Provider - ├── xiami.js # 虾米音乐 Provider ├── kugou.js # 酷狗音乐 Provider ├── baidu.js # 百度音乐 Provider └── kuwo.js # 酷我音乐 Provider diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..f4b2a41 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,73 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [1.6.0] - 2025-10-25 + +### Removed +- **BREAKING CHANGE**: Removed Xiami Music platform support + - Deleted `src/providers/xiami.js` provider implementation + - Removed Xiami from all documentation and examples + - Updated supported platforms list: NetEase, Tencent, KuGou, Baidu, Kuwo (5 platforms) + - Reason: Xiami Music service has been discontinued + +### Changed +- Updated `package.json` description to reflect current supported platforms +- Updated `README.md` to remove Xiami references from features list, platform codes, and support table +- Updated `CLAUDE.md` project overview +- Updated `ARCHITECTURE.md` structure documentation +- Updated `test/example.js` comments +- Cleaned up keywords in `package.json` + +### Migration Guide +If you were using Xiami platform (`'xiami'`), please switch to one of the supported platforms: +- NetEase Cloud Music (`'netease'`) +- Tencent Music (`'tencent'`) +- KuGou Music (`'kugou'`) +- Baidu Music (`'baidu'`) +- Kuwo Music (`'kuwo'`) + +```javascript +// Before +const meting = new Meting('xiami'); + +// After - choose an alternative platform +const meting = new Meting('netease'); +``` + +Note: The library will automatically fallback to `'netease'` if an unsupported platform is specified. + +## [1.5.14] - 2025-01-XX + +### Fixed +- Added HTML entity decoding for Tencent Music lyrics + +### Changed +- Improved npm authentication configuration in GitHub Actions +- Simplified npm authentication using `~/.npmrc` + +## [1.5.13] - Earlier + +### Added +- Initial stable release with 6 platform support +- Provider pattern architecture +- Zero-dependency implementation +- Built-in encryption support +- Promise-based async/await APIs +- Unified data format across platforms +- Version number injection at build time + +--- + +## Version History + +- **v1.6.0** - Removed Xiami Music support (breaking change) +- **v1.5.14** - Bug fixes and CI improvements +- **v1.5.13** - Stable release with complete platform support + +[1.6.0]: https://github.com/metowolf/Meting/compare/v1.5.14...v1.6.0 +[1.5.14]: https://github.com/metowolf/Meting/compare/v1.5.13...v1.5.14 +[1.5.13]: https://github.com/metowolf/Meting/releases/tag/v1.5.13 diff --git a/CLAUDE.md b/CLAUDE.md index 1d785df..ba5cee0 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -4,7 +4,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co ## 项目概述 -Meting Node.js 版本是一个强大的音乐 API 框架,用于加速音乐相关应用的开发。这是原版 PHP Meting 项目的 Node.js 移植版本,支持多个主流音乐平台的 API 调用,包括网易云音乐(netease)、腾讯音乐(tencent)、虾米音乐(xiami)、酷狗音乐(kugou)、百度音乐(baidu)和酷我音乐(kuwo)。 +Meting Node.js 版本是一个强大的音乐 API 框架,用于加速音乐相关应用的开发。这是原版 PHP Meting 项目的 Node.js 移植版本,支持多个主流音乐平台的 API 调用,包括网易云音乐(netease)、腾讯音乐(tencent)、酷狗音乐(kugou)、百度音乐(baidu)和酷我音乐(kuwo)。 ## 开发相关命令 diff --git a/README.md b/README.md index e80be8b..b706f24 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ Meting is a powerful music API framework designed to accelerate music-related de ### Features -- **🎵 Multi-Platform Support** - Supports NetEase Cloud Music, Tencent Music, Xiami, KuGou, Baidu Music, and Kuwo +- **🎵 Multi-Platform Support** - Supports NetEase Cloud Music, Tencent Music, KuGou, Baidu Music, and Kuwo - **🚀 Lightweight & Fast** - Zero external dependencies, built with Node.js native modules only - **📱 Modern Async/Await** - Promise-based APIs with full async/await support - **🔄 Unified Interface** - Standardized data format across all music platforms @@ -44,7 +44,7 @@ yarn add @meting/core import Meting from '@meting/core'; // Initialize with a music platform -const meting = new Meting('netease'); // 'netease', 'tencent', 'xiami', 'kugou', 'baidu', 'kuwo' +const meting = new Meting('netease'); // 'netease', 'tencent', 'kugou', 'baidu', 'kuwo' // Enable data formatting for consistent output meting.format(true); @@ -115,7 +115,7 @@ musicExample(); const meting = new Meting(server); ``` -- `server` (string): Music platform ('netease', 'tencent', 'xiami', 'kugou', 'baidu', 'kuwo') +- `server` (string): Music platform ('netease', 'tencent', 'kugou', 'baidu', 'kuwo') ### Core Methods @@ -167,7 +167,6 @@ await meting.pic(id, size) // Get album artwork |----------|------|--------|------|-------|--------|----------|-----|-------|---------| | NetEase Cloud Music | `netease` | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | | Tencent Music | `tencent` | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | -| Xiami Music | `xiami` | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | | KuGou Music | `kugou` | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | | Baidu Music | `baidu` | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | | Kuwo Music | `kuwo` | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | diff --git a/package.json b/package.json index 039693e..3f0b422 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@meting/core", - "version": "1.5.14", - "description": "A powerful music API framework to accelerate development - Node.js port. Support Netease, Tencent, Xiami, Kugou, Baidu, Kuwo music platforms.", + "version": "1.6.0", + "description": "A powerful music API framework to accelerate development - Node.js port. Support Netease, Tencent, Kugou, Baidu, Kuwo music platforms.", "main": "./lib/meting.js", "module": "./lib/meting.esm.js", "type": "module", @@ -26,7 +26,6 @@ "api", "netease", "tencent", - "xiami", "kugou", "baidu", "kuwo", diff --git a/src/providers/index.js b/src/providers/index.js index 6f432bb..7045331 100644 --- a/src/providers/index.js +++ b/src/providers/index.js @@ -1,6 +1,5 @@ import NeteaseProvider from './netease.js'; import TencentProvider from './tencent.js'; -import XiamiProvider from './xiami.js'; import KugouProvider from './kugou.js'; import BaiduProvider from './baidu.js'; import KuwoProvider from './kuwo.js'; @@ -12,7 +11,6 @@ export default class ProviderFactory { static providers = { netease: NeteaseProvider, tencent: TencentProvider, - xiami: XiamiProvider, kugou: KugouProvider, baidu: BaiduProvider, kuwo: KuwoProvider diff --git a/src/providers/xiami.js b/src/providers/xiami.js deleted file mode 100644 index af93d9d..0000000 --- a/src/providers/xiami.js +++ /dev/null @@ -1,347 +0,0 @@ -import crypto from 'crypto'; -import BaseProvider from './base.js'; - -/** - * 虾米音乐平台提供者 - */ -export default class XiamiProvider extends BaseProvider { - constructor(meting) { - super(meting); - this.name = 'xiami'; - } - - /** - * 获取虾米音乐的请求头配置 - */ - getHeaders() { - return { - 'Cookie': '_m_h5_tk=15d3402511a022796d88b249f83fb968_1511163656929; _m_h5_tk_enc=b6b3e64d81dae577fc314b5c5692df3c', - 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_5) AppleWebKit/537.36 (KHTML, like Gecko) XIAMI-MUSIC/3.1.1 Chrome/56.0.2924.87 Electron/1.6.11 Safari/537.36', - 'Accept': 'application/json', - 'Content-Type': 'application/x-www-form-urlencoded', - 'Accept-Language': 'zh-CN' - }; - } - - /** - * 搜索歌曲 - */ - search(keyword, option = {}) { - return { - method: 'GET', - url: 'https://acs.m.xiami.com/h5/mtop.alimusic.search.searchservice.searchsongs/1.0/', - body: { - data: { - key: keyword, - pagingVO: { - page: option.page || 1, - pageSize: option.limit || 30 - } - }, - r: 'mtop.alimusic.search.searchservice.searchsongs' - }, - encode: 'xiami_sign', - format: 'data.data.songs' - }; - } - - /** - * 获取歌曲详情 - */ - song(id) { - return { - method: 'GET', - url: 'https://acs.m.xiami.com/h5/mtop.alimusic.music.songservice.getsongdetail/1.0/', - body: { - data: { - songId: id - }, - r: 'mtop.alimusic.music.songservice.getsongdetail' - }, - encode: 'xiami_sign', - format: 'data.data.songDetail' - }; - } - - /** - * 获取专辑信息 - */ - album(id) { - return { - method: 'GET', - url: 'https://acs.m.xiami.com/h5/mtop.alimusic.music.albumservice.getalbumdetail/1.0/', - body: { - data: { - albumId: id - }, - r: 'mtop.alimusic.music.albumservice.getalbumdetail' - }, - encode: 'xiami_sign', - format: 'data.data.albumDetail.songs' - }; - } - - /** - * 获取艺术家作品 - */ - artist(id, limit = 50) { - return { - method: 'GET', - url: 'https://acs.m.xiami.com/h5/mtop.alimusic.music.songservice.getartistsongs/1.0/', - body: { - data: { - artistId: id, - pagingVO: { - page: 1, - pageSize: limit - } - }, - r: 'mtop.alimusic.music.songservice.getartistsongs' - }, - encode: 'xiami_sign', - format: 'data.data.songs' - }; - } - - /** - * 获取播放列表 - */ - playlist(id) { - return { - method: 'GET', - url: 'https://acs.m.xiami.com/h5/mtop.alimusic.music.list.collectservice.getcollectdetail/1.0/', - body: { - data: { - listId: id, - isFullTags: false, - pagingVO: { - page: 1, - pageSize: 1000 - } - }, - r: 'mtop.alimusic.music.list.collectservice.getcollectdetail' - }, - encode: 'xiami_sign', - format: 'data.data.collectDetail.songs' - }; - } - - /** - * 获取音频播放链接 - */ - url(id, br = 320) { - return { - method: 'GET', - url: 'https://acs.m.xiami.com/h5/mtop.alimusic.music.songservice.getsongs/1.0/', - body: { - data: { - songIds: [id] - }, - r: 'mtop.alimusic.music.songservice.getsongs' - }, - encode: 'xiami_sign', - decode: 'xiami_url' - }; - } - - /** - * 获取歌词 - */ - lyric(id) { - return { - method: 'GET', - url: 'https://acs.m.xiami.com/h5/mtop.alimusic.music.lyricservice.getsonglyrics/1.0/', - body: { - data: { - songId: id - }, - r: 'mtop.alimusic.music.lyricservice.getsonglyrics' - }, - encode: 'xiami_sign', - decode: 'xiami_lyric' - }; - } - - /** - * 获取封面图片 - */ - async pic(id, size = 300) { - const format = this.meting.isFormat; - const data = await this.meting.format(false).song(id); - this.meting.isFormat = format; - const songData = JSON.parse(data); - let url = songData.data.data.songDetail.albumLogo; - url = url.replace('http:', 'https:') + `@1e_1c_100Q_${size}h_${size}w`; - return JSON.stringify({ url: url }); - } - - /** - * 格式化虾米音乐数据 - */ - format(data) { - const result = { - id: data.songId, - name: data.songName, - artist: [], - album: data.albumName, - pic_id: data.songId, - url_id: data.songId, - lyric_id: data.songId, - source: 'xiami' - }; - - data.singerVOs.forEach(singer => { - result.artist.push(singer.artistName); - }); - - return result; - } - - /** - * 处理虾米音乐的编码/解码逻辑 - */ - async handleEncode(api) { - if (api.encode === 'xiami_sign') { - return this.signEncrypt(api); - } - return api; - } - - async handleDecode(decodeType, data) { - if (decodeType === 'xiami_url') { - return this.urlDecode(data); - } else if (decodeType === 'xiami_lyric') { - return this.lyricDecode(data); - } - return data; - } - - /** - * 虾米音乐签名加密 - */ - async signEncrypt(api) { - // 获取 token - const tokenUrl = 'https://acs.m.xiami.com/h5/mtop.alimusic.recommend.songservice.getdailysongs/1.0/?appKey=12574478&t=1560663823000&dataType=json&data=%7B%22requestStr%22%3A%22%7B%5C%22header%5C%22%3A%7B%5C%22platformId%5C%22%3A%5C%22mac%5C%22%7D%2C%5C%22model%5C%22%3A%5B%5D%7D%22%7D&api=mtop.alimusic.recommend.songservice.getdailysongs&v=1.0&type=originaljson&sign=22ad1377ee193f3e2772c17c6192b17c'; - - await this.meting._curl(tokenUrl, null, true); - const cookieMatch = this.meting.raw.match(/_m_h5[^;]+/g); - if (cookieMatch && cookieMatch.length >= 2) { - this.meting.header['Cookie'] = cookieMatch[0] + '; ' + cookieMatch[1]; - } - - const data = JSON.stringify({ - requestStr: JSON.stringify({ - header: { - platformId: 'mac' - }, - model: api.body.data - }) - }); - - const appkey = '12574478'; - const cookie = this.meting.header['Cookie']; - const tokenMatch = cookie.match(/_m_h5_tk=([^_]+)/); - const token = tokenMatch ? tokenMatch[1] : ''; - const t = Date.now(); - - const signStr = `${token}&${t}&${appkey}&${data}`; - const sign = crypto.createHash('md5').update(signStr).digest('hex'); - - api.body = { - appKey: appkey, - t: t, - dataType: 'json', - data: data, - api: api.body.r, - v: '1.0', - type: 'originaljson', - sign: sign - }; - - return api; - } - - /** - * 虾米音乐 URL 解码 - */ - urlDecode(result) { - const data = JSON.parse(result); - - const qualityMap = { - 's': 740, - 'h': 320, - 'l': 128, - 'f': 64, - 'e': 32 - }; - - let maxBr = 0; - let url; - - data.data.data.songs[0].listenFiles.forEach(file => { - const br = qualityMap[file.quality]; - if (br <= this.meting.temp.br && br > maxBr) { - maxBr = br; - url = { - url: file.listenFile, - size: file.fileSize, - br: br - }; - } - }); - - if (!url) { - url = { - url: '', - size: 0, - br: -1 - }; - } - - return JSON.stringify(url); - } - - /** - * 虾米音乐歌词解码 - */ - lyricDecode(result) { - const data = JSON.parse(result); - - let lyricData; - if (data.data.data.lyrics.length > 0) { - let content = data.data.data.lyrics[0].content; - content = content.replace(/<[^>]+>/g, ''); - - const matches = content.match(/\[([\d:\.]+)\](.*)\s\[x-trans\](.*)/gi); - if (matches) { - const lyricLines = []; - const tlyricLines = []; - - matches.forEach(match => { - const parts = match.match(/\[([\d:\.]+)\](.*)\s\[x-trans\](.*)/i); - if (parts) { - lyricLines.push(`[${parts[1]}]${parts[2]}`); - tlyricLines.push(`[${parts[1]}]${parts[3]}`); - } - }); - - lyricData = { - lyric: content.replace(/\[([\d:\.]+)\](.*)\s\[x-trans\](.*)/gi, (match, time, lyric) => `[${time}]${lyric}`), - tlyric: content.replace(/\[([\d:\.]+)\](.*)\s\[x-trans\](.*)/gi, (match, time, lyric, trans) => `[${time}]${trans}`) - }; - } else { - lyricData = { - lyric: content, - tlyric: '' - }; - } - } else { - lyricData = { - lyric: '', - tlyric: '' - }; - } - - return JSON.stringify(lyricData); - } -} \ No newline at end of file diff --git a/test/example.js b/test/example.js index 8b0d5b1..1f0123d 100644 --- a/test/example.js +++ b/test/example.js @@ -6,7 +6,7 @@ import Meting from '../lib/meting.esm.js'; async function main() { // 创建 Meting 实例 - const meting = new Meting('netease'); // 可选: 'netease', 'tencent', 'xiami', 'kugou', 'baidu', 'kuwo' + const meting = new Meting('netease'); // 可选: 'netease', 'tencent', 'kugou', 'baidu', 'kuwo' // 开启数据格式化 meting.format(true);