Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion electron/main/apis/qqmusic/core/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
* QM API 通用常量
*/

import he from "he";

/** 统一接口入口(移动端 musicu) */
export const QM_API_URL = "https://u.y.qq.com/cgi-bin/musicu.fcg";

Expand Down Expand Up @@ -40,7 +42,15 @@ export const formatSingerName = (
): string => {
if (!singers?.length) return "";
return singers
.map((item) => item[key])
.map((item) => decodeName(item[key]))
.filter((item): item is string => !!item)
.join(join);
};

/**
* HTML 实体反转义(he 库通用解码,支持所有命名实体和数字实体)
*/
export const decodeName = (str: string | null | undefined): string => {
if (!str) return "";
return he.decode(str);
};
6 changes: 3 additions & 3 deletions electron/main/apis/qqmusic/modules/album.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/** QM 专辑歌曲列表 */

import { qmRequest } from "../core/request";
import { formatSingerName } from "../core/config";
import { formatSingerName, decodeName } from "../core/config";
import type { QMModule } from "../core/types";

interface AlbumSong {
Expand Down Expand Up @@ -35,10 +35,10 @@ const album: QMModule = async (params) => {
{
id: String(song.id ?? ""),
mid: song.mid,
name: song.title ?? "",
name: decodeName(song.title),
artist: formatSingerName(song.singer),
artists: song.singer ?? [],
album: song.album?.name ?? "",
album: decodeName(song.album?.name),
albumMid: song.album?.mid ?? mid,
duration: (song.interval ?? 0) * 1000,
},
Expand Down
12 changes: 6 additions & 6 deletions electron/main/apis/qqmusic/modules/artist.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/** QM 歌手详情、热门歌曲与专辑 */

import { qmRequest } from "../core/request";
import { formatSingerName } from "../core/config";
import { formatSingerName, decodeName } from "../core/config";
import type { QMModule } from "../core/types";

interface SingerSong {
Expand Down Expand Up @@ -48,17 +48,17 @@ const artist: QMModule = async (params) => {
const songs = (songsData.songlist ?? []).map((song) => ({
id: String(song.id ?? ""),
mid: song.mid ?? "",
name: song.title ?? song.name ?? "",
name: decodeName(song.title ?? song.name),
artist: formatSingerName(song.singer),
artists: song.singer ?? [],
album: song.album?.name ?? "",
album: decodeName(song.album?.name),
albumMid: song.album?.mid ?? "",
duration: (song.interval ?? 0) * 1000,
}));
const albums = (albumsData.list ?? []).map((album) => ({
id: album.album_mid ?? "",
name: album.album_name ?? "",
artist: album.singer_name ?? songsData.singer_info?.name ?? "",
name: decodeName(album.album_name),
artist: decodeName(album.singer_name ?? songsData.singer_info?.name),
trackCount: album.latest_song?.song_count ?? 0,
publishTime: album.pub_time,
}));
Expand All @@ -67,7 +67,7 @@ const artist: QMModule = async (params) => {
artist: {
id: String(songsData.singer_info?.id ?? ""),
mid: songsData.singer_info?.mid ?? mid,
name: songsData.singer_info?.name ?? "",
name: decodeName(songsData.singer_info?.name),
songCount: songsData.total_song ?? songs.length,
albumCount: songsData.total_album ?? albumsData.total ?? albums.length,
},
Expand Down
10 changes: 5 additions & 5 deletions electron/main/apis/qqmusic/modules/leaderboard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
*/

import { qmRequest } from "../core/request";
import { formatSingerName } from "../core/config";
import { formatSingerName, decodeName } from "../core/config";
import type { QMModule } from "../core/types";

interface ToplistSong {
Expand Down Expand Up @@ -46,17 +46,17 @@ const leaderboard: QMModule = async (params) => {
.map((song) => ({
id: String(song.id ?? ""),
mid: song.mid ?? "",
name: song.title ?? "",
name: decodeName(song.title),
artist: formatSingerName(song.singer),
album: song.album?.name ?? "",
album: decodeName(song.album?.name),
albumMid: song.album?.mid ?? "",
duration: (song.interval ?? 0) * 1000,
}));

return {
code: 200,
title: data?.title ?? "",
subTitle: data?.titleDetail ?? "",
title: decodeName(data?.title),
subTitle: decodeName(data?.titleDetail),
updateTime: data?.updateTime ?? "",
cover: data?.headPicUrl ?? "",
songs,
Expand Down
16 changes: 8 additions & 8 deletions electron/main/apis/qqmusic/modules/search.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* session / search_id 偶发校验错误。详情接口仍走 musicu.fcg。
*/

import { QM_HEADERS, formatSingerName } from "../core/config";
import { QM_HEADERS, formatSingerName, decodeName } from "../core/config";
import type { QMModule } from "../core/types";

const SEARCH_URL = "https://c.y.qq.com/soso/fcgi-bin/client_search_cp";
Expand Down Expand Up @@ -92,10 +92,10 @@ const searchSongs = async (keywords: string, page: number, limit: number) => {
const songs = (bucket?.list ?? []).map((song) => ({
id: String(song.songid ?? ""),
mid: song.songmid ?? "",
name: song.songname ?? "",
name: decodeName(song.songname),
artist: formatSingerName(song.singer),
artists: song.singer ?? [],
album: song.albumname ?? "",
album: decodeName(song.albumname),
albumMid: song.albummid ?? "",
duration: (song.interval ?? 0) * 1000,
mediaMid: song.media_mid ?? "",
Expand All @@ -115,9 +115,9 @@ const searchAlbums = async (keywords: string, page: number, limit: number) => {
const bucket = body.data?.album as SearchBucket<QMAlbum> | undefined;
const albums = (bucket?.list ?? []).map((album) => ({
id: album.albumMID ?? String(album.albumID ?? ""),
name: album.albumName ?? "",
name: decodeName(album.albumName),
cover: secureUrl(album.albumPic),
artist: album.singerName ?? "",
artist: decodeName(album.singerName),
artistMid: album.singerMID ?? "",
trackCount: album.song_count ?? 0,
}));
Expand All @@ -130,7 +130,7 @@ const searchArtists = async (keywords: string, page: number, limit: number) => {
const bucket = body.data?.singer as SearchBucket<QMArtist> | undefined;
const artists = (bucket?.list ?? []).map((artist) => ({
id: artist.singerMID ?? String(artist.singerID ?? ""),
name: artist.singerName ?? "",
name: decodeName(artist.singerName),
cover: secureUrl(artist.singerPic),
albumCount: artist.albumNum ?? 0,
songCount: artist.songNum ?? 0,
Expand All @@ -154,9 +154,9 @@ const searchPlaylists = async (keywords: string, page: number, limit: number) =>
if (body.code !== 0) throw new Error(`QM 歌单搜索失败: ${body.code}`);
const playlists = (body.data?.list ?? []).map((playlist) => ({
id: playlist.dissid ?? "",
name: playlist.dissname ?? "",
name: decodeName(playlist.dissname),
cover: secureUrl(playlist.imgurl),
creator: playlist.creator?.name ?? "",
creator: decodeName(playlist.creator?.name),
trackCount: playlist.song_count ?? 0,
playCount: playlist.listennum ?? 0,
}));
Expand Down
6 changes: 3 additions & 3 deletions electron/main/apis/qqmusic/modules/song_info.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
*/

import { qmRequest } from "../core/request";
import { formatSingerName } from "../core/config";
import { formatSingerName, decodeName } from "../core/config";
import type { QMModule } from "../core/types";

interface TrackInfo {
Expand Down Expand Up @@ -37,9 +37,9 @@ const songInfo: QMModule = async (params) => {
song: {
id: String(track.id),
mid: track.mid,
name: track.title,
name: decodeName(track.title),
artist: formatSingerName(track.singer),
album: track.album?.name ?? "",
album: decodeName(track.album?.name),
albumMid: track.album?.mid ?? "",
duration: (track.interval ?? 0) * 1000,
file: track.file,
Expand Down
12 changes: 6 additions & 6 deletions electron/main/apis/qqmusic/modules/song_list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
* - id disstid(歌单 ID,必填)
*/

import { QM_HEADERS, formatSingerName } from "../core/config";
import { QM_HEADERS, formatSingerName, decodeName } from "../core/config";
import type { QMModule } from "../core/types";

interface CdSongItem {
Expand Down Expand Up @@ -59,20 +59,20 @@ const songList: QMModule = async (params) => {
const songs = (cd.songlist ?? []).map((item) => ({
id: String(item.songid ?? ""),
mid: item.songmid ?? "",
name: item.songname ?? "",
name: decodeName(item.songname),
artist: formatSingerName(item.singer),
artists: item.singer ?? [],
album: item.albumname ?? "",
album: decodeName(item.albumname),
albumMid: item.albummid ?? "",
duration: (item.interval ?? 0) * 1000,
}));

return {
code: 200,
id: cd.disstid,
name: cd.dissname ?? "",
description: cd.desc ?? "",
creator: cd.nickname ?? "",
name: decodeName(cd.dissname),
description: decodeName(cd.desc),
creator: decodeName(cd.nickname),
cover: cd.logo ?? "",
playCount: cd.visitnum ?? 0,
total: cd.songnum ?? songs.length,
Expand Down
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
"electron-updater": "^6.8.9",
"fast-xml-parser": "^5.10.1",
"font-list": "^2.1.0",
"he": "^1.2.0",
"hono": "^4.13.0",
"undici": "^8.10.0",
"ws": "^8.21.2",
Expand All @@ -86,6 +87,7 @@
"@pixi/filter-color-matrix": "^7.4.3",
"@pixi/sprite": "^7.4.3",
"@types/better-sqlite3": "^9.6.0",
"@types/he": "^1.2.3",
"@types/node": "^22.19.17",
"@types/sortablejs": "^1.15.9",
"@types/ws": "^8.18.1",
Expand Down
17 changes: 17 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.