From 47514f57aa7301a078fe7420bed907a98c375a4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=98=BF=E6=A2=93=E5=96=B5=5F=E3=81=82=E3=81=9A=E3=81=AB?= =?UTF-8?q?=E3=82=83=E3=82=93?= Date: Mon, 20 Apr 2026 23:48:46 +0800 Subject: [PATCH 1/3] fix #37 --- src/music/qq.ts | 34 ++++++++++++++------ src/web/api/player.ts | 49 +++++++++++++++++++--------- web/package-lock.json | 57 --------------------------------- web/src/components/SongCard.vue | 3 +- web/src/stores/player.ts | 14 +++++++- web/src/views/History.vue | 8 +++-- web/src/views/Home.vue | 4 +-- web/src/views/Playlist.vue | 18 +++-------- web/src/views/Search.vue | 8 +++-- 9 files changed, 91 insertions(+), 104 deletions(-) diff --git a/src/music/qq.ts b/src/music/qq.ts index f91345d..0049ea2 100644 --- a/src/music/qq.ts +++ b/src/music/qq.ts @@ -42,7 +42,7 @@ export class QQMusicProvider implements MusicProvider { const songs: Song[] = (res.data?.response?.data?.song?.list ?? []).map( (s: any) => ({ - id: String(s.songmid ?? s.songid), + id: String(s.mid ?? s.songmid ?? s.songid), name: s.songname ?? "", artist: (s.singer ?? []).map((a: any) => a.name).join(" / "), album: s.albumname ?? "", @@ -57,12 +57,26 @@ export class QQMusicProvider implements MusicProvider { return { songs, playlists: [], albums: [] }; } - async getSongUrl(songId: string, _quality?: string): Promise { - const res = await this.api.get("/getMusicPlay", { - params: { songmid: songId, ...this.cookieParams }, - }); - const playUrl = res.data?.data?.playUrl?.[songId]; - return playUrl?.url || null; + async getSongUrl(songId: string, quality?: string): Promise { + try { + const res = await this.api.get("/getMusicPlay", { + params: { songmid: songId, quality: quality ?? this.quality, ...this.cookieParams }, + }); + const playUrl = res.data?.data?.playUrl?.[songId]; + if (playUrl?.url) return playUrl.url; + } catch { + // try with songid + try { + const res = await this.api.get("/getMusicPlay", { + params: { songid: songId, quality: quality ?? this.quality, ...this.cookieParams }, + }); + const playUrl = res.data?.data?.playUrl?.[songId]; + if (playUrl?.url) return playUrl.url; + } catch { + // ignore + } + } + return null; } async getSongDetail(songId: string): Promise { @@ -115,12 +129,14 @@ export class QQMusicProvider implements MusicProvider { const cdlist = res.data?.response?.cdlist ?? []; if (cdlist.length === 0) return []; return (cdlist[0].songlist ?? []).map((s: any) => ({ - id: String(s.songmid ?? s.songid), + id: String(s.mid ?? s.songmid ?? s.songid), name: s.songname ?? s.name ?? "", artist: (s.singer ?? []).map((a: any) => a.name).join(" / "), album: s.albumname ?? "", duration: s.interval ?? 0, - coverUrl: s.albummid + coverUrl: s.album?.mid + ? `https://y.gtimg.cn/music/photo_new/T002R300x300M000${s.album.mid}.jpg` + : s.albummid ? `https://y.gtimg.cn/music/photo_new/T002R300x300M000${s.albummid}.jpg` : "", platform: "qq", diff --git a/src/web/api/player.ts b/src/web/api/player.ts index 330ea49..312ee6a 100644 --- a/src/web/api/player.ts +++ b/src/web/api/player.ts @@ -277,35 +277,54 @@ export function createPlayerRouter( }); // Play a single song by ID — resolves URL on demand - router.post("/:botId/play-by-id", async (req, res) => { + router.post("/:botId/play-song", async (req, res) => { try { const bot = (req as any).bot; - const { songId, platform } = req.body; - const provider = bot.getProviderFor( - platform === "bilibili" || platform === "qq" || platform === "youtube" - ? platform - : "netease" - ); - - const song = await provider.getSongDetail(songId); - if (!song) { - res.json({ message: "Song not found" }); + const { song } = req.body; + if (!song || !song.id || !song.platform) { + res.status(400).json({ error: "song object with id and platform is required" }); return; } - const queue = bot.getQueueManager(); queue.clear(); - queue.add({ ...song, platform: provider.platform }); + queue.add(song); queue.play(); bot.getPlayer().resetFailures(); const ok = await bot.resolveAndPlay(queue.current()!); if (!ok) { - res.json({ message: `Cannot play: ${song.name}` }); + res.json({ message: `Cannot play: ${song.name || song.id}` }); return; } - res.json({ message: `Now playing: ${song.name} - ${song.artist}` }); + res.json({ message: `Now playing: ${song.name || 'Unknown'} - ${song.artist || 'Unknown'}` }); + } catch (err) { + res.status(500).json({ error: (err as Error).message }); + } + }); + + router.post("/:botId/add-song", async (req, res) => { + try { + const bot = (req as any).bot; + const { song } = req.body; + if (!song || !song.id || !song.platform) { + res.status(400).json({ error: "song object with id and platform is required" }); + return; + } + const queue = bot.getQueueManager(); + const wasIdle = bot.getPlayer().getState() === "idle"; + queue.add(song); + + // If nothing was playing, start this newly-added song immediately. + if (wasIdle) { + queue.playAt(queue.size() - 1); + bot.getPlayer().resetFailures(); + await bot.resolveAndPlay(queue.current()!); + res.json({ message: `Now playing: ${song.name || 'Unknown'} - ${song.artist || 'Unknown'}` }); + return; + } + + res.json({ message: `Added to queue: ${song.name || 'Unknown'} - ${song.artist || 'Unknown'} (position ${queue.size()})` }); } catch (err) { res.status(500).json({ error: (err as Error).message }); } diff --git a/web/package-lock.json b/web/package-lock.json index 2d9e469..c3a65c8 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -797,9 +797,6 @@ "arm" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -821,9 +818,6 @@ "arm" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -845,9 +839,6 @@ "arm64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -869,9 +860,6 @@ "arm64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -893,9 +881,6 @@ "x64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -917,9 +902,6 @@ "x64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -1088,9 +1070,6 @@ "arm" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -1105,9 +1084,6 @@ "arm" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -1122,9 +1098,6 @@ "arm64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -1139,9 +1112,6 @@ "arm64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -1156,9 +1126,6 @@ "loong64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -1173,9 +1140,6 @@ "loong64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -1190,9 +1154,6 @@ "ppc64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -1207,9 +1168,6 @@ "ppc64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -1224,9 +1182,6 @@ "riscv64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -1241,9 +1196,6 @@ "riscv64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -1258,9 +1210,6 @@ "s390x" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -1275,9 +1224,6 @@ "x64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -1292,9 +1238,6 @@ "x64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ diff --git a/web/src/components/SongCard.vue b/web/src/components/SongCard.vue index 35e48fc..2ace6f2 100644 --- a/web/src/components/SongCard.vue +++ b/web/src/components/SongCard.vue @@ -28,9 +28,10 @@