Skip to content

Commit 3eda65d

Browse files
committed
✨ feat: 新增 API 及 Socket 获取播放信息
1 parent 7f40dfe commit 3eda65d

6 files changed

Lines changed: 187 additions & 1 deletion

File tree

docs/api.md

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,44 @@
169169

170170
---
171171

172+
### 获取当前播放信息
173+
174+
**接口**: `GET /api/control/song-info`
175+
176+
**描述**: 获取当前播放的歌曲信息
177+
178+
> [!WARNING]
179+
> 请勿频繁调用此接口(如每秒调用一次)来获取播放进度,这会导致软件性能异常。
180+
> 如需实时获取播放进度和状态,请使用 WebSocket 连接并监听相关事件。
181+
182+
**响应示例**:
183+
184+
```json
185+
{
186+
"code": 200,
187+
"message": "获取当前播放信息成功",
188+
"data": {
189+
"playStatus": "play",
190+
"playName": "歌曲名",
191+
"artistName": "歌手名",
192+
"albumName": "专辑名",
193+
"currentTime": 123.45,
194+
"volume": 1,
195+
"playRate": 1,
196+
"id": 123456,
197+
"name": "歌曲名",
198+
"artists": "歌手名",
199+
"album": "专辑名",
200+
"cover": "http://...",
201+
"duration": 300,
202+
"lrcData": [],
203+
"yrcData": []
204+
}
205+
}
206+
```
207+
208+
---
209+
172210
## 云音乐 API (Netease API)
173211

174212
**基础路径**: `/api/netease`

