diff --git a/src/music/netease.ts b/src/music/netease.ts index e01772e..8aaeab6 100644 --- a/src/music/netease.ts +++ b/src/music/netease.ts @@ -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 { + 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 { // First get user ID from login status const statusRes = await this.api.get("/login/status", { diff --git a/src/music/provider.ts b/src/music/provider.ts index 55a2c76..566ffba 100644 --- a/src/music/provider.ts +++ b/src/music/provider.ts @@ -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; getDailyRecommendSongs?(): Promise; getUserPlaylists?(): Promise; + getPlaylistDetail?(playlistId: string): Promise; } diff --git a/src/music/qq.ts b/src/music/qq.ts index 73ef2d0..1a96790 100644 --- a/src/music/qq.ts +++ b/src/music/qq.ts @@ -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 { + 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 { const res = await this.api.get("/getSongLists", { params: { categoryId: 10000000, pageSize: 10, ...this.cookieParams }, diff --git a/src/web/api/music.ts b/src/web/api/music.ts index 3690674..b08f9a2 100644 --- a/src/web/api/music.ts +++ b/src/web/api/music.ts @@ -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, - }, - }); - return; - } + if (!provider.getPlaylistDetail) { + res.status(501).json({ error: "Not supported by this provider" }); + return; } - res.status(404).json({ error: "Playlist not found" }); + 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 });