mirror of
https://github.com/ZHANGTIANYAO1/teamspeak-music-bot.git
synced 2026-10-11 09:22:49 +08:00
feat(web): artist search, artist pages, and full-catalogue playback
Adds artist support for the NetEase and QQ providers plus the matching UI. Backend: - SearchResult gains `artists`; new optional MusicProvider methods getArtistDetail / getArtistSongs / getArtistAlbums / getArtistAllSongs. - NetEase: /cloudsearch type=100 for artist search, /artists, /artist/songs, /artist/album and /artist/desc for the artist page. - QQ: singer search rides along in the existing musicu.fcg batch (search_type=1); singer detail via music.web_singer_info_svr. QQ exposes no working singer-song paging endpoint, so the full catalogue is built from the hot 50 plus every album of the singer (album search filtered by singerMID, songs fetched per album, de-duplicated, cached for 10 minutes). A failed album sweep is never cached and degrades to the hot list. - API: GET /api/music/artist/:id and POST /api/player/:botId/play-artist (player.control capability, guest flag playCollection); /search/all now aggregates artists too. Frontend: - Search history in localStorage (max 10, per platform, never shared between users), shown as a dropdown under the search box and as 最近搜索 chips. - Artist row in the search results; new /artist/:id page (portrait, aliases, stats, description, top songs, album shelf) with 播放 / 随机播放, which queue the singer's whole catalogue. - playArtist store action. Tests cover the provider mappers, the new routes, the play-artist collector (paging, de-duplication, 500-track cap), permission gating and the new views.
This commit is contained in:
1 parent
87fca6d8b7
commit
b6ad536bb7
17 files changed
+2260
-14
No files matched your search
@@ -59,6 +59,34 @@ export interface Album {
|
||||
platform: Platform;
|
||||
}
|
||||
|
||||
/** An artist / singer entity. Only sources with a real artist concept expose
|
||||
* these (NetEase, QQ); the others simply never return `SearchResult.artists`
|
||||
* and leave the optional provider methods unimplemented. */
|
||||
export interface Artist {
|
||||
id: string;
|
||||
name: string;
|
||||
avatarUrl: string;
|
||||
platform: Platform;
|
||||
/** Alternate names / romanizations (NetEase alias, QQ other_name). */
|
||||
aliases?: string[];
|
||||
songCount?: number;
|
||||
albumCount?: number;
|
||||
}
|
||||
|
||||
export interface ArtistDetail extends Artist {
|
||||
/** Short biography, when the source provides one. */
|
||||
description?: string;
|
||||
}
|
||||
|
||||
/** One page of an artist's COMPLETE catalogue (the "全部歌曲" list), as opposed
|
||||
* to `getArtistSongs`, which only ever returns the hot top-N. */
|
||||
export interface ArtistSongPage {
|
||||
songs: Song[];
|
||||
/** Total tracks the source reports for this artist (best effort). */
|
||||
total: number;
|
||||
hasMore: boolean;
|
||||
}
|
||||
|
||||
export interface LyricLine {
|
||||
time: number; // seconds
|
||||
text: string;
|
||||
@@ -69,6 +97,8 @@ export interface SearchResult {
|
||||
songs: Song[];
|
||||
playlists: Playlist[];
|
||||
albums: Album[];
|
||||
/** Present only for sources with an artist entity (NetEase, QQ). */
|
||||
artists?: Artist[];
|
||||
}
|
||||
|
||||
export interface QrCodeResult {
|
||||
@@ -108,4 +138,16 @@ export interface MusicProvider {
|
||||
getDailyRecommendSongs?(): Promise<Song[]>;
|
||||
getUserPlaylists?(): Promise<Playlist[]>;
|
||||
getPlaylistDetail?(playlistId: string): Promise<PlaylistDetail | null>;
|
||||
getArtistDetail?(artistId: string): Promise<ArtistDetail | null>;
|
||||
/** The artist's most popular tracks, best-first. */
|
||||
getArtistSongs?(artistId: string, limit?: number): Promise<Song[]>;
|
||||
/** One page of the artist's full catalogue, best-first. Sources that can only
|
||||
* expose a fixed top-N list leave this unimplemented (the route then 501s and
|
||||
* the web hides the "全部歌曲" section). */
|
||||
getArtistAllSongs?(
|
||||
artistId: string,
|
||||
offset?: number,
|
||||
limit?: number
|
||||
): Promise<ArtistSongPage>;
|
||||
getArtistAlbums?(artistId: string, limit?: number): Promise<Album[]>;
|
||||
}
|
||||
Reference in new issue
Block a user