docs/socket.md

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,56 @@ ws.onmessage = (event) => {
108108
};
109109
```
110110

111+
## 获取信息
112+
113+
**消息类型**: `get-song-info`
114+
115+
**请求格式**:
116+
117+
```json
118+
{
119+
"type": "get-song-info"
120+
}
121+
```
122+
123+
**响应格式**:
124+
125+
成功响应:
126+
127+
```json
128+
{
129+
"type": "song-info",
130+
"data": {
131+
"playStatus": "play",
132+
"playName": "歌曲名",
133+
"artistName": "歌手名",
134+
"albumName": "专辑名",
135+
"currentTime": 123.45,
136+
"volume": 1,
137+
"playRate": 1,
138+
"id": 123456,
139+
"name": "歌曲名",
140+
"artists": "歌手名",
141+
"album": "专辑名",
142+
"cover": "http://...",
143+
"duration": 300,
144+
"lrcData": [],
145+
"yrcData": []
146+
}
147+
}
148+
```
149+
150+
错误响应:
151+
152+
```json
153+
{
154+
"type": "error",
155+
"data": {
156+
"message": "获取当前播放信息失败"
157+
}
158+
}
159+
```
160+
111161
## 事件广播
112162

113163
当播放器状态发生变化时,服务器会向所有连接的客户端广播消息。
@@ -167,7 +217,7 @@ ws.onmessage = (event) => {
167217
"type": "progress-change",
168218
"data": {
169219
"currentTime": 12000, // 当前播放时间(ms)
170-
"duration": 240000, // 总时长(ms)
220+
"duration": 240000, // 总时长(ms)
171221
"timestamp": 1234567890123
172222
}
173223
}

electron/main/services/SocketService.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,8 @@ export class SocketService {
290290
// 根据消息类型进行处理
291291
if (messageObj.type === "control") {
292292
this.handleControlCommand(socket, messageObj.data as { command?: string });
293+
} else if (messageObj.type === "get-song-info") {
294+
this.handleGetSongInfo(socket);
293295
} else {
294296
// 未知的消息类型
295297
socketLog.warn(`⚠️ Unknown message type: ${messageObj.type}`);
@@ -300,6 +302,27 @@ export class SocketService {
300302
}
301303
}
302304

305+
/**
306+
* 处理获取当前播放信息请求
307+
* @param socket WebSocket 客户端连接
308+
*/
309+
private async handleGetSongInfo(socket: WebSocket): Promise<void> {
310+
try {
311+
const { getTrackInfoFromRenderer } = await import("../utils/track-info");
312+
const trackInfo = await getTrackInfoFromRenderer();
313+
this.sendToClient(socket, {
314+
type: "song-info",
315+
data: trackInfo,
316+
});
317+
} catch (error) {
318+
socketLog.error("❌ Error getting current track info:", error);
319+
this.sendToClient(socket, {
320+
type: "error",
321+
data: { message: "获取当前播放信息失败" },
322+
});
323+
}
324+
}
325+
303326
/**
304327
* 向指定客户端发送消息
305328
* @param socket WebSocket 客户端连接

electron/main/utils/track-info.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { ipcMain } from "electron";
2+
import mainWindow from "../windows/main-window";
3+
4+
/**
5+
* 从渲染进程获取当前播放信息
6+
* @returns Promise<any>
7+
*/
8+
export const getTrackInfoFromRenderer = async (): Promise<any> => {
9+
return new Promise((resolve, reject) => {
10+
const mainWin = mainWindow.getWin();
11+
if (!mainWin || mainWin.isDestroyed() || mainWin.webContents.isDestroyed()) {
12+
return reject(new Error("主窗口未找到"));
13+
}
14+
15+
// 设置超时,防止无限等待
16+
const timeout = setTimeout(() => {
17+
ipcMain.removeListener("return-track-info", listener);
18+
reject(new Error("获取播放信息超时"));
19+
}, 2000);
20+
21+
const listener = (_event: any, data: any) => {
22+
clearTimeout(timeout);
23+
resolve(data);
24+
};
25+
26+
// 监听一次性回复
27+
ipcMain.once("return-track-info", listener);
28+
29+
// 发送请求
30+
mainWin.webContents.send("request-track-info");
31+
});
32+
};

electron/server/control/index.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,25 @@ export const initControlAPI = async (fastify: FastifyInstance) => {
197197
});
198198
}
199199
});
200+
201+
// 获取当前播放信息
202+
fastify.get("/song-info", async (_request, reply) => {
203+
try {
204+
const { getTrackInfoFromRenderer } = await import("../../main/utils/track-info");
205+
const trackInfo = await getTrackInfoFromRenderer();
206+
return reply.send({
207+
code: 200,
208+
message: "获取当前播放信息成功",
209+
data: trackInfo,
210+
});
211+
} catch (error) {
212+
return reply.code(500).send({
213+
code: 500,
214+
message: "获取当前播放信息失败",
215+
data: error instanceof Error ? error.message : error,
216+
});
217+
}
218+
});
200219
},
201220
{ prefix: "/control" },
202221
);

src/utils/initIpc.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,30 @@ const initIpc = () => {
9393
console.log("📡 Received protocol url:", url);
9494
handleProtocolUrl(url);
9595
});
96+
// 请求播放信息
97+
window.electron.ipcRenderer.on("request-track-info", () => {
98+
const musicStore = useMusicStore();
99+
const statusStore = useStatusStore();
100+
const { name, artist, album } = getPlayerInfoObj() || {};
101+
// 获取原始对象
102+
const playSong = toRaw(musicStore.playSong);
103+
const songLyric = toRaw(musicStore.songLyric);
104+
window.electron.ipcRenderer.send(
105+
"return-track-info",
106+
cloneDeep({
107+
playStatus: statusStore.playStatus,
108+
playName: name,
109+
artistName: artist,
110+
albumName: album,
111+
currentTime: statusStore.currentTime,
112+
// 音量及播放速率
113+
volume: statusStore.playVolume,
114+
playRate: statusStore.playRate,
115+
...playSong,
116+
...songLyric,
117+
}),
118+
);
119+
});
96120
} catch (error) {
97121
console.log(error);
98122
}

0 commit comments

Comments
 (0)