mirror of
https://github.com/ZHANGTIANYAO1/teamspeak-music-bot.git
synced 2026-10-02 04:52:50 +08:00
fix(api): add getPlaylistDetail provider method for QQ playlist detail
The /playlist/:id/detail route was hardcoded to call the NetEase API path /playlist/detail on whatever provider was selected. For QQ this hit a non-existent path, so clicking into any QQ playlist showed "歌单不存在或加载失败". Replace the platform-specific hack in the route handler with a proper getPlaylistDetail method on MusicProvider. Implement it for NetEase (porting the existing /playlist/detail logic) and QQ (calling /getSongListDetail and reading from response.cdlist[0]). Pre-existing bug exposed by the QQ source tab. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
e8d2f1ad98
commit
900191eca5
4 files changed
+48
-23
No files matched your search
@@ -3,6 +3,7 @@ import type {
|
||||
MusicProvider,
|
||||
Song,
|
||||
Playlist,
|
||||
PlaylistDetail,
|
||||
LyricLine,
|
||||
SearchResult,
|
||||
QrCodeResult,
|
||||
@@ -319,6 +320,21 @@ export class NeteaseProvider implements MusicProvider {
|
||||
}));
|
||||
}
|
||||
|
||||
async getPlaylistDetail(playlistId: string): Promise<PlaylistDetail | null> {
|
||||
const res = await this.api.get("/playlist/detail", {
|
||||
params: { id: playlistId, ...this.cookieParams },
|
||||
});
|
||||
const p = res.data?.playlist;
|
||||
if (!p) return null;
|
||||
return {
|
||||
id: String(p.id),
|
||||
name: p.name ?? "",
|
||||
description: p.description ?? "",
|
||||
coverUrl: p.coverImgUrl ?? "",
|
||||
songCount: p.trackCount ?? 0,
|
||||
};
|
||||
}
|
||||
|
||||
async getUserPlaylists(): Promise<Playlist[]> {
|
||||
// First get user ID from login status
|
||||
const statusRes = await this.api.get("/login/status", {
|
||||
|
||||
@@ -20,6 +20,14 @@ export interface Playlist {
|
||||
platform: "netease" | "qq" | "bilibili" | "youtube";
|
||||
}
|
||||
|
||||
export interface PlaylistDetail {
|
||||
id: string;
|
||||
name: string;
|
||||
description: string;
|
||||
coverUrl: string;
|
||||
songCount: number;
|
||||
}
|
||||
|
||||
export interface Album {
|
||||
id: string;
|
||||
name: string;
|
||||
@@ -77,4 +85,5 @@ export interface MusicProvider {
|
||||
getPersonalFm?(): Promise<Song[]>;
|
||||
getDailyRecommendSongs?(): Promise<Song[]>;
|
||||
getUserPlaylists?(): Promise<Playlist[]>;
|
||||
getPlaylistDetail?(playlistId: string): Promise<PlaylistDetail | null>;
|
||||
}
|
||||
@@ -3,6 +3,7 @@ import type {
|
||||
MusicProvider,
|
||||
Song,
|
||||
Playlist,
|
||||
PlaylistDetail,
|
||||
LyricLine,
|
||||
SearchResult,
|
||||
QrCodeResult,
|
||||
@@ -178,6 +179,21 @@ export class QQMusicProvider implements MusicProvider {
|
||||
}));
|
||||
}
|
||||
|
||||
async getPlaylistDetail(playlistId: string): Promise<PlaylistDetail | null> {
|
||||
const res = await this.api.get("/getSongListDetail", {
|
||||
params: { disstid: playlistId, ...this.cookieParams },
|
||||
});
|
||||
const cd = res.data?.response?.cdlist?.[0];
|
||||
if (!cd) return null;
|
||||
return {
|
||||
id: String(cd.disstid ?? cd.dissid ?? ""),
|
||||
name: cd.dissname ?? "",
|
||||
description: cd.desc ?? "",
|
||||
coverUrl: cd.logo ?? "",
|
||||
songCount: cd.songnum ?? cd.total_song_num ?? 0,
|
||||
};
|
||||
}
|
||||
|
||||
async getRecommendPlaylists(): Promise<Playlist[]> {
|
||||
const res = await this.api.get("/getSongLists", {
|
||||
params: { categoryId: 10000000, pageSize: 10, ...this.cookieParams },
|
||||
|
||||
+7
-23
@@ -168,32 +168,16 @@ export function createMusicRouter(
|
||||
router.get("/playlist/:id/detail", async (req, res) => {
|
||||
try {
|
||||
const provider = getProvider(req.query.platform as string);
|
||||
// Use the playlist songs endpoint to get basic info,
|
||||
// but we also need detail info (name, cover, description).
|
||||
// For netease, we access the underlying API directly.
|
||||
const nProvider = provider as any;
|
||||
if (nProvider.api) {
|
||||
const cookieParams = nProvider.cookie
|
||||
? { cookie: nProvider.cookie }
|
||||
: {};
|
||||
const detailRes = await nProvider.api.get("/playlist/detail", {
|
||||
params: { id: req.params.id, ...cookieParams },
|
||||
});
|
||||
const p = detailRes.data?.playlist;
|
||||
if (p) {
|
||||
res.json({
|
||||
playlist: {
|
||||
id: String(p.id),
|
||||
name: p.name,
|
||||
description: p.description ?? "",
|
||||
coverUrl: p.coverImgUrl ?? "",
|
||||
songCount: p.trackCount ?? 0,
|
||||
},
|
||||
});
|
||||
if (!provider.getPlaylistDetail) {
|
||||
res.status(501).json({ error: "Not supported by this provider" });
|
||||
return;
|
||||
}
|
||||
}
|
||||
const detail = await provider.getPlaylistDetail(req.params.id);
|
||||
if (!detail) {
|
||||
res.status(404).json({ error: "Playlist not found" });
|
||||
return;
|
||||
}
|
||||
res.json({ playlist: detail });
|
||||
} catch (err) {
|
||||
logger.error({ err }, "Get playlist detail failed");
|
||||
res.status(500).json({ error: (err as Error).message });
|
||||
|
||||
Reference in new issue
Block a user