From 15190413b2bf680c9fe0421090c944e14fc6117a Mon Sep 17 00:00:00 2001 From: Claude Code Date: Tue, 7 Jul 2026 10:35:54 +0800 Subject: [PATCH] =?UTF-8?q?fix(qq):=20=E6=8C=89=20ID=20=E6=92=AD=E6=94=BE?= =?UTF-8?q?=E6=97=B6=E5=9B=9E=E5=A1=AB=E6=AD=8C=E6=9B=B2=E5=85=83=E6=95=B0?= =?UTF-8?q?=E6=8D=AE=EF=BC=8C=E4=BF=AE=E5=A4=8D=20TS=20=E6=98=BE=E7=A4=BA?= =?UTF-8?q?=E7=A9=BA=E6=AD=8C=E5=90=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 用 id: / play-by-id 播放 QQ 音乐时,getSongDetail 依赖的 /getSongInfo 端点已被上游废弃(返回 code 500001),会 fall through 到一个 name 为空 的兜底 stub。空歌名一路传到 currentSong,导致 TS 昵称 / 正在播放消息 显示成「♪ 正在播放: - []」。 修复:在返回空 stub 前新增 fetchSongDetailViaMusicu(),改用与搜索同源 且可用的 u.y.qq.com/cgi-bin/musicu.fcg(music.pf_song_detail_svr / get_song_detail_yqq)按 mid 拉取真实 track_info(歌名/歌手/专辑/时长/ 封面),无需登录;仅当它也失败时才退回空 stub。 Co-Authored-By: Claude Opus 4.8 --- src/music/qq.ts | 48 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) diff --git a/src/music/qq.ts b/src/music/qq.ts index 8c70382..ec77d45 100644 --- a/src/music/qq.ts +++ b/src/music/qq.ts @@ -364,8 +364,18 @@ export class QQMusicProvider implements MusicProvider { }; } } catch { - // fall through to stub + // fall through to musicu.fcg fallback } + + // Fallback: fetch metadata via u.y.qq.com/cgi-bin/musicu.fcg. The bundled + // library's /getSongInfo route is broken (upstream code 500001), so the + // primary try above almost always falls through here for QQ. Without this, + // play-by-id songs carry an empty name → the TS nickname / now-playing + // message renders as "♪ 正在播放: - []". This endpoint (same host/shape + // as search) reliably returns full track_info without requiring login. + const detail = await this.fetchSongDetailViaMusicu(songId); + if (detail) return detail; + // Minimal stub — resolveAndPlay only needs id + platform to fetch a // play URL. Name/artist/album will be empty in play history, but the // song will actually play, which is the important part. @@ -380,6 +390,42 @@ export class QQMusicProvider implements MusicProvider { }; } + /** Fetch a single song's metadata via u.y.qq.com/cgi-bin/musicu.fcg + * (module music.pf_song_detail_svr / get_song_detail_yqq). This mirrors the + * search path's host + request shape and, unlike the library's /getSongInfo, + * actually works against QQ's current API. Returns null on any failure so the + * caller can fall back to the minimal stub. */ + private async fetchSongDetailViaMusicu(songId: string): Promise { + try { + const reqData = JSON.stringify({ + req_0: { + module: "music.pf_song_detail_svr", + method: "get_song_detail_yqq", + param: { song_mid: songId, song_type: 0 }, + }, + }); + const res = await qqMusicuApi.get("/cgi-bin/musicu.fcg", { + params: { format: "json", data: reqData }, + }); + const t = res.data?.req_0?.data?.track_info; + if (!t) return null; + const albumMid = t.album?.mid ?? t.album?.pmid ?? ""; + return { + id: String(t.mid ?? t.id ?? songId), + name: t.name ?? t.title ?? "", + artist: (t.singer ?? []).map((a: any) => a.name ?? a.title ?? "").filter(Boolean).join(" / "), + album: t.album?.name ?? t.album?.title ?? "", + duration: t.interval ?? 0, + coverUrl: albumMid + ? `https://y.gtimg.cn/music/photo_new/T002R300x300M000${albumMid}.jpg` + : "", + platform: "qq", + }; + } catch { + return null; + } + } + async getPlaylistSongs(playlistId: string): Promise { const res = await this.api.get("/getSongListDetail", { params: { disstid: playlistId, ...this.cookieParams },