@zhin.js/plugin-music 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 凉菜
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,335 @@
1
+ # 音乐插件
2
+
3
+ 为 Zhin.js 提供音乐搜索和分享功能。支持多个音乐平台:QQ 音乐、网易云音乐。
4
+
5
+ ## ✨ 功能特性
6
+
7
+ - 🎵 **多平台搜索**:支持 QQ 音乐、网易云音乐
8
+ - 🎯 **智能选择**:交互式选择搜索结果
9
+ - 🔗 **音频直链**:获取音乐播放链接
10
+ - 📝 **歌词获取**:获取音乐歌词文本
11
+ - 📱 **多适配器支持**:
12
+ - ICQQ: 发送音乐分享卡片
13
+ - 其他平台: 发送格式化的文本消息
14
+ - 🎨 **美观展示**:格式化的音乐信息展示
15
+ - ⚡ **并行搜索**:同时搜索多个平台,提高效率
16
+
17
+ ## 📦 安装
18
+
19
+ ```bash
20
+ pnpm add @zhin.js/plugin-music
21
+ ```
22
+
23
+ ## 🚀 使用
24
+
25
+ ### 配置
26
+
27
+ 在 `zhin.config.ts` 中添加插件:
28
+
29
+ ```typescript
30
+ export default defineConfig({
31
+ plugins: [
32
+ 'music' // 添加音乐插件
33
+ ]
34
+ })
35
+ ```
36
+
37
+ ### 命令列表
38
+
39
+ #### 1. 点歌(搜索音乐)
40
+
41
+ **语法**:
42
+ ```
43
+ 点歌 <关键词> [音乐源...]
44
+ ```
45
+
46
+ **示例**:
47
+ ```
48
+ 点歌 告白气球
49
+ 点歌 七里香 qq
50
+ 点歌 晴天 qq netease
51
+ ```
52
+
53
+ **支持的音乐源**:
54
+ - `qq` - QQ 音乐
55
+ - `netease` - 网易云音乐
56
+
57
+ 如果不指定音乐源,默认搜索 QQ 音乐和网易云音乐。
58
+
59
+ #### 2. 获取音频直链
60
+
61
+ **语法**:
62
+ ```
63
+ 音乐链接 <关键词> [音乐源]
64
+ ```
65
+
66
+ **示例**:
67
+ ```
68
+ 音乐链接 告白气球
69
+ 音乐链接 七里香 qq
70
+ ```
71
+
72
+ 获取音乐的播放直链,可用于下载或直接播放。
73
+
74
+ **注意**:链接可能有时效性,请及时使用。
75
+
76
+ #### 3. 获取歌词
77
+
78
+ **语法**:
79
+ ```
80
+ 歌词 <关键词> [音乐源]
81
+ ```
82
+
83
+ **示例**:
84
+ ```
85
+ 歌词 告白气球
86
+ 歌词 七里香 qq
87
+ ```
88
+
89
+ 获取音乐的歌词文本。
90
+
91
+ ## 📱 平台适配
92
+
93
+ ### ICQQ 适配器
94
+
95
+ 使用 ICQQ 适配器时,插件会发送音乐分享卡片:
96
+
97
+ ```typescript
98
+ // 自动识别平台并发送分享卡片
99
+ // 支持 QQ 音乐和网易云音乐的原生分享
100
+ ```
101
+
102
+ ### 其他适配器
103
+
104
+ 其他平台会发送格式化的文本消息:
105
+
106
+ ```
107
+ 🎵 音乐分享
108
+
109
+ 🎼 告白气球
110
+ 🎤 周杰伦
111
+ 💿 周杰伦的床边故事
112
+ ⏱️ 3:34
113
+ 📱 QQ
114
+
115
+ 🔗 https://y.qq.com/n/yqq/song/xxxxx.html
116
+ ```
117
+
118
+ ## 🎨 使用示例
119
+
120
+ ### 基础搜索
121
+
122
+ ```
123
+ 用户: 点歌 晴天
124
+ 机器人: 🎵 请选择音乐
125
+ 1. 晴天 - 周杰伦 - 叶惠美 [4:29] [QQ]
126
+ 2. 晴天 - 周杰伦 - 叶惠美 [4:30] [NETEASE]
127
+ 3. 晴天娃娃 - 周杰伦 - 我很忙 [4:59] [QQ]
128
+
129
+ 用户: 1
130
+ 机器人: [发送音乐分享卡片]
131
+ ```
132
+
133
+ ### 指定音乐源
134
+
135
+ ```
136
+ 用户: 点歌 夜曲 netease
137
+ 机器人: [仅在网易云音乐搜索]
138
+ ```
139
+
140
+ ### 获取音频直链
141
+
142
+ ```
143
+ 用户: 音乐链接 告白气球
144
+ 机器人: 🎵 请选择音乐
145
+ 1. 告白气球 - 周杰伦 - 周杰伦的床边故事 [3:34] [NETEASE]
146
+ ...
147
+
148
+ 用户: 1
149
+ 机器人: 🎵 告白气球 - 周杰伦
150
+
151
+ 🔗 音频直链:
152
+ https://music.example.com/song.mp3
153
+
154
+ ⚠️ 链接可能有时效性,请及时使用
155
+ ```
156
+
157
+ ### 获取歌词
158
+
159
+ ```
160
+ 用户: 歌词 晴天
161
+ 机器人: 🎵 请选择音乐
162
+ ...
163
+
164
+ 用户: 1
165
+ 机器人: 🎵 晴天 - 周杰伦
166
+
167
+ 📝 歌词:
168
+
169
+ 故事的小黄花
170
+ 从出生那年就飘着
171
+ 童年的荡秋千
172
+ 随记忆一直晃到现在
173
+ ...
174
+ ```
175
+
176
+ ## 🗄️ 数据库结构
177
+
178
+ **注意**:当前版本已移除数据库依赖,所有功能都是无状态的。
179
+
180
+ ## 🔧 API 参考
181
+
182
+ ### 类型定义
183
+
184
+ ```typescript
185
+ import type { ShareContent, MusicSource, MusicSearchService } from '@zhin.js/plugin-music'
186
+
187
+ // 音乐源类型
188
+ type MusicSource = 'qq' | 'netease'
189
+
190
+ // 音乐分享内容
191
+ interface ShareContent {
192
+ id: string
193
+ source: MusicSource
194
+ url: string
195
+ title: string
196
+ artist?: string
197
+ album?: string
198
+ image?: string
199
+ duration?: number
200
+ }
201
+ ```
202
+
203
+ ### 导出的服务
204
+
205
+ ```typescript
206
+ import { musicServices } from '@zhin.js/plugin-music'
207
+
208
+ // 使用音乐搜索服务
209
+ const qqMusic = musicServices.qq
210
+ const results = await qqMusic.search('周杰伦', 10)
211
+ const cover = await qqMusic.getCover('音乐ID')
212
+ const detail = await qqMusic.getDetail('音乐ID')
213
+
214
+ // 获取音频直链(需要 Meting API)
215
+ const audioUrl = await qqMusic.getAudioUrl?.('音乐ID')
216
+
217
+ // 获取歌词
218
+ const lyric = await qqMusic.getLyric?.('音乐ID')
219
+ ```
220
+
221
+ ### 配置工具
222
+
223
+ ```typescript
224
+ import { formatDuration, formatMusicInfo } from '@zhin.js/plugin-music'
225
+
226
+ // 格式化时长
227
+ formatDuration(214) // "3:34"
228
+
229
+ // 格式化音乐信息
230
+ formatMusicInfo({
231
+ title: '晴天',
232
+ artist: '周杰伦',
233
+ album: '叶惠美',
234
+ duration: 269,
235
+ source: 'qq'
236
+ })
237
+ // "晴天 - 周杰伦 - 叶惠美 [4:29] [QQ]"
238
+ ```
239
+
240
+ ## 🎯 扩展开发
241
+
242
+ ### 添加新的音乐源
243
+
244
+ 1. 创建音乐源服务类:
245
+
246
+ ```typescript
247
+ // src/sources/my-music.ts
248
+ import type { MusicSearchService, ShareContent } from '../types.js'
249
+
250
+ export class MyMusicService implements MusicSearchService {
251
+ async search(keyword: string, limit = 10): Promise<ShareContent[]> {
252
+ // 实现搜索逻辑
253
+ return []
254
+ }
255
+
256
+ async getCover(id: string): Promise<string | null> {
257
+ // 实现获取封面逻辑
258
+ return null
259
+ }
260
+ }
261
+ ```
262
+
263
+ 2. 注册到服务映射:
264
+
265
+ ```typescript
266
+ // src/sources/index.ts
267
+ import { MyMusicService } from './my-music.js'
268
+
269
+ export const musicServices = {
270
+ qq: new QQMusicService(),
271
+ netease: new NeteaseMusicService(),
272
+ mymusic: new MyMusicService(), // 添加新服务
273
+ }
274
+ ```
275
+
276
+ 3. 更新类型定义:
277
+
278
+ ```typescript
279
+ // src/types.ts
280
+ export type MusicSource = 'qq' | 'netease' | 'mymusic'
281
+ ```
282
+
283
+ ## ⚠️ 注意事项
284
+
285
+ 1. **API 限制**:各音乐平台可能有请求频率限制
286
+ 2. **版权问题**:仅用于搜索和分享链接,不提供下载功能
287
+ 3. **网络问题**:部分 API 可能需要稳定的网络连接
288
+ 4. **平台兼容**:ICQQ 特定功能仅在 ICQQ 适配器下可用
289
+
290
+ ## 🐛 故障排除
291
+
292
+ ### 搜索无结果
293
+
294
+ - 检查网络连接
295
+ - 尝试更换关键词
296
+ - 尝试指定不同的音乐源
297
+
298
+ ### 分享卡片发送失败
299
+
300
+ - 确认使用的是 ICQQ 适配器
301
+ - 检查是否有封面图
302
+ - 降级使用文本消息
303
+
304
+ ### 收藏功能异常
305
+
306
+ - 确认数据库已正确配置
307
+ - 检查数据库文件权限
308
+ - 查看日志获取详细错误信息
309
+
310
+ ## 📝 开发
311
+
312
+ ### 构建
313
+
314
+ ```bash
315
+ pnpm build
316
+ ```
317
+
318
+ ### 测试
319
+
320
+ ```bash
321
+ # 在测试机器人中测试
322
+ pnpm dev
323
+ ```
324
+
325
+ ## 🤝 贡献
326
+
327
+ 欢迎提交 Issue 和 Pull Request!
328
+
329
+ ## 📄 许可证
330
+
331
+ MIT
332
+
333
+ ---
334
+
335
+ **享受音乐,享受编程!** 🎵✨
@@ -0,0 +1,19 @@
1
+ import { SendContent } from "zhin.js";
2
+ /**
3
+ * 异步组件包装器
4
+ * 类似 Next.js 的异步组件支持
5
+ */
6
+ export declare function AsyncComponent<P = any>(Component: (props: P) => Promise<SendContent>): (props: P) => SendContent;
7
+ /**
8
+ * 渲染异步内容
9
+ * 检查是否是异步标记,如果是则执行
10
+ */
11
+ export declare function renderAsync(content: any): Promise<SendContent>;
12
+ /**
13
+ * Suspense 组件工厂
14
+ * 用于包装异步组件并提供 fallback
15
+ */
16
+ export declare function createSuspense(fallback?: string): (props: {
17
+ children: any;
18
+ }) => Promise<SendContent>;
19
+ //# sourceMappingURL=async-jsx.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"async-jsx.d.ts","sourceRoot":"","sources":["../src/async-jsx.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAEtC;;;GAGG;AACH,wBAAgB,cAAc,CAAC,CAAC,GAAG,GAAG,EACpC,SAAS,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,OAAO,CAAC,WAAW,CAAC,GAC5C,CAAC,KAAK,EAAE,CAAC,KAAK,WAAW,CAc3B;AAED;;;GAGG;AACH,wBAAsB,WAAW,CAAC,OAAO,EAAE,GAAG,GAAG,OAAO,CAAC,WAAW,CAAC,CAQpE;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,QAAQ,GAAE,MAAiB,IACzB,OAAO;IAAE,QAAQ,EAAE,GAAG,CAAA;CAAE,KAAG,OAAO,CAAC,WAAW,CAAC,CAQ/E"}
@@ -0,0 +1,48 @@
1
+ // async-jsx.ts - 异步 JSX 增强工具
2
+ /**
3
+ * 异步组件包装器
4
+ * 类似 Next.js 的异步组件支持
5
+ */
6
+ export function AsyncComponent(Component) {
7
+ // 返回一个同步函数,但内部处理是异步的
8
+ return (props) => {
9
+ // 创建一个特殊的标记对象
10
+ const asyncMarker = {
11
+ __async: true,
12
+ component: Component,
13
+ props,
14
+ execute: () => Component(props)
15
+ };
16
+ // TypeScript 类型断言
17
+ return asyncMarker;
18
+ };
19
+ }
20
+ /**
21
+ * 渲染异步内容
22
+ * 检查是否是异步标记,如果是则执行
23
+ */
24
+ export async function renderAsync(content) {
25
+ if (content && typeof content === 'object' && content.__async) {
26
+ return await content.execute();
27
+ }
28
+ if (content && typeof content.then === 'function') {
29
+ return await content;
30
+ }
31
+ return content;
32
+ }
33
+ /**
34
+ * Suspense 组件工厂
35
+ * 用于包装异步组件并提供 fallback
36
+ */
37
+ export function createSuspense(fallback = '加载中...') {
38
+ return async function Suspense(props) {
39
+ try {
40
+ return await renderAsync(props.children);
41
+ }
42
+ catch (error) {
43
+ console.error('Suspense error:', error);
44
+ return fallback;
45
+ }
46
+ };
47
+ }
48
+ //# sourceMappingURL=async-jsx.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"async-jsx.js","sourceRoot":"","sources":["../src/async-jsx.ts"],"names":[],"mappings":"AAAA,6BAA6B;AAI7B;;;GAGG;AACH,MAAM,UAAU,cAAc,CAC5B,SAA6C;IAE7C,qBAAqB;IACrB,OAAO,CAAC,KAAQ,EAAE,EAAE;QAClB,cAAc;QACd,MAAM,WAAW,GAAG;YAClB,OAAO,EAAE,IAAI;YACb,SAAS,EAAE,SAAS;YACpB,KAAK;YACL,OAAO,EAAE,GAAG,EAAE,CAAC,SAAS,CAAC,KAAK,CAAC;SAChC,CAAC;QAEF,kBAAkB;QAClB,OAAO,WAAiC,CAAC;IAC3C,CAAC,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,OAAY;IAC5C,IAAI,OAAO,IAAI,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;QAC9D,OAAO,MAAM,OAAO,CAAC,OAAO,EAAE,CAAC;IACjC,CAAC;IACD,IAAI,OAAO,IAAI,OAAO,OAAO,CAAC,IAAI,KAAK,UAAU,EAAE,CAAC;QAClD,OAAO,MAAM,OAAO,CAAC;IACvB,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,cAAc,CAAC,WAAmB,QAAQ;IACxD,OAAO,KAAK,UAAU,QAAQ,CAAC,KAAwB;QACrD,IAAI,CAAC;YACH,OAAO,MAAM,WAAW,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;QAC3C,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,iBAAiB,EAAE,KAAK,CAAC,CAAC;YACxC,OAAO,QAAQ,CAAC;QAClB,CAAC;IACH,CAAC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,14 @@
1
+ import type { MusicSourceConfig, MusicSource } from './types.js';
2
+ /** 音乐源配置映射 */
3
+ export declare const sourceConfigMap: Record<MusicSource, MusicSourceConfig>;
4
+ /** 格式化时长 */
5
+ export declare function formatDuration(seconds?: number): string;
6
+ /** 格式化音乐信息 */
7
+ export declare function formatMusicInfo(music: {
8
+ title: string;
9
+ artist?: string;
10
+ album?: string;
11
+ duration?: number;
12
+ source: string;
13
+ }): string;
14
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAC,WAAW,EAAE,MAAM,YAAY,CAAA;AAE/D,cAAc;AACd,eAAO,MAAM,eAAe,EAAE,MAAM,CAAC,WAAW,EAAE,iBAAiB,CAelE,CAAA;AAED,YAAY;AACZ,wBAAgB,cAAc,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAMvD;AAED,cAAc;AACd,wBAAgB,eAAe,CAAC,KAAK,EAAE;IACrC,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,MAAM,EAAE,MAAM,CAAA;CACf,GAAG,MAAM,CAgBT"}
package/lib/config.js ADDED
@@ -0,0 +1,40 @@
1
+ /** 音乐源配置映射 */
2
+ export const sourceConfigMap = {
3
+ qq: {
4
+ appid: 100497308,
5
+ package: 'com.tencent.qqmusic',
6
+ icon: 'https://p.qpic.cn/qqconnect/0/app_100497308_1626060999/100?max-age=2592000&t=0',
7
+ sign: 'cbd27cd7c861227d013a25b2d10f0799',
8
+ version: '13.11.0.8',
9
+ },
10
+ netease: {
11
+ appid: 100495085,
12
+ package: 'com.netease.cloudmusic',
13
+ icon: 'https://i.gtimg.cn/open/app_icon/00/49/50/85/100495085_100_m.png',
14
+ sign: 'da6b069da1e2982db3e386233f68d76d',
15
+ version: '9.1.92',
16
+ },
17
+ };
18
+ /** 格式化时长 */
19
+ export function formatDuration(seconds) {
20
+ if (!seconds)
21
+ return '未知';
22
+ const minutes = Math.floor(seconds / 60);
23
+ const secs = seconds % 60;
24
+ return `${minutes}:${secs.toString().padStart(2, '0')}`;
25
+ }
26
+ /** 格式化音乐信息 */
27
+ export function formatMusicInfo(music) {
28
+ const parts = [music.title];
29
+ if (music.artist) {
30
+ parts.push(music.artist);
31
+ }
32
+ if (music.album) {
33
+ parts.push(music.album);
34
+ }
35
+ const info = parts.join(' - ');
36
+ const duration = formatDuration(music.duration);
37
+ const source = music.source.toUpperCase();
38
+ return `${info} [${duration}] [${source}]`;
39
+ }
40
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAGA,cAAc;AACd,MAAM,CAAC,MAAM,eAAe,GAA2C;IACrE,EAAE,EAAE;QACF,KAAK,EAAE,SAAS;QAChB,OAAO,EAAE,qBAAqB;QAC9B,IAAI,EAAE,gFAAgF;QACtF,IAAI,EAAE,kCAAkC;QACxC,OAAO,EAAE,WAAW;KACrB;IACD,OAAO,EAAE;QACP,KAAK,EAAE,SAAS;QAChB,OAAO,EAAE,wBAAwB;QACjC,IAAI,EAAE,kEAAkE;QACxE,IAAI,EAAE,kCAAkC;QACxC,OAAO,EAAE,QAAQ;KAClB;CACF,CAAA;AAED,YAAY;AACZ,MAAM,UAAU,cAAc,CAAC,OAAgB;IAC7C,IAAI,CAAC,OAAO;QAAE,OAAO,IAAI,CAAA;IAEzB,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,EAAE,CAAC,CAAA;IACxC,MAAM,IAAI,GAAG,OAAO,GAAG,EAAE,CAAA;IACzB,OAAO,GAAG,OAAO,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAA;AACzD,CAAC;AAED,cAAc;AACd,MAAM,UAAU,eAAe,CAAC,KAM/B;IACC,MAAM,KAAK,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,CAAA;IAE3B,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC;QACjB,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAA;IAC1B,CAAC;IAED,IAAI,KAAK,CAAC,KAAK,EAAE,CAAC;QAChB,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAA;IACzB,CAAC;IAED,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;IAC9B,MAAM,QAAQ,GAAG,cAAc,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAA;IAC/C,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC,WAAW,EAAE,CAAA;IAEzC,OAAO,GAAG,IAAI,KAAK,QAAQ,MAAM,MAAM,GAAG,CAAA;AAC5C,CAAC"}
package/lib/index.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.tsx"],"names":[],"mappings":""}
package/lib/index.js ADDED
@@ -0,0 +1,70 @@
1
+ import { jsx as _jsx } from "zhin.js/jsx-runtime";
2
+ import { addCommand, MessageCommand, usePrompt, useLogger, addComponent, defineComponent } from "zhin.js";
3
+ import { musicServices } from "./sources/index.js";
4
+ import { sourceConfigMap } from "./config.js";
5
+ const logger = useLogger();
6
+ // 异步组件:分享音乐
7
+ const ShareMusic = defineComponent(async function ShareMusic({ platform, musicId }) {
8
+ const service = musicServices[platform];
9
+ if (!service)
10
+ return 'unsupported music source';
11
+ const { id, source, ...detail } = await service.getDetail(musicId);
12
+ return _jsx("share", { ...detail, config: sourceConfigMap[platform] });
13
+ }, 'ShareMusic');
14
+ addComponent(ShareMusic);
15
+ // Suspense 组件 - 用于包装异步组件
16
+ const Suspense = defineComponent(async function Suspense(props, context) {
17
+ try {
18
+ // 如果 children 是一个 Promise(异步组件),等待它
19
+ if (props.children && typeof props.children === 'object' && 'then' in props.children) {
20
+ return await props.children;
21
+ }
22
+ // 否则直接返回
23
+ return props.children || '';
24
+ }
25
+ catch (error) {
26
+ logger.error('Suspense error:', error);
27
+ return props.fallback || '加载失败';
28
+ }
29
+ }, 'Suspense');
30
+ addComponent(Suspense);
31
+ addCommand(new MessageCommand("点歌 <keyword:text>")
32
+ .permit("adapter(icqq)")
33
+ .action(async (message, result) => {
34
+ const keyword = result.params.keyword;
35
+ const sourcesParam = result.params.sources || [];
36
+ // 解析音乐源
37
+ const sources = [];
38
+ for (const source of sourcesParam) {
39
+ const normalized = source.toLowerCase();
40
+ if (["qq", "netease"].includes(normalized)) {
41
+ sources.push(normalized);
42
+ }
43
+ }
44
+ // 如果没有指定音乐源,默认搜索 QQ 和网易云
45
+ const searchSources = sources.length > 0 ? sources : ["qq", "netease"];
46
+ logger.info(`搜索音乐: ${keyword}, 来源: ${searchSources.join(", ")}`);
47
+ // 并行搜索多个音乐源
48
+ const searchPromises = searchSources.map((source) => musicServices[source].search(keyword, 5));
49
+ const searchResults = await Promise.all(searchPromises);
50
+ const allMusic = searchResults.flat().filter(Boolean);
51
+ if (allMusic.length === 0) {
52
+ await message.$reply("没有找到结果");
53
+ return;
54
+ }
55
+ // 使用 prompt 让用户选择
56
+ const prompt = usePrompt(message);
57
+ const musicUrl = await prompt.pick("请选择搜索结果", {
58
+ type: "text",
59
+ options: allMusic.map((music) => ({
60
+ label: `${music.title} [${music.source.toUpperCase()}]`,
61
+ value: music.url,
62
+ })),
63
+ });
64
+ if (!musicUrl)
65
+ return;
66
+ const music = allMusic.find((m) => m.url === musicUrl);
67
+ // 现在支持直接使用 JSX 语法,异步组件会自动 await
68
+ return _jsx(ShareMusic, { platform: music.source, musicId: music.id });
69
+ }));
70
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ import type { MusicSource, MusicSearchService } from '../types.js';
2
+ /** 音乐源服务映射 */
3
+ export declare const musicServices: Record<MusicSource, MusicSearchService>;
4
+ export * from './qq.js';
5
+ export * from './netease.js';
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/sources/index.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAA;AAElE,cAAc;AACd,eAAO,MAAM,aAAa,EAAE,MAAM,CAAC,WAAW,EAAE,kBAAkB,CAGjE,CAAA;AAED,cAAc,SAAS,CAAA;AACvB,cAAc,cAAc,CAAA"}
@@ -0,0 +1,11 @@
1
+ // plugins/utils/music/src/sources/index.ts
2
+ import { QQMusicService } from './qq.js';
3
+ import { NeteaseMusicService } from './netease.js';
4
+ /** 音乐源服务映射 */
5
+ export const musicServices = {
6
+ qq: new QQMusicService(),
7
+ netease: new NeteaseMusicService(),
8
+ };
9
+ export * from './qq.js';
10
+ export * from './netease.js';
11
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/sources/index.ts"],"names":[],"mappings":"AAAA,2CAA2C;AAC3C,OAAO,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AACxC,OAAO,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAA;AAGlD,cAAc;AACd,MAAM,CAAC,MAAM,aAAa,GAA4C;IACpE,EAAE,EAAE,IAAI,cAAc,EAAE;IACxB,OAAO,EAAE,IAAI,mBAAmB,EAAE;CACnC,CAAA;AAED,cAAc,SAAS,CAAA;AACvB,cAAc,cAAc,CAAA"}
@@ -0,0 +1,15 @@
1
+ import type { MusicSearchService, MusicDetail, MusicInfo } from '../types.js';
2
+ /** 网易云音乐搜索服务 */
3
+ export declare class NeteaseMusicService implements MusicSearchService {
4
+ search(keyword: string, limit?: number): Promise<MusicInfo[]>;
5
+ getCover(id: string): Promise<string | null>;
6
+ getDetail(id: string): Promise<MusicDetail>;
7
+ /**
8
+ * 获取音频直链
9
+ * @param id 音乐 ID
10
+ * @param metingAPI Meting API 地址(可选)
11
+ * @returns 音频直链 URL
12
+ */
13
+ getAudioUrl(id: string, metingAPI?: string): Promise<string>;
14
+ }
15
+ //# sourceMappingURL=netease.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"netease.d.ts","sourceRoot":"","sources":["../../src/sources/netease.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,kBAAkB,EAAE,WAAW,EAAC,SAAS,EAAY,MAAM,aAAa,CAAA;AAEtF,gBAAgB;AAChB,qBAAa,mBAAoB,YAAW,kBAAkB;IACtD,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,SAAK,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC;IAwBzD,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAiB5C,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC;IAoBjD;;;;;OAKG;IACG,WAAW,CAAC,EAAE,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;CAWnE"}
@@ -0,0 +1,75 @@
1
+ /** 网易云音乐搜索服务 */
2
+ export class NeteaseMusicService {
3
+ async search(keyword, limit = 10) {
4
+ try {
5
+ const searchUrl = `http://music.163.com/api/search/get/web?csrf_token=hlpretag=&hlposttag=&s=${encodeURIComponent(keyword)}&type=1&offset=0&total=true&limit=${limit}`;
6
+ const response = await fetch(searchUrl, { method: 'GET' });
7
+ const data = await response.json();
8
+ const songs = data.result?.songs || [];
9
+ return songs.map(music => ({
10
+ id: music.id,
11
+ source: 'netease',
12
+ title: music.name,
13
+ artist: music.artists?.map(a => a.name).join('/'),
14
+ album: music.album.name,
15
+ url: `https://music.163.com/#/song?id=${music.id}`,
16
+ image: music.album.picUrl || music.album.img1v1Url,
17
+ duration: music.duration ? Math.floor(music.duration / 1000) : undefined,
18
+ }));
19
+ }
20
+ catch (error) {
21
+ console.error('Netease Music search failed:', error);
22
+ return [];
23
+ }
24
+ }
25
+ async getCover(id) {
26
+ try {
27
+ const url = new URL(`https://music.163.com/api/song/detail/?ids=[${id}]`);
28
+ const response = await fetch(url, { method: 'GET' });
29
+ const data = await response.json();
30
+ const song = data.songs?.[0];
31
+ if (!song)
32
+ return null;
33
+ return song.album.picUrl || song.album.img1v1Url || null;
34
+ }
35
+ catch (error) {
36
+ console.error('Netease Music get cover failed:', error);
37
+ return null;
38
+ }
39
+ }
40
+ async getDetail(id) {
41
+ const url = new URL(`https://music.163.com/api/song/detail/?ids=[${id}]`);
42
+ const response = await fetch(url, { method: 'GET' });
43
+ const data = await response.json();
44
+ const song = data.songs?.[0];
45
+ if (!song)
46
+ throw new Error('Music not found');
47
+ return {
48
+ id: song.id,
49
+ source: 'netease',
50
+ title: song.name,
51
+ url: `https://music.163.com/#/song?id=${song.id}`,
52
+ image: song.album.picUrl || song.album.img1v1Url,
53
+ duration: song.duration ? Math.floor(song.duration / 1000) : undefined,
54
+ audio: await this.getAudioUrl(id),
55
+ };
56
+ }
57
+ /**
58
+ * 获取音频直链
59
+ * @param id 音乐 ID
60
+ * @param metingAPI Meting API 地址(可选)
61
+ * @returns 音频直链 URL
62
+ */
63
+ async getAudioUrl(id, metingAPI) {
64
+ // 默认使用 Meting API
65
+ const apiUrl = metingAPI || 'https://api.injahow.cn/meting/';
66
+ const url = `${apiUrl}?type=url&id=${id}`;
67
+ const response = await fetch(url, { method: 'GET' });
68
+ const data = await response.json();
69
+ if (!data.url && !data.data?.url)
70
+ throw new Error('Audio URL not found');
71
+ // 不同的 Meting API 返回格式可能不同
72
+ return data.url || data.data?.url;
73
+ }
74
+ }
75
+ //# sourceMappingURL=netease.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"netease.js","sourceRoot":"","sources":["../../src/sources/netease.ts"],"names":[],"mappings":"AAGA,gBAAgB;AAChB,MAAM,OAAO,mBAAmB;IAC9B,KAAK,CAAC,MAAM,CAAC,OAAe,EAAE,KAAK,GAAG,EAAE;QACtC,IAAI,CAAC;YACH,MAAM,SAAS,GAAG,6EAA6E,kBAAkB,CAAC,OAAO,CAAC,qCAAqC,KAAK,EAAE,CAAA;YAEtK,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,SAAS,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAA;YAC1D,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAuC,CAAA;YACvE,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,EAAE,KAAK,IAAI,EAAE,CAAA;YAEtC,OAAO,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;gBACzB,EAAE,EAAE,KAAK,CAAC,EAAE;gBACZ,MAAM,EAAE,SAAkB;gBAC1B,KAAK,EAAE,KAAK,CAAC,IAAI;gBACjB,MAAM,EAAE,KAAK,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC;gBACjD,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,IAAI;gBACvB,GAAG,EAAE,mCAAmC,KAAK,CAAC,EAAE,EAAE;gBAClD,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,MAAM,IAAI,KAAK,CAAC,KAAK,CAAC,SAAS;gBAClD,QAAQ,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,QAAQ,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS;aACzE,CAAC,CAAC,CAAA;QACL,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,8BAA8B,EAAE,KAAK,CAAC,CAAA;YACpD,OAAO,EAAE,CAAA;QACX,CAAC;IACH,CAAC;IAED,KAAK,CAAC,QAAQ,CAAC,EAAU;QACvB,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,+CAA+C,EAAE,GAAG,CAAC,CAAA;YAEzE,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAA;YACpD,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAA2B,CAAA;YAE3D,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,CAAA;YAC5B,IAAI,CAAC,IAAI;gBAAE,OAAO,IAAI,CAAA;YAEtB,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,IAAI,IAAI,CAAC,KAAK,CAAC,SAAS,IAAI,IAAI,CAAA;QAC1D,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,iCAAiC,EAAE,KAAK,CAAC,CAAA;YACvD,OAAO,IAAI,CAAA;QACb,CAAC;IACH,CAAC;IAED,KAAK,CAAC,SAAS,CAAC,EAAU;QACxB,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,+CAA+C,EAAE,GAAG,CAAC,CAAA;QAEvE,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAA;QACpD,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAA2B,CAAA;QAE3D,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,CAAA;QAC5B,IAAI,CAAC,IAAI;YAAE,MAAM,IAAI,KAAK,CAAC,iBAAiB,CAAC,CAAA;QAE7C,OAAO;YACL,EAAE,EAAE,IAAI,CAAC,EAAE;YACX,MAAM,EAAE,SAAS;YACjB,KAAK,EAAE,IAAI,CAAC,IAAI;YAChB,GAAG,EAAE,mCAAmC,IAAI,CAAC,EAAE,EAAE;YACjD,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,MAAM,IAAI,IAAI,CAAC,KAAK,CAAC,SAAS;YAChD,QAAQ,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS;YACtE,KAAK,EAAE,MAAM,IAAI,CAAC,WAAW,CAAC,EAAE,CAAC;SAClC,CAAA;IACL,CAAC;IAED;;;;;OAKG;IACH,KAAK,CAAC,WAAW,CAAC,EAAU,EAAE,SAAkB;QAC9C,kBAAkB;QAChB,MAAM,MAAM,GAAG,SAAS,IAAI,gCAAgC,CAAA;QAC5D,MAAM,GAAG,GAAG,GAAG,MAAM,gBAAgB,EAAE,EAAE,CAAA;QAEzC,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAA;QACpD,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAA+C,CAAA;QAC/E,IAAG,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG;YAAE,MAAM,IAAI,KAAK,CAAC,qBAAqB,CAAC,CAAA;QACvE,0BAA0B;QAC1B,OAAO,IAAI,CAAC,GAAG,IAAI,IAAI,CAAC,IAAI,EAAE,GAAI,CAAA;IACtC,CAAC;CACF"}
@@ -0,0 +1,21 @@
1
+ import type { MusicSearchService, MusicDetail, MusicInfo } from '../types.js';
2
+ /** QQ 音乐搜索服务 */
3
+ export declare class QQMusicService implements MusicSearchService {
4
+ search(keyword: string, limit?: number): Promise<MusicInfo[]>;
5
+ getCover(id: string): Promise<string | null>;
6
+ getDetail(id: string): Promise<MusicDetail>;
7
+ /**
8
+ * 获取音频直链(需要第三方 API)
9
+ * @param id 音乐 ID
10
+ * @param metingAPI Meting API 地址(可选)
11
+ * @returns 音频直链 URL
12
+ */
13
+ getAudioUrl(id: string, metingAPI?: string): Promise<string>;
14
+ /**
15
+ * 获取歌词
16
+ * @param id 音乐 ID
17
+ * @returns 歌词文本
18
+ */
19
+ getLyric(id: string): Promise<string | null>;
20
+ }
21
+ //# sourceMappingURL=qq.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"qq.d.ts","sourceRoot":"","sources":["../../src/sources/qq.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,kBAAkB,EAAE,WAAW,EAAC,SAAS,EAAW,MAAM,aAAa,CAAA;AAErF,gBAAgB;AAChB,qBAAa,cAAe,YAAW,kBAAkB;IACjD,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,SAAK,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC;IAsBzD,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IA+B5C,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC;IAkCjD;;;;;OAKG;IACG,WAAW,CAAC,EAAE,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAalE;;;;OAIG;IACG,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;CA+BnD"}
@@ -0,0 +1,136 @@
1
+ /** QQ 音乐搜索服务 */
2
+ export class QQMusicService {
3
+ async search(keyword, limit = 10) {
4
+ try {
5
+ const url = new URL('https://c.y.qq.com/splcloud/fcgi-bin/smartbox_new.fcg');
6
+ url.searchParams.set('key', keyword);
7
+ url.searchParams.set('format', 'json');
8
+ const response = await fetch(url, { method: 'GET' });
9
+ const data = await response.json();
10
+ const items = data.data?.song?.itemlist || [];
11
+ return items.slice(0, limit).map(music => ({
12
+ id: music.id,
13
+ source: 'qq',
14
+ title: music.name,
15
+ url: `https://y.qq.com/n/yqq/song/${music.mid}.html`
16
+ }));
17
+ }
18
+ catch (error) {
19
+ console.error('QQ Music search failed:', error);
20
+ return [];
21
+ }
22
+ }
23
+ async getCover(id) {
24
+ try {
25
+ const url = new URL('https://u.y.qq.com/cgi-bin/musicu.fcg');
26
+ url.searchParams.set('format', 'json');
27
+ url.searchParams.set('inCharset', 'utf8');
28
+ url.searchParams.set('outCharset', 'utf-8');
29
+ url.searchParams.set('notice', '0');
30
+ url.searchParams.set('platform', 'yqq.json');
31
+ url.searchParams.set('needNewCode', '0');
32
+ url.searchParams.set('data', JSON.stringify({
33
+ comm: { ct: 24, cv: 0 },
34
+ songinfo: {
35
+ method: 'get_song_detail_yqq',
36
+ param: { song_type: 0, song_mid: '', song_id: parseInt(id) },
37
+ module: 'music.pf_song_detail_svr'
38
+ }
39
+ }));
40
+ const response = await fetch(url, { method: 'GET' });
41
+ const result = await response.json();
42
+ const albumMid = result?.songinfo?.data?.track_info?.album?.mid;
43
+ if (!albumMid)
44
+ return null;
45
+ return `https://y.gtimg.cn/music/photo_new/T002R300x300M000${albumMid}.jpg`;
46
+ }
47
+ catch (error) {
48
+ console.error('QQ Music get cover failed:', error);
49
+ return null;
50
+ }
51
+ }
52
+ async getDetail(id) {
53
+ const url = new URL('https://u.y.qq.com/cgi-bin/musicu.fcg');
54
+ url.searchParams.set('format', 'json');
55
+ url.searchParams.set('data', JSON.stringify({
56
+ comm: { ct: 24, cv: 0 },
57
+ songinfo: {
58
+ method: 'get_song_detail_yqq',
59
+ param: { song_type: 0, song_mid: '', song_id: parseInt(id) },
60
+ module: 'music.pf_song_detail_svr'
61
+ }
62
+ }));
63
+ const response = await fetch(url, { method: 'GET' });
64
+ const result = await response.json();
65
+ const trackInfo = result?.songinfo?.data?.track_info;
66
+ if (!trackInfo)
67
+ throw new Error('Music not found');
68
+ const albumMid = trackInfo.album?.mid;
69
+ const image = albumMid
70
+ ? `https://y.gtimg.cn/music/photo_new/T002R300x300M000${albumMid}.jpg`
71
+ : undefined;
72
+ return {
73
+ id,
74
+ source: 'qq',
75
+ title: trackInfo.name,
76
+ url: `https://y.qq.com/n/yqq/song/${trackInfo.mid}.html`,
77
+ audio: await this.getAudioUrl(id),
78
+ image: image || '',
79
+ duration: trackInfo.interval,
80
+ };
81
+ }
82
+ /**
83
+ * 获取音频直链(需要第三方 API)
84
+ * @param id 音乐 ID
85
+ * @param metingAPI Meting API 地址(可选)
86
+ * @returns 音频直链 URL
87
+ */
88
+ async getAudioUrl(id, metingAPI) {
89
+ // QQ 音乐需要使用第三方 API
90
+ const apiUrl = metingAPI || 'https://api.injahow.cn/meting/';
91
+ const url = `${apiUrl}?type=url&id=${id}&source=qq`;
92
+ const response = await fetch(url, { method: 'GET' });
93
+ const data = await response.json();
94
+ if (!data.url && !data.data?.url) {
95
+ throw new Error('Audio URL not found');
96
+ }
97
+ return data.url || data.data?.url;
98
+ }
99
+ /**
100
+ * 获取歌词
101
+ * @param id 音乐 ID
102
+ * @returns 歌词文本
103
+ */
104
+ async getLyric(id) {
105
+ try {
106
+ const url = new URL('https://c.y.qq.com/lyric/fcgi-bin/fcg_query_lyric_new.fcg');
107
+ url.searchParams.set('songmid', id);
108
+ url.searchParams.set('g_tk', '5381');
109
+ url.searchParams.set('format', 'json');
110
+ url.searchParams.set('inCharset', 'utf8');
111
+ url.searchParams.set('outCharset', 'utf-8');
112
+ url.searchParams.set('nobase64', '1');
113
+ const response = await fetch(url, {
114
+ method: 'GET',
115
+ headers: {
116
+ 'Referer': 'https://y.qq.com'
117
+ }
118
+ });
119
+ const data = await response.json();
120
+ if (!data.lyric)
121
+ return null;
122
+ // QQ 音乐返回的歌词需要 base64 解码
123
+ try {
124
+ return Buffer.from(data.lyric, 'base64').toString('utf-8');
125
+ }
126
+ catch {
127
+ return data.lyric;
128
+ }
129
+ }
130
+ catch (error) {
131
+ console.error('QQ Music get lyric failed:', error);
132
+ return null;
133
+ }
134
+ }
135
+ }
136
+ //# sourceMappingURL=qq.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"qq.js","sourceRoot":"","sources":["../../src/sources/qq.ts"],"names":[],"mappings":"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"}
package/lib/types.d.ts ADDED
@@ -0,0 +1,61 @@
1
+ /** 音乐源类型 */
2
+ export type MusicSource = "qq" | "netease";
3
+ /** 音乐分享内容 */
4
+ export interface MusicInfo {
5
+ id: string;
6
+ source: MusicSource;
7
+ /** 跳转链接 */
8
+ url: string;
9
+ /** 音乐标题 */
10
+ title: string;
11
+ }
12
+ export interface MusicDetail extends MusicInfo {
13
+ image: string;
14
+ audio: string;
15
+ duration?: number;
16
+ }
17
+ /** 音乐源配置 */
18
+ export interface MusicSourceConfig {
19
+ appid: number;
20
+ package: string;
21
+ icon: string;
22
+ sign: string;
23
+ version: string;
24
+ }
25
+ /** QQ 音乐 API 响应 */
26
+ export interface MusicQQ {
27
+ id: string;
28
+ mid: string;
29
+ name: string;
30
+ docid: string;
31
+ singer: string;
32
+ album?: {
33
+ mid: string;
34
+ name: string;
35
+ };
36
+ interval?: number;
37
+ }
38
+ /** 网易云音乐 API 响应 */
39
+ export interface Music163 {
40
+ id: string;
41
+ name: string;
42
+ duration?: number;
43
+ artists?: Array<{
44
+ id: string;
45
+ name: string;
46
+ }>;
47
+ album: {
48
+ id: string;
49
+ name: string;
50
+ picUrl: string | null;
51
+ img1v1Url: string;
52
+ };
53
+ }
54
+ /** 音乐搜索服务接口 */
55
+ export interface MusicSearchService {
56
+ /** 搜索音乐 */
57
+ search(keyword: string, limit?: number): Promise<MusicInfo[]>;
58
+ /** 获取音乐详情 */
59
+ getDetail(id: string): Promise<MusicDetail>;
60
+ }
61
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAEA,YAAY;AACZ,MAAM,MAAM,WAAW,GAAG,IAAI,GAAG,SAAS,CAAC;AAE3C,aAAa;AACb,MAAM,WAAW,SAAS;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,WAAW,CAAC;IACpB,WAAW;IACX,GAAG,EAAE,MAAM,CAAC;IACZ,WAAW;IACX,KAAK,EAAE,MAAM,CAAC;CACf;AACD,MAAM,WAAW,WAAY,SAAQ,SAAS;IAC5C,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAC,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,YAAY;AACZ,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,mBAAmB;AACnB,MAAM,WAAW,OAAO;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE;QACN,GAAG,EAAE,MAAM,CAAC;QACZ,IAAI,EAAE,MAAM,CAAC;KACd,CAAC;IACF,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,mBAAmB;AACnB,MAAM,WAAW,QAAQ;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,KAAK,CAAC;QACd,EAAE,EAAE,MAAM,CAAC;QACX,IAAI,EAAE,MAAM,CAAC;KACd,CAAC,CAAC;IACH,KAAK,EAAE;QACL,EAAE,EAAE,MAAM,CAAC;QACX,IAAI,EAAE,MAAM,CAAC;QACb,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;QACtB,SAAS,EAAE,MAAM,CAAC;KACnB,CAAC;CACH;AAED,eAAe;AACf,MAAM,WAAW,kBAAkB;IACjC,WAAW;IACX,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC,CAAC;IAC9D,aAAa;IACb,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;CAC7C"}
package/lib/types.js ADDED
@@ -0,0 +1,3 @@
1
+ // plugins/utils/music/src/types.ts
2
+ export {};
3
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,mCAAmC"}
package/package.json ADDED
@@ -0,0 +1,47 @@
1
+ {
2
+ "name": "@zhin.js/plugin-music",
3
+ "version": "0.0.2",
4
+ "description": "Music search and sharing plugin for Zhin.js",
5
+ "type": "module",
6
+ "main": "./lib/index.js",
7
+ "types": "./lib/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./lib/index.d.ts",
11
+ "import": "./lib/index.js"
12
+ },
13
+ "./package.json": "./package.json"
14
+ },
15
+ "files": [
16
+ "lib",
17
+ "README.md"
18
+ ],
19
+ "keywords": [
20
+ "zhin",
21
+ "plugin",
22
+ "music",
23
+ "qq-music",
24
+ "netease-music"
25
+ ],
26
+ "author": "",
27
+ "license": "MIT",
28
+ "dependencies": {},
29
+ "devDependencies": {
30
+ "typescript": "^5.3.3",
31
+ "zhin.js": "1.0.16",
32
+ "@zhin.js/types": "1.0.5",
33
+ "@zhin.js/adapter-icqq": "1.0.21"
34
+ },
35
+ "peerDependencies": {
36
+ "@zhin.js/adapter-icqq": "1.0.21"
37
+ },
38
+ "peerDependenciesMeta": {
39
+ "@zhin.js/adapter-icqq": {
40
+ "optional": true
41
+ }
42
+ },
43
+ "scripts": {
44
+ "build": "tsc --build",
45
+ "clean": "rm -rf lib"
46
+ }
47
+ }