From a2453784c18e63826d00f3f060150b1b4b2f76f8 Mon Sep 17 00:00:00 2001 From: saopig1 <4x7sw862st@gmail.com> Date: Thu, 7 May 2026 20:17:46 +0800 Subject: [PATCH] feat(netease): include albums in search results Add mapNeteaseAlbums pure helper and wire cloudsearch?type=10 as the third Promise.all arm in search(), replacing the hardcoded albums:[]. Co-Authored-By: Claude Sonnet 4.6 --- src/music/netease.test.ts | 30 +++++++++++++++++++++++++++++- src/music/netease.ts | 22 ++++++++++++++++++++-- 2 files changed, 49 insertions(+), 3 deletions(-) diff --git a/src/music/netease.test.ts b/src/music/netease.test.ts index b2adb3d..ecc2202 100644 --- a/src/music/netease.test.ts +++ b/src/music/netease.test.ts @@ -1,5 +1,5 @@ import { describe, it, expect } from "vitest"; -import { parseLyrics } from "./netease.js"; +import { parseLyrics, mapNeteaseAlbums } from "./netease.js"; describe("NetEase adapter", () => { it("parses LRC format lyrics", () => { @@ -28,4 +28,32 @@ describe("NetEase adapter", () => { expect(lines[0].text).toBe("Hello world"); expect(lines[0].translation).toBe("你好世界"); }); + + it("mapNeteaseAlbums maps raw cloudsearch albums to Album shape", () => { + const raw = [ + { + id: 42, + name: "Album A", + picUrl: "https://x/p.jpg", + artists: [{ name: "Artist X" }, { name: "Featured Y" }], + size: 12, + }, + { + id: 99, + name: "Album B", + picUrl: "", + artists: [], + }, + ]; + expect(mapNeteaseAlbums(raw)).toEqual([ + { id: "42", name: "Album A", artist: "Artist X / Featured Y", coverUrl: "https://x/p.jpg", songCount: 12, platform: "netease" }, + { id: "99", name: "Album B", artist: "", coverUrl: "", songCount: 0, platform: "netease" }, + ]); + }); + + it("mapNeteaseAlbums returns [] for empty/null input", () => { + expect(mapNeteaseAlbums([])).toEqual([]); + expect(mapNeteaseAlbums(null as any)).toEqual([]); + expect(mapNeteaseAlbums(undefined as any)).toEqual([]); + }); }); diff --git a/src/music/netease.ts b/src/music/netease.ts index 8aaeab6..4a863db 100644 --- a/src/music/netease.ts +++ b/src/music/netease.ts @@ -8,6 +8,7 @@ import type { SearchResult, QrCodeResult, AuthStatus, + Album, } from "./provider.js"; export function parseLyrics(lrc: string, tlyric?: string): LyricLine[] { @@ -55,6 +56,18 @@ export function parseLyrics(lrc: string, tlyric?: string): LyricLine[] { return lines.sort((a, b) => a.time - b.time); } +export function mapNeteaseAlbums(raw: any[] | null | undefined): Album[] { + if (!Array.isArray(raw)) return []; + return raw.map((a) => ({ + id: String(a.id), + name: a.name ?? "", + artist: (a.artists ?? []).map((x: any) => x.name).join(" / "), + coverUrl: a.picUrl ?? "", + songCount: a.size ?? 0, + platform: "netease", + })); +} + // NetEase quality levels: standard(128k) higher(192k) exhigh(320k) lossless(flac) hires(hi-res) jyeffect jymaster export const NETEASE_QUALITY_LEVELS = [ { value: "standard", label: "标准 (128kbps)", bitrate: 128 }, @@ -91,7 +104,7 @@ export class NeteaseProvider implements MusicProvider { } async search(query: string, limit = 20): Promise { - const [songRes, playlistRes] = await Promise.all([ + const [songRes, playlistRes, albumRes] = await Promise.all([ this.api.get("/cloudsearch", { params: { keywords: query, type: 1, limit, ...this.cookieParams }, }), @@ -103,6 +116,9 @@ export class NeteaseProvider implements MusicProvider { ...this.cookieParams, }, }), + this.api.get("/cloudsearch", { + params: { keywords: query, type: 10, limit: 5, ...this.cookieParams }, + }), ]); const songs: Song[] = (songRes.data?.result?.songs ?? []).map( @@ -127,7 +143,9 @@ export class NeteaseProvider implements MusicProvider { platform: "netease", })); - return { songs, playlists, albums: [] }; + const albums = mapNeteaseAlbums(albumRes.data?.result?.albums); + + return { songs, playlists, albums }; } async getSongUrl(songId: string, quality?: string): Promise {