Initial import: Music_Server, MusicFree, catalog-sync

This commit is contained in:
2026-05-23 16:51:14 +08:00
commit 069af30dba
847 changed files with 179878 additions and 0 deletions
@@ -0,0 +1 @@
{"plugins":[{"name":"小秋音乐","url":"https://mirror.ghproxy.com/https://raw.githubusercontent.com/Huibq/keep-alive/master/Music_Free/xiaoqiu.js","version":"0.3.0"},{"name":"小蜗音乐","url":"https://mirror.ghproxy.com/https://raw.githubusercontent.com/Huibq/keep-alive/master/Music_Free/xiaowo.js","version":"0.3.0"},{"name":"小芸音乐","url":"https://mirror.ghproxy.com/https://raw.githubusercontent.com/Huibq/keep-alive/master/Music_Free/xiaoyun.js","version":"0.3.0"},{"name":"小枸音乐","url":"https://mirror.ghproxy.com/https://raw.githubusercontent.com/Huibq/keep-alive/master/Music_Free/xiaogou.js","version":"0.3.0"},{"name":"小蜜音乐","url":"https://mirror.ghproxy.com/https://raw.githubusercontent.com/Huibq/keep-alive/master/Music_Free/xiaomi.js","version":"0.3.0"},{"name":"Audiomack","url":"https://gitee.com/maotoumao/MusicFreePlugins/raw/v0.1/dist/audiomack/index.js","version":"0.0.2"},{"name":"bilibili","url":"https://gitee.com/maotoumao/MusicFreePlugins/raw/v0.1/dist/bilibili/index.js","version":"0.1.15"},{"name":"歌词网","url":"https://gitee.com/maotoumao/MusicFreePlugins/raw/v0.1/dist/geciwang/index.js","version":"0.0.0"},{"name":"歌词千寻","url":"https://gitee.com/maotoumao/MusicFreePlugins/raw/v0.1/dist/geciqianxun/index.js","version":"0.0.0"},{"name":"快手","url":"https://gitee.com/maotoumao/MusicFreePlugins/raw/v0.1/dist/kuaishou/index.js","version":"0.0.1"},{"name":"猫耳FM","url":"https://gitee.com/maotoumao/MusicFreePlugins/raw/v0.1/dist/maoerfm/index.js","version":"0.1.4"},{"name":"Navidrome","url":"https://gitee.com/maotoumao/MusicFreePlugins/raw/v0.1/dist/navidrome/index.js","version":"0.0.0"},{"name":"音悦台","url":"https://gitee.com/maotoumao/MusicFreePlugins/raw/v0.1/dist/yinyuetai/index.js","version":"0.0.1"},{"name":"WebDAV","url":"https://gitee.com/maotoumao/MusicFreePlugins/raw/v0.1/dist/webdav/index.js","version":"0.0.2"},{"name":"Youtube","url":"https://gitee.com/maotoumao/MusicFreePlugins/raw/v0.1/dist/youtube/index.js","version":"0.0.1"}]}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,178 @@
"use strict";
const test = require("node:test");
const assert = require("node:assert/strict");
function loadPluginFresh() {
const modulePath = require.resolve("./music_server_lan");
delete require.cache[modulePath];
return require("./music_server_lan");
}
function clearRuntimeEnv() {
delete globalThis.env;
}
test.afterEach(() => {
clearRuntimeEnv();
});
test("plugin.userVariables exposes public and lan base urls", () => {
const plugin = loadPluginFresh();
const keys = plugin.userVariables.map((item) => item.key);
assert.deepEqual(keys, ["baseUrl", "lanBaseUrl", "accessToken"]);
});
test("recommend requests use lan base url when lan health probe succeeds", async () => {
const plugin = loadPluginFresh();
plugin.__clearTestState();
plugin.__setConfigForTests({
baseUrl: "http://64.83.43.123:18081",
lanBaseUrl: "http://192.168.5.43:18081",
accessToken: "",
});
const originalFetch = globalThis.fetch;
const fetchCalls = [];
globalThis.fetch = async (url, options) => {
fetchCalls.push({ url, options });
if (url === "http://192.168.5.43:18081/healthz") {
return {
ok: true,
status: 200,
text: async () => JSON.stringify({ status: "ok" }),
};
}
if (url === "http://192.168.5.43:18081/mf/v1/recommend/tags") {
return {
ok: true,
status: 200,
text: async () =>
JSON.stringify({
pinned: [{ id: "all", title: "all" }],
data: [],
}),
};
}
throw new Error(`Unexpected request: ${url}`);
};
try {
const result = await plugin.getRecommendSheetTags();
assert.equal(result.pinned[0].id, "all");
} finally {
globalThis.fetch = originalFetch;
}
assert.deepEqual(
fetchCalls.map((entry) => entry.url),
[
"http://192.168.5.43:18081/healthz",
"http://192.168.5.43:18081/mf/v1/recommend/tags",
],
);
});
test("recommend requests fall back to public base url when lan health probe fails", async () => {
const plugin = loadPluginFresh();
plugin.__clearTestState();
plugin.__setConfigForTests({
baseUrl: "http://64.83.43.123:18081",
lanBaseUrl: "http://192.168.5.43:18081",
accessToken: "",
});
const originalFetch = globalThis.fetch;
const fetchCalls = [];
globalThis.fetch = async (url, options) => {
fetchCalls.push({ url, options });
if (url === "http://192.168.5.43:18081/healthz") {
throw new Error("connect failed");
}
if (url === "http://64.83.43.123:18081/mf/v1/recommend/tags") {
return {
ok: true,
status: 200,
text: async () =>
JSON.stringify({
pinned: [{ id: "all", title: "all" }],
data: [],
}),
};
}
throw new Error(`Unexpected request: ${url}`);
};
try {
const result = await plugin.getRecommendSheetTags();
assert.equal(result.pinned[0].id, "all");
} finally {
globalThis.fetch = originalFetch;
}
assert.deepEqual(
fetchCalls.map((entry) => entry.url),
[
"http://192.168.5.43:18081/healthz",
"http://64.83.43.123:18081/mf/v1/recommend/tags",
],
);
});
test("getMediaSource joins relative stream url against lan base url after successful probe", async () => {
const plugin = loadPluginFresh();
plugin.__clearTestState();
plugin.__setConfigForTests({
baseUrl: "http://64.83.43.123:18081",
lanBaseUrl: "http://192.168.5.43:18081",
accessToken: "",
});
const originalFetch = globalThis.fetch;
const fetchCalls = [];
globalThis.fetch = async (url, options) => {
fetchCalls.push({ url, options });
if (url === "http://192.168.5.43:18081/healthz") {
return {
ok: true,
status: 200,
text: async () => JSON.stringify({ status: "ok" }),
};
}
if (url === "http://192.168.5.43:18081/mf/v1/media/resolve") {
return {
ok: true,
status: 200,
text: async () =>
JSON.stringify({
stream: {
url: "/mf/v1/media/stream/token-1",
},
selected_source: {
quality: "super",
},
}),
};
}
throw new Error(`Unexpected request: ${url}`);
};
try {
const result = await plugin.getMediaSource({ id: "song-1" }, "high");
assert.deepEqual(result, {
url: "http://192.168.5.43:18081/mf/v1/media/stream/token-1",
headers: {},
quality: "super",
});
} finally {
globalThis.fetch = originalFetch;
}
assert.deepEqual(
fetchCalls.map((entry) => entry.url),
[
"http://192.168.5.43:18081/healthz",
"http://192.168.5.43:18081/mf/v1/media/resolve",
],
);
});
@@ -0,0 +1 @@
{"plugins":[{"name":"小秋音乐","url":"https://fastly.jsdelivr.net/gh/Huibq/keep-alive/Music_Free/xiaoqiu.js","version":"0.3.0"},{"name":"小蜗音乐","url":"https://fastly.jsdelivr.net/gh/Huibq/keep-alive/Music_Free/xiaowo.js","version":"0.3.0"},{"name":"小芸音乐","url":"https://fastly.jsdelivr.net/gh/Huibq/keep-alive/Music_Free/xiaoyun.js","version":"0.3.0"},{"name":"小枸音乐","url":"https://fastly.jsdelivr.net/gh/Huibq/keep-alive/Music_Free/xiaogou.js","version":"0.3.0"},{"name":"小蜜音乐","url":"https://fastly.jsdelivr.net/gh/Huibq/keep-alive/Music_Free/xiaomi.js","version":"0.3.0"}]}
@@ -0,0 +1,3 @@
"use strict";
module.exports = require("./music_server");
@@ -0,0 +1,361 @@
"use strict";
const mockGet = jest.fn();
const mockPost = jest.fn();
jest.mock("axios", () => ({
create: jest.fn(() => ({
get: mockGet,
post: mockPost,
})),
}));
const plugin = require("./netease_17000");
function makeSongUrlResponse(id, url) {
return {
data: {
data: [
{
id,
url,
time: 0,
},
],
},
};
}
describe("netease_17000 getMediaSource fallback", () => {
beforeEach(() => {
mockGet.mockReset();
mockPost.mockReset();
});
test("falls back to noCopyrightRcmd.songId when original id has no source", async () => {
const originalId = "5268162";
const fallbackId = "2035171101";
const fallbackUrl = "http://example.com/fallback.mp3";
mockPost.mockResolvedValueOnce({
data: {
primary: null,
alternates: [],
},
});
mockGet.mockImplementation((url, config) => {
const params = (config && config.params) || {};
if (url.includes("/song/url/v1") && String(params.id) === originalId) {
return Promise.resolve(makeSongUrlResponse(Number(originalId), null));
}
if (url.includes("/song/detail") && String(params.ids) === originalId) {
return Promise.resolve({
data: {
songs: [
{
id: Number(originalId),
name: "origin",
ar: [{ name: "artist" }],
al: { name: "album" },
dt: 260000,
noCopyrightRcmd: {
songId: fallbackId,
},
},
],
},
});
}
if (url.includes("/song/url/v1") && String(params.id) === fallbackId) {
return Promise.resolve(makeSongUrlResponse(Number(fallbackId), fallbackUrl));
}
throw new Error(`Unexpected request: ${url} ${JSON.stringify(params)}`);
});
const result = await plugin.getMediaSource(
{
id: originalId,
title: "origin",
artist: "artist",
album: "album",
duration: 260,
},
"standard",
);
expect(result).toEqual({ url: fallbackUrl });
const requestedIds = mockGet.mock.calls
.filter((call) => call[0].includes("/song/url/v1"))
.map((call) => String((call[1] && call[1].params && call[1].params.id) || ""));
expect(requestedIds).toContain(originalId);
expect(requestedIds).toContain(fallbackId);
});
test("upgrades 126 netease media url to https during fallback", async () => {
const originalId = "5268162";
const fallbackId = "2035171101";
const fallbackUrl =
"http://m701.music.126.net/test-path/audio.mp3?vuutv=a+b/c";
mockPost.mockResolvedValueOnce({
data: {
primary: null,
alternates: [],
},
});
mockGet.mockImplementation((url, config) => {
const params = (config && config.params) || {};
if (url.includes("/song/url/v1") && String(params.id) === originalId) {
return Promise.resolve(makeSongUrlResponse(Number(originalId), null));
}
if (url.includes("/song/detail") && String(params.ids) === originalId) {
return Promise.resolve({
data: {
songs: [
{
id: Number(originalId),
name: "origin",
ar: [{ name: "artist" }],
al: { name: "album" },
dt: 260000,
noCopyrightRcmd: {
songId: fallbackId,
},
},
],
},
});
}
if (url.includes("/song/url/v1") && String(params.id) === fallbackId) {
return Promise.resolve(makeSongUrlResponse(Number(fallbackId), fallbackUrl));
}
throw new Error(`Unexpected request: ${url} ${JSON.stringify(params)}`);
});
const result = await plugin.getMediaSource(
{
id: originalId,
title: "origin",
artist: "artist",
album: "album",
duration: 260,
},
"standard",
);
expect(result).toEqual({
url: "https://m701.music.126.net/test-path/audio.mp3?vuutv=a+b/c",
});
});
test("prefers relay source for fallback song id when relay can resolve it", async () => {
const originalId = "5268162";
const fallbackId = "2035171101";
const relayUrl = "http://111.228.62.29:17000/api/unblock/stream/fallback-token";
const officialFallbackUrl = "http://m701.music.126.net/from-official.mp3";
mockPost
.mockResolvedValueOnce({
data: {
primary: null,
alternates: [],
},
})
.mockResolvedValueOnce({
data: {
primary: {
streamUrl: relayUrl,
},
alternates: [],
},
});
mockGet.mockImplementation((url, config) => {
const params = (config && config.params) || {};
const id = String(params.id || params.ids || "");
if (url.includes("/song/url/v1") && id === originalId) {
return Promise.resolve(makeSongUrlResponse(Number(originalId), null));
}
if (url.includes("/song/detail") && String(params.ids) === originalId) {
return Promise.resolve({
data: {
songs: [
{
id: Number(originalId),
name: "origin",
ar: [{ name: "artist" }],
al: { name: "album" },
dt: 260000,
noCopyrightRcmd: {
songId: fallbackId,
},
},
],
},
});
}
if (url.includes("/song/detail") && String(params.ids) === fallbackId) {
return Promise.resolve({
data: {
songs: [
{
id: Number(fallbackId),
name: "fallback-name",
ar: [{ name: "fallback-artist" }],
al: { name: "fallback-album" },
dt: 260000,
},
],
},
});
}
if (url.includes("/song/url/v1") && id === fallbackId) {
return Promise.resolve(makeSongUrlResponse(Number(fallbackId), officialFallbackUrl));
}
throw new Error(`Unexpected request: ${url} ${JSON.stringify(params)}`);
});
const result = await plugin.getMediaSource(
{
id: originalId,
title: "origin",
artist: "artist",
album: "album",
duration: 260,
},
"standard",
);
expect(result).toEqual({ url: relayUrl });
const fallbackOfficialCalls = mockGet.mock.calls.filter(
(call) =>
call[0].includes("/song/url/v1") &&
String((call[1] && call[1].params && call[1].params.id) || "") === fallbackId,
);
expect(fallbackOfficialCalls).toHaveLength(0);
});
});
describe("netease_17000 recommend sheets", () => {
beforeEach(() => {
mockGet.mockReset();
mockPost.mockReset();
});
test("builds recommend tag groups from playlist catlist", async () => {
mockGet.mockImplementation((url) => {
if (url.includes("/playlist/catlist")) {
return Promise.resolve({
data: {
categories: {
0: "语种",
1: "风格",
},
sub: [
{ category: 0, name: "华语", hot: true },
{ category: 0, name: "欧美", hot: false },
{ category: 1, name: "流行", hot: true },
],
},
});
}
throw new Error(`Unexpected request: ${url}`);
});
const result = await plugin.getRecommendSheetTags();
expect(result).toEqual({
pinned: [
{ id: "华语", title: "华语" },
{ id: "流行", title: "流行" },
],
data: [
{
title: "语种",
data: [
{ id: "华语", title: "华语" },
{ id: "欧美", title: "欧美" },
],
},
{
title: "风格",
data: [
{ id: "流行", title: "流行" },
],
},
],
});
});
test("loads recommend sheets by tag through top playlist endpoint", async () => {
mockGet.mockImplementation((url, config) => {
const params = (config && config.params) || {};
if (url.includes("/top/playlist")) {
return Promise.resolve({
data: {
playlists: [
{
id: 101,
name: "歌单A",
creator: { nickname: "作者A" },
description: "descA",
coverImgUrl: "coverA",
trackCount: 50,
playCount: 1000,
},
],
total: 21,
more: true,
},
});
}
throw new Error(`Unexpected request: ${url} ${JSON.stringify(params)}`);
});
const result = await plugin.getRecommendSheetsByTag({ id: "华语" }, 1);
expect(result).toEqual({
isEnd: false,
data: [
{
id: "101",
title: "歌单A",
artist: "作者A",
description: "descA",
coverImg: "coverA",
worksNum: 50,
playCount: 1000,
},
],
});
const topPlaylistCall = mockGet.mock.calls.find((call) =>
call[0].includes("/top/playlist"),
);
expect(topPlaylistCall).toBeTruthy();
expect(topPlaylistCall[1].params).toMatchObject({
cat: "华语",
limit: 20,
offset: 0,
order: "hot",
});
});
});
@@ -0,0 +1,425 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const axios_1 = require("axios");
const cheerio_1 = require("cheerio");
const CryptoJs = require("crypto-js");
const he = require("he");
const pageSize = 20;
function formatMusicItem(_) {
var _a, _b, _c, _d, _e, _f, _g, _h, _i;
return {
id: (_d = _.FileHash) !== null && _d !== void 0 ? _d : _.Grp[0].FileHash,
title: (_a = _.SongName) !== null && _a !== void 0 ? _a : _.OriSongName,
artist: (_b = _.SingerName) !== null && _b !== void 0 ? _b : _.Singers[0].name,
album: (_c = _.AlbumName) !== null && _c !== void 0 ? _c : _.Grp[0].AlbumName,
album_id: (_e = _.AlbumID) !== null && _e !== void 0 ? _e : _.Grp[0].AlbumID,
album_audio_id: 0,
duration: _.Duration,
artwork: ((_f = _.Image) !== null && _f !== void 0 ? _f : _.Grp[0].Image).replace("{size}", "1080"),
"320hash": (_i = _.HQFileHash) !== null && _i !== void 0 ? _i : undefined,
sqhash: (_g = _.SQFileHash) !== null && _g !== void 0 ? _g : undefined,
ResFileHash: (_h = _.ResFileHash) !== null && _h !== void 0 ? _h : undefined,
};
}
function formatMusicItem2(_) {
var _a, _b, _c, _d, _e, _f, _g;
return {
id: _.hash,
title: _.songname,
artist: (_a = _.singername) !== null && _a !== void 0 ? _a : (((_c = (_b = _.authors) === null || _b === void 0 ? void 0 : _b.map((_) => { var _a; return (_a = _ === null || _ === void 0 ? void 0 : _.author_name) !== null && _a !== void 0 ? _a : ""; })) === null || _c === void 0 ? void 0 : _c.join(", ")) ||
((_f = (_e = (_d = _.filename) === null || _d === void 0 ? void 0 : _d.split("-")) === null || _e === void 0 ? void 0 : _e[0]) === null || _f === void 0 ? void 0 : _f.trim())),
album: (_g = _.album_name) !== null && _g !== void 0 ? _g : _.remark,
album_id: _.album_id,
album_audio_id: _.album_audio_id,
artwork: _.album_sizable_cover
? _.album_sizable_cover.replace("{size}", "400")
: undefined,
duration: _.duration,
"320hash": _["320hash"],
sqhash: _.sqhash,
origin_hash: _.origin_hash,
};
}
function formatImportMusicItem(_) {
var _a, _b, _c, _d, _e, _f, _g;
let title = _.name;
const singerName = _.singername;
if (singerName && title) {
const index = title.indexOf(singerName);
if (index !== -1) {
title = (_a = title.substring(index + singerName.length + 2)) === null || _a === void 0 ? void 0 : _a.trim();
}
if (!title) {
title = singerName;
}
}
const qualites = _.relate_goods;
return {
id: _.hash,
title,
artist: singerName,
album: (_b = _.albumname) !== null && _b !== void 0 ? _b : "",
album_id: _.album_id,
album_audio_id: _.album_audio_id,
artwork: (_d = (_c = _ === null || _ === void 0 ? void 0 : _.info) === null || _c === void 0 ? void 0 : _c.image) === null || _d === void 0 ? void 0 : _d.replace("{size}", "400"),
"320hash": (_e = qualites === null || qualites === void 0 ? void 0 : qualites[1]) === null || _e === void 0 ? void 0 : _e.hash,
sqhash: (_f = qualites === null || qualites === void 0 ? void 0 : qualites[2]) === null || _f === void 0 ? void 0 : _f.hash,
origin_hash: (_g = qualites === null || qualites === void 0 ? void 0 : qualites[3]) === null || _g === void 0 ? void 0 : _g.hash,
};
}
const headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.0.0 Safari/537.36",
Accept: "*/*",
"Accept-Encoding": "gzip, deflate",
"Accept-Language": "zh-CN,zh;q=0.9",
};
async function searchMusic(query, page) {
const res = (await axios_1.default.get("https://songsearch.kugou.com/song_search_v2", {
headers,
params: {
keyword: query,
page,
pagesize: pageSize,
userid: 0,
clientver: "",
platform: "WebFilter",
filter: 2,
iscorrection: 1,
privilege_filter: 0,
area_code: 1,
},
})).data;
const songs = res.data.lists.map(formatMusicItem);
return {
isEnd: page * pageSize >= res.data.total,
data: songs,
};
}
async function searchAlbum(query, page) {
const res = (await axios_1.default.get("http://msearch.kugou.com/api/v3/search/album", {
headers,
params: {
version: 9108,
iscorrection: 1,
highlight: "em",
plat: 0,
keyword: query,
pagesize: 20,
page,
sver: 2,
with_res_tag: 0,
},
})).data;
const albums = res.data.info.map((_) => {
var _a, _b;
return ({
id: _.albumid,
artwork: (_a = _.imgurl) === null || _a === void 0 ? void 0 : _a.replace("{size}", "400"),
artist: _.singername,
title: (0, cheerio_1.load)(_.albumname).text(),
description: _.intro,
date: (_b = _.publishtime) === null || _b === void 0 ? void 0 : _b.slice(0, 10),
});
});
return {
isEnd: page * 20 >= res.data.total,
data: albums,
};
}
async function searchMusicSheet(query, page) {
const res = (await axios_1.default.get("http://mobilecdn.kugou.com/api/v3/search/special", {
headers,
params: {
format: "json",
keyword: query,
page,
pagesize: pageSize,
showtype: 1,
},
})).data;
const sheets = res.data.info.map(item => ({
title: item.specialname,
createAt: item.publishtime,
description: item.intro,
artist: item.nickname,
coverImg: item.imgurl,
gid: item.gid,
playCount: item.playcount,
id: item.specialid,
worksNum: item.songcount
}));
return {
isEnd: page * pageSize >= res.data.total,
data: sheets,
};
}
const qualityLevels = {
low: "128k",
standard: "320k",
high: "flac",
super: "wav",
};
async function getMediaSource(musicItem, quality) {
const targetQualities = [
qualityLevels[quality] || "320k",
"320k",
"128k",
].filter((item, index, arr) => !!item && arr.indexOf(item) === index);
for (const targetQuality of targetQualities) {
const res = (
await axios_1.default.get(`https://lxmusicapi.onrender.com/url/kg/${musicItem.id}/${targetQuality}`, {
headers: {
"X-Request-Key": "share-v3"
},
})
).data;
if (res === null || res === void 0 ? void 0 : res.url) {
return {
url: res.url,
};
}
}
return null;
}
async function getTopLists() {
const lists = (await axios_1.default.get("http://mobilecdnbj.kugou.com/api/v3/rank/list?version=9108&plat=0&showtype=2&parentid=0&apiver=6&area_code=1&withsong=0&with_res_tag=0", {
headers: headers,
})).data.data.info;
const res = [
{
title: "热门榜单",
data: [],
},
{
title: "特色音乐榜",
data: [],
},
{
title: "全球榜",
data: [],
},
];
const extra = {
title: "其他",
data: [],
};
lists.forEach((item) => {
var _a, _b, _c, _d;
if (item.classify === 1 || item.classify === 2) {
res[0].data.push({
id: item.rankid,
description: item.intro,
coverImg: (_a = item.imgurl) === null || _a === void 0 ? void 0 : _a.replace("{size}", "400"),
title: item.rankname,
});
}
else if (item.classify === 3 || item.classify === 5) {
res[1].data.push({
id: item.rankid,
description: item.intro,
coverImg: (_b = item.imgurl) === null || _b === void 0 ? void 0 : _b.replace("{size}", "400"),
title: item.rankname,
});
}
else if (item.classify === 4) {
res[2].data.push({
id: item.rankid,
description: item.intro,
coverImg: (_c = item.imgurl) === null || _c === void 0 ? void 0 : _c.replace("{size}", "400"),
title: item.rankname,
});
}
else {
extra.data.push({
id: item.rankid,
description: item.intro,
coverImg: (_d = item.imgurl) === null || _d === void 0 ? void 0 : _d.replace("{size}", "400"),
title: item.rankname,
});
}
});
if (extra.data.length !== 0) {
res.push(extra);
}
return res;
}
async function getTopListDetail(topListItem) {
const res = await axios_1.default.get(`http://mobilecdnbj.kugou.com/api/v3/rank/song?version=9108&ranktype=0&plat=0&pagesize=100&area_code=1&page=1&volid=35050&rankid=${topListItem.id}&with_res_tag=0`, {
headers,
});
return Object.assign(Object.assign({}, topListItem), { musicList: res.data.data.info.map(formatMusicItem2) });
}
async function getLyricDownload(lyrdata) {
const result = (await (0, axios_1.default)({
// url: `http://lyrics.kugou.com/download?ver=1&client=pc&id=${lyrdata.id}&accesskey=${lyrdata.accessKey}&fmt=krc&charset=utf8`,
url: `http://lyrics.kugou.com/download?ver=1&client=pc&id=${lyrdata.id}&accesskey=${lyrdata.accessKey}&fmt=lrc&charset=utf8`,
headers: {
'KG-RC': 1,
'KG-THash': 'expand_search_manager.cpp:852736169:451',
'User-Agent': 'KuGou2012-9020-ExpandSearchManager',
},
method: "get",
xsrfCookieName: "XSRF-TOKEN",
withCredentials: true,
})).data;
return {
rawLrc: he.decode(CryptoJs.enc.Base64.parse(result.content).toString(CryptoJs.enc.Utf8)),
};
}
// copy from lxmusic https://github.com/lyswhut/lx-music-desktop/blob/master/src/renderer/utils/musicSdk/kg/lyric.js#L114
async function getLyric(musicItem) {
const result = (await (0, axios_1.default)({
url: `http://lyrics.kugou.com/search?ver=1&man=yes&client=pc&keyword=${musicItem.title}&hash=${musicItem.id}&timelength=${musicItem.duration}`,
headers: {
'KG-RC': 1,
'KG-THash': 'expand_search_manager.cpp:852736169:451',
'User-Agent': 'KuGou2012-9020-ExpandSearchManager',
},
method: "get",
xsrfCookieName: "XSRF-TOKEN",
withCredentials: true,
})).data;
const info = result.candidates[0];
return await getLyricDownload({ id: info.id, accessKey: info.accesskey })
}
async function getAlbumInfo(albumItem, page = 1) {
const res = (await axios_1.default.get("http://mobilecdn.kugou.com/api/v3/album/song", {
params: {
version: 9108,
albumid: albumItem.id,
plat: 0,
pagesize: 100,
area_code: 1,
page,
with_res_tag: 0,
},
})).data;
return {
isEnd: page * 100 >= res.data.total,
albumItem: {
worksNum: res.data.total,
},
musicList: res.data.info.map((_) => {
var _a;
const [artist, songname] = _.filename.split("-");
return {
id: _.hash,
title: songname.trim(),
artist: artist.trim(),
album: (_a = _.album_name) !== null && _a !== void 0 ? _a : _.remark,
album_id: _.album_id,
album_audio_id: _.album_audio_id,
artwork: albumItem.artwork,
"320hash": _.HQFileHash,
sqhash: _.SQFileHash,
origin_hash: _.id,
};
}),
};
}
async function importMusicSheet(urlLike) {
var _a;
let id = (_a = urlLike.match(/^(?:.*?)(\d+)(?:.*?)$/)) === null || _a === void 0 ? void 0 : _a[1];
let musicList = [];
if (!id) {
return;
}
let res = await axios_1.default.post(`http://t.kugou.com/command/`, {
appid: 1001,
clientver: 9020,
mid: "21511157a05844bd085308bc76ef3343",
clienttime: 640612895,
key: "36164c4015e704673c588ee202b9ecb8",
data: id,
});
if (res.status === 200 && res.data.status === 1) {
let data = res.data.data;
let response = await axios_1.default.post(`http://www2.kugou.kugou.com/apps/kucodeAndShare/app/`, {
appid: 1001,
clientver: 10112,
mid: "70a02aad1ce4648e7dca77f2afa7b182",
clienttime: 722219501,
key: "381d7062030e8a5a94cfbe50bfe65433",
data: {
id: data.info.id,
type: 3,
userid: data.info.userid,
collect_type: data.info.collect_type,
page: 1,
pagesize: data.info.count,
},
});
if (response.status === 200 && response.data.status === 1) {
let resource = [];
response.data.data.forEach((song) => {
resource.push({
album_audio_id: 0,
album_id: "0",
hash: song.hash,
id: 0,
name: song.filename.replace(".mp3", ""),
page_id: 0,
type: "audio",
});
});
let postData = {
appid: 1001,
area_code: "1",
behavior: "play",
clientver: "10112",
dfid: "2O3jKa20Gdks0LWojP3ly7ck",
mid: "70a02aad1ce4648e7dca77f2afa7b182",
need_hash_offset: 1,
relate: 1,
resource,
token: "",
userid: "0",
vip: 0,
};
var result = await axios_1.default.post(`https://gateway.kugou.com/v2/get_res_privilege/lite?appid=1001&clienttime=1668883879&clientver=10112&dfid=2O3jKa20Gdks0LWojP3ly7ck&mid=70a02aad1ce4648e7dca77f2afa7b182&userid=390523108&uuid=92691C6246F86F28B149BAA1FD370DF1`, postData, {
headers: {
"x-router": "media.store.kugou.com",
},
});
if (response.status === 200 && response.data.status === 1) {
musicList = result.data.data
.map(formatImportMusicItem);
}
}
}
return musicList;
}
module.exports = {
platform: "小枸音乐",
version: "0.3.0",
author: 'Huibq',
appVersion: ">0.1.0-alpha.0",
srcUrl: "https://fastly.jsdelivr.net/gh/Huibq/keep-alive/Music_Free/xiaogou.js",
cacheControl: "no-cache",
description: "",
primaryKey: ["id", "album_id", "album_audio_id"],
hints: {
importMusicSheet: [
"仅支持酷狗APP通过酷狗码导入,输入纯数字酷狗码即可。",
"导入时间和歌单大小有关,请耐心等待",
],
},
supportedSearchType: ["music", "album", "sheet"],
async search(query, page, type) {
if (type === "music") {
return await searchMusic(query, page);
}
else if (type === "album") {
return await searchAlbum(query, page);
}
else if (type === "sheet") {
return await searchMusicSheet(query, page);
}
},
getMediaSource,
getTopLists,
getLyric,
getTopListDetail,
getAlbumInfo,
importMusicSheet,
};
@@ -0,0 +1,665 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const axios_1 = require("axios");
const cheerio_1 = require("cheerio");
const CryptoJS = require("crypto-js");
const searchRows = 20;
async function searchBase(query, page, type) {
const headers = {
Accept: "application/json, text/javascript, */*; q=0.01",
"Accept-Encoding": "gzip, deflate, br",
"Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6",
Connection: "keep-alive",
"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
Host: "m.music.migu.cn",
Referer: `https://m.music.migu.cn/v3/search?keyword=${encodeURIComponent(query)}`,
"Sec-Fetch-Dest": "empty",
"Sec-Fetch-Mode": "cors",
"Sec-Fetch-Site": "same-origin",
"User-Agent": "Mozilla/5.0 (Linux; Android 6.0.1; Moto G (4)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.114 Mobile Safari/537.36 Edg/89.0.774.68",
"X-Requested-With": "XMLHttpRequest",
};
const params = {
keyword: query,
type,
pgc: page,
rows: searchRows,
};
const data = await axios_1.default.get("https://m.music.migu.cn/migu/remoting/scr_search_tag", { headers, params });
return data.data;
}
// function musicCanPlayFilter(_) {
// return _.lisSQ || _.lisHQ || _.lisBq || _.lisCr || _.lisQq || _.listenUrl || _.mp3;
// }
function musicCanPlayFilter(_) {
return _.mp3 || _.listenUrl || _.lisQq || _.lisCr;
}
async function searchMusic(query, page) {
const data = await searchBase(query, page, 2);
const musics = data.musics.map((_) => ({
id: _.id,
artwork: _.cover,
title: _.songName,
artist: _.artist,
album: _.albumName,
url: musicCanPlayFilter(_),
copyrightId: _.copyrightId,
singerId: _.singerId,
}));
return {
isEnd: +data.pageNo * searchRows >= data.pgt,
data: musics,
};
}
async function searchAlbum(query, page) {
const data = await searchBase(query, page, 4);
const albums = data.albums.map((_) => ({
id: _.id,
artwork: _.albumPicL,
title: _.title,
date: _.publishDate,
artist: (_.singer || []).map((s) => s.name).join(","),
singer: _.singer,
fullSongTotal: _.fullSongTotal,
}));
return {
isEnd: +data.pageNo * searchRows >= data.pgt,
data: albums,
};
}
async function searchArtist(query, page) {
const data = await searchBase(query, page, 1);
const artists = data.artists.map((result) => ({
name: result.title,
id: result.id,
avatar: result.artistPicL,
worksNum: result.songNum,
}));
return {
isEnd: +data.pageNo * searchRows >= data.pgt,
data: artists,
};
}
async function searchMusicSheet(query, page) {
const data = await searchBase(query, page, 6);
const musicsheet = data.songLists.map((result) => ({
title: result.name,
id: result.id,
artist: result.userName,
artwork: result.img,
description: result.intro,
worksNum: result.musicNum,
playCount: result.playNum,
}));
return {
isEnd: +data.pageNo * searchRows >= data.pgt,
data: musicsheet,
};
}
async function searchLyric(query, page) {
const data = await searchBase(query, page, 7);
const lyrics = data.songs.map((result) => ({
title: result.title,
id: result.id,
artist: result.artist,
artwork: result.cover,
lrc: result.lyrics,
album: result.albumName,
copyrightId: result.copyrightId,
}));
return {
isEnd: +data.pageNo * searchRows >= data.pgt,
data: lyrics,
};
}
async function getArtistAlbumWorks(artistItem, page) {
const headers = {
accept: "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",
"accept-encoding": "gzip, deflate, br",
"accept-language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6",
connection: "keep-alive",
host: "music.migu.cn",
referer: "http://music.migu.cn",
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.0.0 Safari/537.36",
"Cache-Control": "max-age=0",
};
const html = (await axios_1.default.get(`https://music.migu.cn/v3/music/artist/${artistItem.id}/album?page=${page}`, {
headers,
})).data;
const $ = (0, cheerio_1.load)(html);
const rawAlbums = $("div.artist-album-list").find("li");
const albums = [];
for (let i = 0; i < rawAlbums.length; ++i) {
const al = $(rawAlbums[i]);
const artwork = al.find(".thumb-img").attr("data-original");
albums.push({
id: al.find(".album-play").attr("data-id"),
title: al.find(".album-name").text(),
artwork: artwork.startsWith("//") ? `https:${artwork}` : artwork,
date: "",
artist: artistItem.name,
});
}
return {
isEnd: $(".pagination-next").hasClass("disabled"),
data: albums,
};
}
async function getArtistWorks(artistItem, page, type) {
if (type === "music") {
const headers = {
Accept: "application/json, text/javascript, */*; q=0.01",
"Accept-Encoding": "gzip, deflate, br",
"Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6",
Connection: "keep-alive",
"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
Host: "m.music.migu.cn",
Referer: `https://m.music.migu.cn/migu/l/?s=149&p=163&c=5123&j=l&id=${artistItem.id}`,
"Sec-Fetch-Dest": "empty",
"Sec-Fetch-Mode": "cors",
"Sec-Fetch-Site": "same-origin",
"User-Agent": "Mozilla/5.0 (Linux; Android 6.0.1; Moto G (4)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.114 Mobile Safari/537.36 Edg/89.0.774.68",
"X-Requested-With": "XMLHttpRequest",
};
const musicList = (await axios_1.default.get("https://m.music.migu.cn/migu/remoting/cms_artist_song_list_tag", {
headers,
params: {
artistId: artistItem.id,
pageSize: 20,
pageNo: page - 1,
},
})).data || {};
return {
data: musicList.result.results.map((_) => ({
id: _.songId,
artwork: _.picL,
title: _.songName,
artist: (_.singerName || []).join(", "),
album: _.albumName,
url: musicCanPlayFilter(_),
rawLrc: _.lyricLrc,
copyrightId: _.copyrightId,
singerId: _.singerId,
})),
};
}
else if (type === "album") {
return getArtistAlbumWorks(artistItem, page);
}
}
async function getLyric(musicItem) {
const headers = {
Accept: "application/json, text/javascript, */*; q=0.01",
"Accept-Encoding": "gzip, deflate, br",
"Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6",
Connection: "keep-alive",
"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
Host: "m.music.migu.cn",
Referer: `https://m.music.migu.cn/migu/l/?s=149&p=163&c=5200&j=l&id=${musicItem.copyrightId}`,
"Sec-Fetch-Dest": "empty",
"Sec-Fetch-Mode": "cors",
"Sec-Fetch-Site": "same-origin",
"User-Agent": "Mozilla/5.0 (Linux; Android 6.0.1; Moto G (4)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.114 Mobile Safari/537.36 Edg/89.0.774.68",
"X-Requested-With": "XMLHttpRequest",
};
const result = (await axios_1.default.get("https://m.music.migu.cn/migu/remoting/cms_detail_tag", {
headers,
params: {
cpid: musicItem.copyrightId,
},
})).data;
return {
rawLrc: result.data.lyricLrc,
};
}
async function getMusicSheetInfo(sheet, page) {
const res = (await axios_1.default.get("https://m.music.migu.cn/migumusic/h5/playlist/songsInfo", {
params: {
palylistId: sheet.id,
pageNo: page,
pageSize: 30,
},
headers: {
Host: "m.music.migu.cn",
referer: "https://m.music.migu.cn/v4/music/playlist/",
By: "7242bd16f68cd9b39c54a8e61537009f",
"User-Agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1 Edg/113.0.0.0",
},
})).data.data;
if (!res) {
return {
isEnd: true,
musicList: [],
};
}
const isEnd = res.total < 30;
return {
isEnd,
musicList: res.items
.filter((item) => { var _a; return ((_a = item === null || item === void 0 ? void 0 : item.fullSong) === null || _a === void 0 ? void 0 : _a.vipFlag) === 0; })
.map((_) => {
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
return ({
id: _.id,
artwork: ((_a = _.mediumPic) === null || _a === void 0 ? void 0 : _a.startsWith("//"))
? `http:${_.mediumPic}`
: _.mediumPic,
title: _.name,
artist: (_f = (_e = (_d = (_c = (_b = _.singers) === null || _b === void 0 ? void 0 : _b.map) === null || _c === void 0 ? void 0 : _c.call(_b, (_) => _.name)) === null || _d === void 0 ? void 0 : _d.join) === null || _e === void 0 ? void 0 : _e.call(_d, ",")) !== null && _f !== void 0 ? _f : "",
album: (_h = (_g = _.album) === null || _g === void 0 ? void 0 : _g.albumName) !== null && _h !== void 0 ? _h : "",
copyrightId: _.copyrightId,
singerId: (_k = (_j = _.singers) === null || _j === void 0 ? void 0 : _j[0]) === null || _k === void 0 ? void 0 : _k.id,
});
}),
};
}
async function importMusicSheet(urlLike) {
var _a, _b, _c, _d;
let id;
if (!id) {
id = (urlLike.match(/https?:\/\/music\.migu\.cn\/v3\/(?:my|music)\/playlist\/([0-9]+)/) || [])[1];
}
if (!id) {
id = (urlLike.match(/https?:\/\/h5\.nf\.migu\.cn\/app\/v4\/p\/share\/playlist\/index.html\?.*id=([0-9]+)/) || [])[1];
}
if (!id) {
id = (_a = urlLike.match(/^\s*(\d+)\s*$/)) === null || _a === void 0 ? void 0 : _a[1];
}
if (!id) {
const tempUrl = (_b = urlLike.match(/(https?:\/\/c\.migu\.cn\/[\S]+)\?/)) === null || _b === void 0 ? void 0 : _b[1];
if (tempUrl) {
const request = (await axios_1.default.get(tempUrl, {
headers: {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36 Edg/109.0.1518.61",
Accept: "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",
host: "c.migu.cn",
},
validateStatus(status) {
return (status >= 200 && status < 300) || status === 403;
},
})).request;
const realpath = (_c = request === null || request === void 0 ? void 0 : request.path) !== null && _c !== void 0 ? _c : request === null || request === void 0 ? void 0 : request.responseURL;
if (realpath) {
id = (_d = realpath.match(/id=(\d+)/)) === null || _d === void 0 ? void 0 : _d[1];
}
}
}
if (!id) {
return;
}
const headers = {
host: "m.music.migu.cn",
"Sec-Fetch-Dest": "empty",
"Sec-Fetch-Mode": "cors",
"Sec-Fetch-Site": "same-origin",
"User-Agent": "Mozilla/5.0 (Linux; Android 6.0.1; Moto G (4)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.114 Mobile Safari/537.36 Edg/89.0.774.68",
"X-Requested-With": "XMLHttpRequest",
Referer: "https://m.music.migu.cn",
};
const res = (await axios_1.default.get(`https://m.music.migu.cn/migu/remoting/query_playlist_by_id_tag?onLine=1&queryChannel=0&createUserId=migu&contentCountMin=5&playListId=${id}`, {
headers,
})).data;
const contentCount = parseInt(res.rsp.playList[0].contentCount);
const cids = [];
let pageNo = 1;
while ((pageNo - 1) * 20 < contentCount) {
const listPage = (await axios_1.default.get(`https://music.migu.cn/v3/music/playlist/${id}?page=${pageNo}`)).data;
const $ = (0, cheerio_1.load)(listPage);
$(".row.J_CopySong").each((i, v) => {
cids.push($(v).attr("data-cid"));
});
pageNo += 1;
}
if (cids.length === 0) {
return;
}
const songs = (await (0, axios_1.default)({
url: `https://music.migu.cn/v3/api/music/audioPlayer/songs?type=1&copyrightId=${cids.join(",")}`,
headers: {
referer: "http://m.music.migu.cn/v3",
},
xsrfCookieName: "XSRF-TOKEN",
withCredentials: true,
})).data;
return songs.items
.filter((_) => _.vipFlag === 0)
.map((_) => {
var _a, _b, _c, _d, _e, _f;
return ({
id: _.songId,
artwork: _.cover,
title: _.songName,
artist: (_b = (_a = _.singers) === null || _a === void 0 ? void 0 : _a.map((_) => _.artistName)) === null || _b === void 0 ? void 0 : _b.join(", "),
album: (_d = (_c = _.albums) === null || _c === void 0 ? void 0 : _c[0]) === null || _d === void 0 ? void 0 : _d.albumName,
copyrightId: _.copyrightId,
singerId: (_f = (_e = _.singers) === null || _e === void 0 ? void 0 : _e[0]) === null || _f === void 0 ? void 0 : _f.artistId,
});
});
}
async function getTopLists() {
const jianjiao = {
title: "咪咕尖叫榜",
data: [
{
id: "jianjiao_newsong",
title: "尖叫新歌榜",
coverImg: "https://cdnmusic.migu.cn/tycms_picture/20/02/36/20020512065402_360x360_2997.png",
},
{
id: "jianjiao_hotsong",
title: "尖叫热歌榜",
coverImg: "https://cdnmusic.migu.cn/tycms_picture/20/04/99/200408163640868_360x360_6587.png",
},
{
id: "jianjiao_original",
title: "尖叫原创榜",
coverImg: "https://cdnmusic.migu.cn/tycms_picture/20/04/99/200408163702795_360x360_1614.png",
},
],
};
const tese = {
title: "咪咕特色榜",
data: [
{
id: "movies",
title: "影视榜",
coverImg: "https://cdnmusic.migu.cn/tycms_picture/20/05/136/200515161848938_360x360_673.png",
},
{
id: "mainland",
title: "内地榜",
coverImg: "https://cdnmusic.migu.cn/tycms_picture/20/08/231/200818095104122_327x327_4971.png",
},
{
id: "hktw",
title: "港台榜",
coverImg: "https://cdnmusic.migu.cn/tycms_picture/20/08/231/200818095125191_327x327_2382.png",
},
{
id: "eur_usa",
title: "欧美榜",
coverImg: "https://cdnmusic.migu.cn/tycms_picture/20/08/231/200818095229556_327x327_1383.png",
},
{
id: "jpn_kor",
title: "日韩榜",
coverImg: "https://cdnmusic.migu.cn/tycms_picture/20/08/231/200818095259569_327x327_4628.png",
},
{
id: "coloring",
title: "彩铃榜",
coverImg: "https://cdnmusic.migu.cn/tycms_picture/20/08/231/200818095356693_327x327_7955.png",
},
{
id: "ktv",
title: "KTV榜",
coverImg: "https://cdnmusic.migu.cn/tycms_picture/20/08/231/200818095414420_327x327_4992.png",
},
{
id: "network",
title: "网络榜",
coverImg: "https://cdnmusic.migu.cn/tycms_picture/20/08/231/200818095442606_327x327_1298.png",
},
],
};
return [jianjiao, tese];
}
const UA = "Mozilla/5.0 (Linux; Android 6.0.1; Moto G (4)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.114 Mobile Safari/537.36 Edg/89.0.774.68";
const By = CryptoJS.MD5(UA).toString();
async function getTopListDetail(topListItem) {
const res = await axios_1.default.get(`https://m.music.migu.cn/migumusic/h5/billboard/home`, {
params: {
pathName: topListItem.id,
pageNum: 1,
pageSize: 100,
},
headers: {
Accept: "*/*",
"Accept-Encoding": "gzip, deflate, br",
Connection: "keep-alive",
Host: "m.music.migu.cn",
referer: `https://m.music.migu.cn/v4/music/top/${topListItem.id}`,
"User-Agent": UA,
By,
},
});
return Object.assign(Object.assign({}, topListItem), {
musicList: res.data.data.songs.items
.map((_) => {
var _a, _b, _c, _d, _e, _f;
return ({
id: _.id,
artwork: ((_a = _.mediumPic) === null || _a === void 0 ? void 0 : _a.startsWith("//"))
? `https:${_.mediumPic}`
: _.mediumPic,
title: _.name,
artist: (_c = (_b = _.singers) === null || _b === void 0 ? void 0 : _b.map((_) => _.name)) === null || _c === void 0 ? void 0 : _c.join(", "),
album: (_d = _.album) === null || _d === void 0 ? void 0 : _d.albumName,
copyrightId: _.copyrightId,
singerId: (_f = (_e = _.singers) === null || _e === void 0 ? void 0 : _e[0]) === null || _f === void 0 ? void 0 : _f.id,
});
})
});
}
async function getRecommendSheetTags() {
const allTags = (await axios_1.default.get("https://m.music.migu.cn/migumusic/h5/playlist/allTag", {
headers: {
host: "m.music.migu.cn",
referer: "https://m.music.migu.cn/v4/music/playlist",
"User-Agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1 Edg/113.0.0.0",
By: "7242bd16f68cd9b39c54a8e61537009f",
},
})).data.data.tags;
const data = allTags.map((_) => {
return {
title: _.tagName,
data: _.tags.map((_) => ({
id: _.tagId,
title: _.tagName,
})),
};
});
return {
pinned: [
{
title: "小清新",
id: "1000587673",
},
{
title: "电视剧",
id: "1001076078",
},
{
title: "民谣",
id: "1000001775",
},
{
title: "旅行",
id: "1000001749",
},
{
title: "思念",
id: "1000001703",
},
],
data,
};
}
async function getRecommendSheetsByTag(sheetItem, page) {
const pageSize = 20;
const res = (await axios_1.default.get("https://m.music.migu.cn/migumusic/h5/playlist/list", {
params: {
columnId: 15127272,
tagId: sheetItem.id,
pageNum: page,
pageSize,
},
headers: {
"user-agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1 Edg/113.0.0.0",
host: "m.music.migu.cn",
By: "7242bd16f68cd9b39c54a8e61537009f",
Referer: "https://m.music.migu.cn/v4/music/playlist",
},
})).data.data;
const isEnd = page * pageSize > res.total;
const data = res.items.map((_) => ({
id: _.playListId,
artist: _.createUserName,
title: _.playListName,
artwork: _.image.startsWith("//") ? `http:${_.image}` : _.image,
playCount: _.playCount,
createUserId: _.createUserId,
}));
return {
isEnd,
data,
};
}
async function getMediaSourceByMTM(musicItem, quality) {
if (quality === "standard" && musicItem.url) {
return {
url: musicItem.url,
};
}
else if (quality === "standard") {
const headers = {
Accept: "application/json, text/javascript, */*; q=0.01",
"Accept-Encoding": "gzip, deflate, br",
"Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6",
Connection: "keep-alive",
"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
Host: "m.music.migu.cn",
Referer: `https://m.music.migu.cn/migu/l/?s=149&p=163&c=5200&j=l&id=${musicItem.copyrightId}`,
"Sec-Fetch-Dest": "empty",
"Sec-Fetch-Mode": "cors",
"Sec-Fetch-Site": "same-origin",
"User-Agent": "Mozilla/5.0 (Linux; Android 6.0.1; Moto G (4)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.114 Mobile Safari/537.36 Edg/89.0.774.68",
"X-Requested-With": "XMLHttpRequest",
};
const result = (await axios_1.default.get("https://m.music.migu.cn/migu/remoting/cms_detail_tag", {
headers,
params: {
cpid: musicItem.copyrightId,
},
})).data.data;
return {
artwork: musicItem.artwork || result.picL,
url: result.listenUrl || result.listenQq || result.lisCr,
};
}
}
const qualityLevels = {
low: "128k",
standard: "320k",
high: "flac",
super: "wav",
};
async function getMediaSource(musicItem, quality) {
const targetQualities = [
qualityLevels[quality] || "320k",
"320k",
"128k",
].filter((item, index, arr) => !!item && arr.indexOf(item) === index);
for (const targetQuality of targetQualities) {
const res = (
await axios_1.default.get(`https://lxmusicapi.onrender.com/url/mg/${musicItem.id}/${targetQuality}`, {
headers: {
"X-Request-Key": "share-v3"
},
})
).data;
if (res === null || res === void 0 ? void 0 : res.url) {
return {
url: res.url,
};
}
}
return null;
}
module.exports = {
platform: "小蜜音乐",
author: "Huibq",
version: "0.3.0",
appVersion: ">0.1.0-alpha.0",
hints: {
importMusicSheet: [
"咪咕APP:自建歌单-分享-复制链接,直接粘贴即可",
"H5/PC端:复制URL并粘贴,或者直接输入纯数字歌单ID即可",
"导入时间和歌单大小有关,请耐心等待",
],
},
primaryKey: ["id", "copyrightId"],
cacheControl: "cache",
srcUrl: "https://fastly.jsdelivr.net/gh/Huibq/keep-alive/Music_Free/xiaomi.js",
supportedSearchType: ["music", "album", "sheet", "artist", "lyric"],
getMediaSource,
async search(query, page, type) {
if (type === "music") {
return await searchMusic(query, page);
}
if (type === "album") {
return await searchAlbum(query, page);
}
if (type === "artist") {
return await searchArtist(query, page);
}
if (type === "sheet") {
return await searchMusicSheet(query, page);
}
if (type === "lyric") {
return await searchLyric(query, page);
}
},
async getAlbumInfo(albumItem) {
const headers = {
Accept: "application/json, text/javascript, */*; q=0.01",
"Accept-Encoding": "gzip, deflate, br",
"Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6",
Connection: "keep-alive",
"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
Host: "m.music.migu.cn",
Referer: `https://m.music.migu.cn/migu/l/?record=record&id=${albumItem.id}`,
"Sec-Fetch-Dest": "empty",
"Sec-Fetch-Mode": "cors",
"Sec-Fetch-Site": "same-origin",
"User-Agent": "Mozilla/5.0 (Linux; Android 6.0.1; Moto G (4)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.114 Mobile Safari/537.36 Edg/89.0.774.68",
"X-Requested-With": "XMLHttpRequest",
};
const musicList = (await axios_1.default.get("https://m.music.migu.cn/migu/remoting/cms_album_song_list_tag", {
headers,
params: {
albumId: albumItem.id,
pageSize: 30,
},
})).data || {};
const albumDesc = (await axios_1.default.get("https://m.music.migu.cn/migu/remoting/cms_album_detail_tag", {
headers,
params: {
albumId: albumItem.id,
},
})).data || {};
return {
albumItem: { description: albumDesc.albumIntro },
musicList: musicList.result.results
.map((_) => ({
id: _.songId,
artwork: _.picL,
title: _.songName,
artist: (_.singerName || []).join(", "),
album: albumItem.title,
url: musicCanPlayFilter(_),
rawLrc: _.lyricLrc,
copyrightId: _.copyrightId,
singerId: _.singerId,
})),
};
},
getArtistWorks: getArtistWorks,
getLyric: getLyric,
importMusicSheet,
getTopLists,
getTopListDetail,
getRecommendSheetTags,
getRecommendSheetsByTag,
getMusicSheetInfo,
};
@@ -0,0 +1,507 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const axios_1 = require("axios");
const CryptoJs = require("crypto-js");
const he = require("he");
const pageSize = 20;
function formatMusicItem(_) {
var _a, _b, _c;
const albumid = _.albumid || ((_a = _.album) === null || _a === void 0 ? void 0 : _a.id);
const albummid = _.albummid || ((_b = _.album) === null || _b === void 0 ? void 0 : _b.mid);
const albumname = _.albumname || ((_c = _.album) === null || _c === void 0 ? void 0 : _c.title);
return {
id: _.id || _.songid,
songmid: _.mid || _.songmid,
title: _.title || _.songname,
artist: _.singer.map((s) => s.name).join(", "),
artwork: albummid
? `https://y.gtimg.cn/music/photo_new/T002R800x800M000${albummid}.jpg`
: undefined,
album: albumname,
lrc: _.lyric || undefined,
albumid: albumid,
albummid: albummid,
};
}
function formatAlbumItem(_) {
return {
id: _.albumID || _.albumid,
albumMID: _.albumMID || _.album_mid,
title: _.albumName || _.album_name,
artwork: _.albumPic ||
`https://y.gtimg.cn/music/photo_new/T002R800x800M000${_.albumMID || _.album_mid}.jpg`,
date: _.publicTime || _.pub_time,
singerID: _.singerID || _.singer_id,
artist: _.singerName || _.singer_name,
singerMID: _.singerMID || _.singer_mid,
description: _.desc,
};
}
function formatArtistItem(_) {
return {
name: _.singerName,
id: _.singerID,
singerMID: _.singerMID,
avatar: _.singerPic,
worksNum: _.songNum,
};
}
const searchTypeMap = {
0: "song",
2: "album",
1: "singer",
3: "songlist",
7: "song",
12: "mv",
};
const headers = {
referer: "https://y.qq.com",
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.0.0 Safari/537.36",
Cookie: "uin=",
};
async function searchBase(query, page, type) {
const res = (await (0, axios_1.default)({
url: "https://u.y.qq.com/cgi-bin/musicu.fcg",
method: "POST",
data: {
req_1: {
method: "DoSearchForQQMusicDesktop",
module: "music.search.SearchCgiService",
param: {
num_per_page: pageSize,
page_num: page,
query: query,
search_type: type,
},
},
},
headers: headers,
xsrfCookieName: "XSRF-TOKEN",
withCredentials: true,
})).data;
return {
isEnd: res.req_1.data.meta.sum <= page * pageSize,
data: res.req_1.data.body[searchTypeMap[type]].list,
};
}
async function searchMusic(query, page) {
const songs = await searchBase(query, page, 0);
return {
isEnd: songs.isEnd,
data: songs.data.map(formatMusicItem),
};
}
async function searchAlbum(query, page) {
const albums = await searchBase(query, page, 2);
return {
isEnd: albums.isEnd,
data: albums.data.map(formatAlbumItem),
};
}
async function searchArtist(query, page) {
const artists = await searchBase(query, page, 1);
return {
isEnd: artists.isEnd,
data: artists.data.map(formatArtistItem),
};
}
async function searchMusicSheet(query, page) {
const musicSheet = await searchBase(query, page, 3);
return {
isEnd: musicSheet.isEnd,
data: musicSheet.data.map((item) => ({
title: item.dissname,
createAt: item.createtime,
description: item.introduction,
playCount: item.listennum,
worksNums: item.song_count,
artwork: item.imgurl,
id: item.dissid,
artist: item.creator.name,
})),
};
}
async function searchLyric(query, page) {
const songs = await searchBase(query, page, 7);
return {
isEnd: songs.isEnd,
data: songs.data.map((it) => (Object.assign(Object.assign({}, formatMusicItem(it)), { rawLrcTxt: it.content }))),
};
}
function getQueryFromUrl(key, search) {
try {
const sArr = search.split("?");
let s = "";
if (sArr.length > 1) {
s = sArr[1];
}
else {
return key ? undefined : {};
}
const querys = s.split("&");
const result = {};
querys.forEach((item) => {
const temp = item.split("=");
result[temp[0]] = decodeURIComponent(temp[1]);
});
return key ? result[key] : result;
}
catch (err) {
return key ? "" : {};
}
}
function changeUrlQuery(obj, baseUrl) {
const query = getQueryFromUrl(null, baseUrl);
let url = baseUrl.split("?")[0];
const newQuery = Object.assign(Object.assign({}, query), obj);
let queryArr = [];
Object.keys(newQuery).forEach((key) => {
if (newQuery[key] !== undefined && newQuery[key] !== "") {
queryArr.push(`${key}=${encodeURIComponent(newQuery[key])}`);
}
});
return `${url}?${queryArr.join("&")}`.replace(/\?$/, "");
}
const typeMap = {
m4a: {
s: "C400",
e: ".m4a",
},
128: {
s: "M500",
e: ".mp3",
},
320: {
s: "M800",
e: ".mp3",
},
ape: {
s: "A000",
e: ".ape",
},
flac: {
s: "F000",
e: ".flac",
},
};
async function getAlbumInfo(albumItem) {
const url = changeUrlQuery({
data: JSON.stringify({
comm: {
ct: 24,
cv: 10000,
},
albumSonglist: {
method: "GetAlbumSongList",
param: {
albumMid: albumItem.albumMID,
albumID: 0,
begin: 0,
num: 999,
order: 2,
},
module: "music.musichallAlbum.AlbumSongList",
},
}),
}, "https://u.y.qq.com/cgi-bin/musicu.fcg?g_tk=5381&format=json&inCharset=utf8&outCharset=utf-8");
const res = (await (0, axios_1.default)({
url: url,
headers: headers,
xsrfCookieName: "XSRF-TOKEN",
withCredentials: true,
})).data;
return {
musicList: res.albumSonglist.data.songList
.map((item) => {
const _ = item.songInfo;
return formatMusicItem(_);
}),
};
}
async function getArtistSongs(artistItem, page) {
const url = changeUrlQuery({
data: JSON.stringify({
comm: {
ct: 24,
cv: 0,
},
singer: {
method: "get_singer_detail_info",
param: {
sort: 5,
singermid: artistItem.singerMID,
sin: (page - 1) * pageSize,
num: pageSize,
},
module: "music.web_singer_info_svr",
},
}),
}, "http://u.y.qq.com/cgi-bin/musicu.fcg");
const res = (await (0, axios_1.default)({
url: url,
method: "get",
headers: headers,
xsrfCookieName: "XSRF-TOKEN",
withCredentials: true,
})).data;
return {
isEnd: res.singer.data.total_song <= page * pageSize,
data: res.singer.data.songlist.map(formatMusicItem),
};
}
async function getArtistAlbums(artistItem, page) {
const url = changeUrlQuery({
data: JSON.stringify({
comm: {
ct: 24,
cv: 0,
},
singerAlbum: {
method: "get_singer_album",
param: {
singermid: artistItem.singerMID,
order: "time",
begin: (page - 1) * pageSize,
num: pageSize / 1,
exstatus: 1,
},
module: "music.web_singer_info_svr",
},
}),
}, "http://u.y.qq.com/cgi-bin/musicu.fcg");
const res = (await (0, axios_1.default)({
url,
method: "get",
headers: headers,
xsrfCookieName: "XSRF-TOKEN",
withCredentials: true,
})).data;
return {
isEnd: res.singerAlbum.data.total <= page * pageSize,
data: res.singerAlbum.data.list.map(formatAlbumItem),
};
}
async function getArtistWorks(artistItem, page, type) {
if (type === "music") {
return getArtistSongs(artistItem, page);
}
if (type === "album") {
return getArtistAlbums(artistItem, page);
}
}
async function getLyric(musicItem) {
const result = (await (0, axios_1.default)({
url: `http://c.y.qq.com/lyric/fcgi-bin/fcg_query_lyric_new.fcg?songmid=${musicItem.songmid}&pcachetime=${new Date().getTime()}&g_tk=5381&loginUin=0&hostUin=0&inCharset=utf8&outCharset=utf-8&notice=0&platform=yqq&needNewCode=0`,
headers: { Referer: "https://y.qq.com", Cookie: "uin=" },
method: "get",
xsrfCookieName: "XSRF-TOKEN",
withCredentials: true,
})).data;
const res = JSON.parse(result.replace(/callback\(|MusicJsonCallback\(|jsonCallback\(|\)$/g, ""));
let translation;
if (res.trans) {
translation = he.decode(CryptoJs.enc.Base64.parse(res.trans).toString(CryptoJs.enc.Utf8));
}
return {
rawLrc: he.decode(CryptoJs.enc.Base64.parse(res.lyric).toString(CryptoJs.enc.Utf8)),
translation,
};
}
async function importMusicSheet(urlLike) {
let id;
if (!id) {
id = (urlLike.match(/https?:\/\/i\.y\.qq\.com\/n2\/m\/share\/details\/taoge\.html\?.*id=([0-9]+)/) || [])[1];
}
if (!id) {
id = (urlLike.match(/https?:\/\/y\.qq\.com\/n\/ryqq\/playlist\/([0-9]+)/) ||
[])[1];
}
if (!id) {
id = (urlLike.match(/^(\d+)$/) || [])[1];
}
if (!id) {
return;
}
const result = (await (0, axios_1.default)({
url: `http://i.y.qq.com/qzone/fcg-bin/fcg_ucc_getcdinfo_byids_cp.fcg?type=1&utf8=1&disstid=${id}&loginUin=0`,
headers: { Referer: "https://y.qq.com/n/yqq/playlist", Cookie: "uin=" },
method: "get",
xsrfCookieName: "XSRF-TOKEN",
withCredentials: true,
})).data;
const res = JSON.parse(result.replace(/callback\(|MusicJsonCallback\(|jsonCallback\(|\)$/g, ""));
return res.cdlist[0].songlist.map(formatMusicItem);
}
async function getTopLists() {
const list = await (0, axios_1.default)({
url: "https://u.y.qq.com/cgi-bin/musicu.fcg?_=1577086820633&data=%7B%22comm%22%3A%7B%22g_tk%22%3A5381%2C%22uin%22%3A123456%2C%22format%22%3A%22json%22%2C%22inCharset%22%3A%22utf-8%22%2C%22outCharset%22%3A%22utf-8%22%2C%22notice%22%3A0%2C%22platform%22%3A%22h5%22%2C%22needNewCode%22%3A1%2C%22ct%22%3A23%2C%22cv%22%3A0%7D%2C%22topList%22%3A%7B%22module%22%3A%22musicToplist.ToplistInfoServer%22%2C%22method%22%3A%22GetAll%22%2C%22param%22%3A%7B%7D%7D%7D",
method: "get",
headers: {
Cookie: "uin=",
},
xsrfCookieName: "XSRF-TOKEN",
withCredentials: true,
});
return list.data.topList.data.group.map((e) => ({
title: e.groupName,
data: e.toplist.map((_) => ({
id: _.topId,
description: _.intro,
title: _.title,
period: _.period,
coverImg: _.headPicUrl || _.frontPicUrl,
})),
}));
}
async function getTopListDetail(topListItem) {
var _a;
const res = await (0, axios_1.default)({
url: `https://u.y.qq.com/cgi-bin/musicu.fcg?g_tk=5381&data=%7B%22detail%22%3A%7B%22module%22%3A%22musicToplist.ToplistInfoServer%22%2C%22method%22%3A%22GetDetail%22%2C%22param%22%3A%7B%22topId%22%3A${topListItem.id}%2C%22offset%22%3A0%2C%22num%22%3A100%2C%22period%22%3A%22${(_a = topListItem.period) !== null && _a !== void 0 ? _a : ""}%22%7D%7D%2C%22comm%22%3A%7B%22ct%22%3A24%2C%22cv%22%3A0%7D%7D`,
method: "get",
headers: {
Cookie: "uin=",
},
xsrfCookieName: "XSRF-TOKEN",
withCredentials: true,
});
return Object.assign(Object.assign({}, topListItem), {
musicList: res.data.detail.data.songInfoList
.map(formatMusicItem)
});
}
async function getRecommendSheetTags() {
const res = (await axios_1.default.get("https://c.y.qq.com/splcloud/fcgi-bin/fcg_get_diss_tag_conf.fcg?format=json&inCharset=utf8&outCharset=utf-8", {
headers: {
referer: "https://y.qq.com/",
},
})).data.data.categories;
const data = res.slice(1).map((_) => ({
title: _.categoryGroupName,
data: _.items.map((tag) => ({
id: tag.categoryId,
title: tag.categoryName,
})),
}));
const pinned = [];
for (let d of data) {
if (d.data.length) {
pinned.push(d.data[0]);
}
}
return {
pinned,
data,
};
}
async function getRecommendSheetsByTag(tag, page) {
const pageSize = 20;
const rawRes = (await axios_1.default.get("https://c.y.qq.com/splcloud/fcgi-bin/fcg_get_diss_by_tag.fcg", {
headers: {
referer: "https://y.qq.com/",
},
params: {
inCharset: "utf8",
outCharset: "utf-8",
sortId: 5,
categoryId: (tag === null || tag === void 0 ? void 0 : tag.id) || "10000000",
sin: pageSize * (page - 1),
ein: page * pageSize - 1,
},
})).data;
const res = JSON.parse(rawRes.replace(/callback\(|MusicJsonCallback\(|jsonCallback\(|\)$/g, "")).data;
const isEnd = res.sum <= page * pageSize;
const data = res.list.map((item) => {
var _a, _b;
return ({
id: item.dissid,
createTime: item.createTime,
title: item.dissname,
artwork: item.imgurl,
description: item.introduction,
playCount: item.listennum,
artist: (_b = (_a = item.creator) === null || _a === void 0 ? void 0 : _a.name) !== null && _b !== void 0 ? _b : "",
});
});
return {
isEnd,
data,
};
}
async function getMusicSheetInfo(sheet, page) {
const data = await importMusicSheet(sheet.id);
return {
isEnd: true,
musicList: data,
};
}
const qualityLevels = {
low: "128k",
standard: "320k",
high: "flac",
super: "wav",
};
async function getMediaSource(musicItem, quality) {
const targetQualities = [
qualityLevels[quality] || "320k",
"320k",
"128k",
].filter((item, index, arr) => !!item && arr.indexOf(item) === index);
for (const targetQuality of targetQualities) {
const res = (
await axios_1.default.get(`https://lxmusicapi.onrender.com/url/tx/${musicItem.songmid}/${targetQuality}`, {
headers: {
"X-Request-Key": "share-v3"
},
})
).data;
if (res === null || res === void 0 ? void 0 : res.url) {
return {
url: res.url,
};
}
}
return null;
}
module.exports = {
platform: "小秋音乐",
author: "Huibq",
version: "0.3.0",
srcUrl: "https://fastly.jsdelivr.net/gh/Huibq/keep-alive/Music_Free/xiaoqiu.js",
cacheControl: "no-cache",
hints: {
importMusicSheet: [
"QQ音乐APP:自建歌单-分享-分享到微信好友/QQ好友;然后点开并复制链接,直接粘贴即可",
"H5:复制URL并粘贴,或者直接输入纯数字歌单ID即可",
"导入时间和歌单大小有关,请耐心等待",
],
},
primaryKey: ["id", "songmid"],
supportedSearchType: ["music", "album", "sheet", "artist", "lyric"],
async search(query, page, type) {
if (type === "music") {
return await searchMusic(query, page);
}
if (type === "album") {
return await searchAlbum(query, page);
}
if (type === "artist") {
return await searchArtist(query, page);
}
if (type === "sheet") {
return await searchMusicSheet(query, page);
}
if (type === "lyric") {
return await searchLyric(query, page);
}
},
getMediaSource,
getLyric,
getAlbumInfo,
getArtistWorks,
importMusicSheet,
getTopLists,
getTopListDetail,
getRecommendSheetTags,
getRecommendSheetsByTag,
getMusicSheetInfo,
};
@@ -0,0 +1,555 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const axios_1 = require("axios");
const he = require("he");
const pageSize = 30;
function artworkShort2Long(albumpicShort) {
var _a;
const firstSlashOfAlbum = (_a = albumpicShort === null || albumpicShort === void 0 ? void 0 : albumpicShort.indexOf("/")) !== null && _a !== void 0 ? _a : -1;
return firstSlashOfAlbum !== -1
? `https://img4.kuwo.cn/star/albumcover/1080${albumpicShort.slice(firstSlashOfAlbum)}`
: undefined;
}
function formatMusicItem(_) {
return {
id: _.MUSICRID.replace("MUSIC_", ""),
artwork: artworkShort2Long(_.web_albumpic_short),
title: he.decode(_.NAME || ""),
artist: he.decode(_.ARTIST || ""),
album: he.decode(_.ALBUM || ""),
albumId: _.ALBUMID,
artistId: _.ARTISTID,
formats: _.FORMATS,
};
}
function formatAlbumItem(_) {
var _a;
return {
id: _.albumid,
artist: he.decode(_.artist || ""),
title: he.decode(_.name || ""),
artwork: (_a = _.img) !== null && _a !== void 0 ? _a : artworkShort2Long(_.pic),
description: he.decode(_.info || ""),
date: _.pub,
artistId: _.artistid,
};
}
function formatArtistItem(_) {
return {
id: _.ARTISTID,
avatar: _.hts_PICPATH,
name: he.decode(_.ARTIST || ""),
artistId: _.ARTISTID,
description: he.decode(_.desc || ""),
worksNum: _.SONGNUM
};
}
function formatMusicSheet(_) {
return {
id: _.playlistid,
title: he.decode(_.name || ""),
artist: he.decode(_.nickname || ""),
artwork: _.pic,
playCount: _.playcnt,
description: he.decode(_.intro || ""),
worksNum: _.songnum,
};
}
async function searchMusic(query, page) {
const res = (await (0, axios_1.default)({
method: "get",
url: `http://search.kuwo.cn/r.s`,
params: {
client: "kt",
all: query,
pn: page - 1,
rn: pageSize,
uid: 2574109560,
ver: "kwplayer_ar_8.5.4.2",
vipver: 1,
ft: "music",
cluster: 0,
strategy: 2012,
encoding: "utf8",
rformat: "json",
vermerge: 1,
mobi: 1,
},
})).data;
const songs = res.abslist.map(formatMusicItem);
return {
isEnd: (+res.PN + 1) * +res.RN >= +res.TOTAL,
data: songs,
};
}
async function searchAlbum(query, page) {
const res = (await (0, axios_1.default)({
method: "get",
url: `http://search.kuwo.cn/r.s`,
params: {
all: query,
ft: "album",
itemset: "web_2013",
client: "kt",
pn: page - 1,
rn: pageSize,
rformat: "json",
encoding: "utf8",
pcjson: 1,
},
})).data;
const albums = res.albumlist.map(formatAlbumItem);
return {
isEnd: (+res.PN + 1) * +res.RN >= +res.TOTAL,
data: albums,
};
}
async function searchArtist(query, page) {
const res = (await (0, axios_1.default)({
method: "get",
url: `http://search.kuwo.cn/r.s`,
params: {
all: query,
ft: "artist",
itemset: "web_2013",
client: "kt",
pn: page - 1,
rn: pageSize,
rformat: "json",
encoding: "utf8",
pcjson: 1,
},
})).data;
const artists = res.abslist.map(formatArtistItem);
return {
isEnd: (+res.PN + 1) * +res.RN >= +res.TOTAL,
data: artists,
};
}
async function searchMusicSheet(query, page) {
const res = (await (0, axios_1.default)({
method: "get",
url: `http://search.kuwo.cn/r.s`,
params: {
all: query,
ft: "playlist",
itemset: "web_2013",
client: "kt",
pn: page - 1,
rn: pageSize,
rformat: "json",
encoding: "utf8",
pcjson: 1,
},
})).data;
const musicSheets = res.abslist.map(formatMusicSheet);
return {
isEnd: (+res.PN + 1) * +res.RN >= +res.TOTAL,
data: musicSheets,
};
}
async function getArtistMusicWorks(artistItem, page) {
const res = (await (0, axios_1.default)({
method: "get",
url: `http://search.kuwo.cn/r.s`,
params: {
pn: page - 1,
rn: pageSize,
artistid: artistItem.id,
stype: "artist2music",
sortby: 0,
alflac: 1,
show_copyright_off: 1,
pcmp4: 1,
encoding: "utf8",
plat: "pc",
thost: "search.kuwo.cn",
vipver: "MUSIC_9.1.1.2_BCS2",
devid: "38668888",
newver: 1,
pcjson: 1,
},
})).data;
const songs = res.musiclist.map((_) => {
return {
id: _.musicrid,
artwork: artworkShort2Long(_.web_albumpic_short),
title: he.decode(_.name || ""),
artist: he.decode(_.artist || ""),
album: he.decode(_.album || ""),
albumId: _.albumid,
artistId: _.artistid,
formats: _.formats,
};
});
return {
isEnd: (+res.pn + 1) * pageSize >= +res.total,
data: songs,
};
}
async function getArtistAlbumWorks(artistItem, page) {
const res = (await (0, axios_1.default)({
method: "get",
url: `http://search.kuwo.cn/r.s`,
params: {
pn: page - 1,
rn: pageSize,
artistid: artistItem.id,
stype: "albumlist",
sortby: 1,
alflac: 1,
show_copyright_off: 1,
pcmp4: 1,
encoding: "utf8",
plat: "pc",
thost: "search.kuwo.cn",
vipver: "MUSIC_9.1.1.2_BCS2",
devid: "38668888",
newver: 1,
pcjson: 1,
},
})).data;
const albums = res.albumlist.map(formatAlbumItem);
return {
isEnd: (+res.pn + 1) * pageSize >= +res.total,
data: albums,
};
}
async function getArtistWorks(artistItem, page, type) {
if (type === "music") {
return getArtistMusicWorks(artistItem, page);
}
else if (type === "album") {
return getArtistAlbumWorks(artistItem, page);
}
}
async function getLyric(musicItem) {
const res = (await axios_1.default.get("http://m.kuwo.cn/newh5/singles/songinfoandlrc", {
params: {
musicId: musicItem.id,
httpStatus: 1,
},
})).data;
const list = res.data.lrclist;
return {
rawLrc: list.map((_) => `[${_.time}]${_.lineLyric}`).join("\n"),
};
}
async function getAlbumInfo(albumItem) {
const res = (await (0, axios_1.default)({
method: "get",
url: `http://search.kuwo.cn/r.s`,
params: {
pn: 0,
rn: 100,
albumid: albumItem.id,
stype: "albuminfo",
sortby: 0,
alflac: 1,
show_copyright_off: 1,
pcmp4: 1,
encoding: "utf8",
plat: "pc",
thost: "search.kuwo.cn",
vipver: "MUSIC_9.1.1.2_BCS2",
devid: "38668888",
newver: 1,
pcjson: 1,
},
})).data;
const songs = res.musiclist.map((_) => {
var _a;
return {
id: _.id,
artwork: (_a = albumItem.artwork) !== null && _a !== void 0 ? _a : res.img,
title: he.decode(_.name || ""),
artist: he.decode(_.artist || ""),
album: he.decode(_.album || ""),
albumId: albumItem.id,
artistId: _.artistid,
formats: _.formats,
};
});
return {
musicList: songs,
};
}
async function getTopLists() {
const result = (await axios_1.default.get("http://wapi.kuwo.cn/api/pc/bang/list")).data
.child;
return result.map((e) => ({
title: e.disname,
data: e.child.map((_) => {
var _a, _b;
return ({
id: _.sourceid,
coverImg: (_b = (_a = _.pic5) !== null && _a !== void 0 ? _a : _.pic2) !== null && _b !== void 0 ? _b : _.pic,
title: _.name,
description: _.intro,
});
}),
}));
}
async function getTopListDetail(topListItem) {
const res = await axios_1.default.get(`http://kbangserver.kuwo.cn/ksong.s`, {
params: {
from: "pc",
fmt: "json",
pn: 0,
rn: 80,
type: "bang",
data: "content",
id: topListItem.id,
show_copyright_off: 0,
pcmp4: 1,
isbang: 1,
userid: 0,
httpStatus: 1,
},
});
return Object.assign(Object.assign({}, topListItem), {
musicList: res.data.musiclist.map((_) => {
return {
id: _.id,
title: he.decode(_.name || ""),
artist: he.decode(_.artist || ""),
album: he.decode(_.album || ""),
albumId: _.albumid,
artistId: _.artistid,
formats: _.formats,
};
})
});
}
async function getMusicSheetResponseById(id, page, pagesize = 50) {
return (await axios_1.default.get(`http://nplserver.kuwo.cn/pl.svc`, {
params: {
op: "getlistinfo",
pid: id,
pn: page - 1,
rn: pagesize,
encode: "utf8",
keyset: "pl2012",
vipver: "MUSIC_9.1.1.2_BCS2",
newver: 1,
},
})).data;
}
async function importMusicSheet(urlLike) {
var _a, _b;
let id;
if (!id) {
id = (_a = urlLike.match(/https?:\/\/www\/kuwo\.cn\/playlist_detail\/(\d+)/)) === null || _a === void 0 ? void 0 : _a[1];
}
if (!id) {
id = (_b = urlLike.match(/https?:\/\/m\.kuwo\.cn\/h5app\/playlist\/(\d+)/)) === null || _b === void 0 ? void 0 : _b[1];
}
if (!id) {
id = urlLike.match(/^\s*(\d+)\s*$/);
}
if (!id) {
return;
}
let page = 1;
let totalPage = 30;
let musicList = [];
while (page < totalPage) {
try {
const data = await getMusicSheetResponseById(id, page, 80);
totalPage = Math.ceil(data.total / 80);
if (isNaN(totalPage)) {
totalPage = 1;
}
musicList = musicList.concat(data.musicList.map((_) => ({
id: _.id,
title: he.decode(_.name || ""),
artist: he.decode(_.artist || ""),
album: he.decode(_.album || ""),
albumId: _.albumid,
artistId: _.artistid,
formats: _.formats,
})));
}
catch (_c) { }
await new Promise((resolve) => {
setTimeout(() => {
resolve();
}, 200 + Math.random() * 100);
});
++page;
}
return musicList;
}
async function getRecommendSheetTags() {
const res = (await axios_1.default.get(`http://wapi.kuwo.cn/api/pc/classify/playlist/getTagList?cmd=rcm_keyword_playlist&user=0&prod=kwplayer_pc_9.0.5.0&vipver=9.0.5.0&source=kwplayer_pc_9.0.5.0&loginUid=0&loginSid=0&appUid=76039576`)).data.data;
const data = res
.map((group) => ({
title: group.name,
data: group.data.map((_) => ({
id: _.id,
digest: _.digest,
title: _.name,
})),
}))
.filter((item) => item.data.length);
const pinned = [
{
id: "1848",
title: "翻唱",
digest: "10000",
},
{
id: "621",
title: "网络",
digest: "10000",
},
{
title: "伤感",
digest: "10000",
id: "146",
},
{
title: "欧美",
digest: "10000",
id: "35",
},
];
return {
data,
pinned,
};
}
async function getRecommendSheetsByTag(tag, page) {
const pageSize = 20;
let res;
if (tag.id) {
if (tag.digest === "10000") {
res = (await axios_1.default.get(`http://wapi.kuwo.cn/api/pc/classify/playlist/getTagPlayList?loginUid=0&loginSid=0&appUid=76039576&pn=${page - 1}&id=${tag.id}&rn=${pageSize}`)).data.data;
}
else {
let digest43Result = (await axios_1.default.get(`http://mobileinterfaces.kuwo.cn/er.s?type=get_pc_qz_data&f=web&id=${tag.id}&prod=pc`)).data;
res = {
total: 0,
data: digest43Result.reduce((prev, curr) => [...prev, ...curr.list]),
};
}
}
else {
res = (await axios_1.default.get(`https://wapi.kuwo.cn/api/pc/classify/playlist/getRcmPlayList?loginUid=0&loginSid=0&appUid=76039576&&pn=${page - 1}&rn=${pageSize}&order=hot`)).data.data;
}
const isEnd = page * pageSize >= res.total;
return {
isEnd,
data: res.data.map((_) => ({
title: _.name,
artist: _.uname,
id: _.id,
artwork: _.img,
playCount: _.listencnt,
createUserId: _.uid,
})),
};
}
async function getMusicSheetInfo(sheet, page) {
const res = await getMusicSheetResponseById(sheet.id, page, pageSize);
return {
isEnd: page * pageSize >= res.total,
musicList: res.musiclist.map((_) => ({
id: _.id,
title: he.decode(_.name || ""),
artist: he.decode(_.artist || ""),
album: he.decode(_.album || ""),
albumId: _.albumid,
artistId: _.artistid,
formats: _.formats,
})),
};
}
const qualityLevels = {
low: "128k",
standard: "320k",
high: "flac",
super: "wav",
};
async function getMediaSource(musicItem, quality) {
const targetQualities = [
qualityLevels[quality] || "320k",
"320k",
"128k",
].filter((item, index, arr) => !!item && arr.indexOf(item) === index);
for (const targetQuality of targetQualities) {
const res = (
await axios_1.default.get(`https://lxmusicapi.onrender.com/url/kw/${musicItem.id}/${targetQuality}`, {
headers: {
"X-Request-Key": "share-v3"
},
})
).data;
if (res === null || res === void 0 ? void 0 : res.url) {
return {
url: res.url,
};
}
}
return null;
}
async function getMusicInfo(musicItem) {
const res = (await axios_1.default.get("http://m.kuwo.cn/newh5/singles/songinfoandlrc", {
params: {
musicId: musicItem.id,
httpStatus: 1,
},
})).data;
const originalUrl = res.data.songinfo.pic;
let picUrl;
if (originalUrl.includes("starheads/")) {
picUrl = originalUrl.replace(/starheads\/\d+/, "starheads/800");
}
else if (originalUrl.includes("albumcover/")) {
picUrl = originalUrl.replace(/albumcover\/\d+/, "albumcover/800");
}
return {
artwork: picUrl,
};
}
module.exports = {
platform: "小蜗音乐",
author: 'Huibq',
version: "0.3.0",
appVersion: ">0.1.0-alpha.0",
srcUrl: "https://fastly.jsdelivr.net/gh/Huibq/keep-alive/Music_Free/xiaowo.js",
cacheControl: "no-cache",
hints: {
importMusicSheet: [
"酷我APP:自建歌单-分享-复制试听链接,直接粘贴即可",
"H5:复制URL并粘贴,或者直接输入纯数字歌单ID即可",
"导入时间和歌单大小有关,请耐心等待",
],
},
supportedSearchType: ["music", "album", "sheet", 'artist'],
async search(query, page, type) {
if (type === "music") {
return await searchMusic(query, page);
}
if (type === "album") {
return await searchAlbum(query, page);
}
if (type === "artist") {
return await searchArtist(query, page);
}
if (type === "sheet") {
return await searchMusicSheet(query, page);
}
},
getMediaSource,
getMusicInfo,
getAlbumInfo,
getLyric,
getArtistWorks,
getTopLists,
getTopListDetail,
importMusicSheet,
getRecommendSheetTags,
getRecommendSheetsByTag,
getMusicSheetInfo,
};
@@ -0,0 +1,566 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const axios_1 = require("axios");
const CryptoJs = require("crypto-js");
const qs = require("qs");
const bigInt = require("big-integer");
const dayjs = require("dayjs");
const cheerio = require("cheerio");
function create_key() {
var d, e, b = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789", c = "";
for (d = 0; d < 16; d += 1)
(e = Math.random() * b.length), (e = Math.floor(e)), (c += b.charAt(e));
return c;
}
function AES(a, b) {
var c = CryptoJs.enc.Utf8.parse(b), d = CryptoJs.enc.Utf8.parse("0102030405060708"), e = CryptoJs.enc.Utf8.parse(a), f = CryptoJs.AES.encrypt(e, c, {
iv: d,
mode: CryptoJs.mode.CBC,
});
return f.toString();
}
function Rsa(text) {
text = text.split("").reverse().join("");
const d = "010001";
const e = "00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7";
const hexText = text
.split("")
.map((_) => _.charCodeAt(0).toString(16))
.join("");
const res = bigInt(hexText, 16)
.modPow(bigInt(d, 16), bigInt(e, 16))
.toString(16);
return Array(256 - res.length)
.fill("0")
.join("")
.concat(res);
}
function getParamsAndEnc(text) {
const first = AES(text, "0CoJUm6Qyw8W8jud");
const rand = create_key();
const params = AES(first, rand);
const encSecKey = Rsa(rand);
return {
params,
encSecKey,
};
}
function formatMusicItem(_) {
var _a, _b, _c, _d;
const album = _.al || _.album;
return {
id: _.id,
artwork: album === null || album === void 0 ? void 0 : album.picUrl,
title: _.name,
artist: (_.ar || _.artists)[0].name,
album: album === null || album === void 0 ? void 0 : album.name,
url: `https://share.duanx.cn/url/wy/${_.id}/128k`,
qualities: {
low: {
size: (_a = (_.l || {})) === null || _a === void 0 ? void 0 : _a.size,
},
standard: {
size: (_b = (_.m || {})) === null || _b === void 0 ? void 0 : _b.size,
},
high: {
size: (_c = (_.h || {})) === null || _c === void 0 ? void 0 : _c.size,
},
super: {
size: (_d = (_.sq || {})) === null || _d === void 0 ? void 0 : _d.size,
},
},
copyrightId: _ === null || _ === void 0 ? void 0 : _.copyrightId
};
}
function formatAlbumItem(_) {
return {
id: _.id,
artist: _.artist.name,
title: _.name,
artwork: _.picUrl,
description: "",
date: dayjs.unix(_.publishTime / 1000).format("YYYY-MM-DD"),
};
}
const pageSize = 30;
async function searchBase(query, page, type) {
const data = {
s: query,
limit: pageSize,
type: type,
offset: (page - 1) * pageSize,
csrf_token: "",
};
const pae = getParamsAndEnc(JSON.stringify(data));
const paeData = qs.stringify(pae);
const headers = {
authority: "music.163.com",
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.135 Safari/537.36",
"content-type": "application/x-www-form-urlencoded",
accept: "*/*",
origin: "https://music.163.com",
"sec-fetch-site": "same-origin",
"sec-fetch-mode": "cors",
"sec-fetch-dest": "empty",
referer: "https://music.163.com/search/",
"accept-language": "zh-CN,zh;q=0.9",
};
const res = (await (0, axios_1.default)({
method: "post",
url: "https://music.163.com/weapi/search/get",
headers,
data: paeData,
})).data;
return res;
}
async function searchMusic(query, page) {
const res = await searchBase(query, page, 1);
const songs = res.result.songs
.map(formatMusicItem);
return {
isEnd: res.result.songCount <= page * pageSize,
data: songs,
};
}
async function searchAlbum(query, page) {
const res = await searchBase(query, page, 10);
const albums = res.result.albums.map(formatAlbumItem);
return {
isEnd: res.result.albumCount <= page * pageSize,
data: albums,
};
}
async function searchArtist(query, page) {
const res = await searchBase(query, page, 100);
const artists = res.result.artists.map((_) => ({
name: _.name,
id: _.id,
avatar: _.img1v1Url,
worksNum: _.albumSize,
}));
return {
isEnd: res.result.artistCount <= page * pageSize,
data: artists,
};
}
async function searchMusicSheet(query, page) {
const res = await searchBase(query, page, 1000);
const playlists = res.result.playlists.map((_) => {
var _a;
return ({
title: _.name,
id: _.id,
coverImg: _.coverImgUrl,
artist: (_a = _.creator) === null || _a === void 0 ? void 0 : _a.nickname,
playCount: _.playCount,
worksNum: _.trackCount,
});
});
return {
isEnd: res.result.playlistCount <= page * pageSize,
data: playlists,
};
}
async function searchLyric(query, page) {
var _a, _b;
const res = await searchBase(query, page, 1006);
const lyrics = (_b = (_a = res.result.songs) === null || _a === void 0 ? void 0 : _a.map((it) => {
var _a, _b, _c, _d;
return ({
title: it.name,
artist: (_a = it.ar) === null || _a === void 0 ? void 0 : _a.map((_) => _.name).join(", "),
id: it.id,
artwork: (_b = it.al) === null || _b === void 0 ? void 0 : _b.picUrl,
album: (_c = it.al) === null || _c === void 0 ? void 0 : _c.name,
rawLrcTxt: (_d = it.lyrics) === null || _d === void 0 ? void 0 : _d.join("\n"),
});
})) !== null && _b !== void 0 ? _b : [];
return {
isEnd: res.result.songCount <= page * pageSize,
data: lyrics,
};
}
async function getArtistWorks(artistItem, page, type) {
const data = {
csrf_token: "",
};
const pae = getParamsAndEnc(JSON.stringify(data));
const paeData = qs.stringify(pae);
const headers = {
authority: "music.163.com",
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.135 Safari/537.36",
"content-type": "application/x-www-form-urlencoded",
accept: "*/*",
origin: "https://music.163.com",
"sec-fetch-site": "same-origin",
"sec-fetch-mode": "cors",
"sec-fetch-dest": "empty",
referer: "https://music.163.com/search/",
"accept-language": "zh-CN,zh;q=0.9",
};
if (type === "music") {
const res = (await (0, axios_1.default)({
method: "post",
url: `https://music.163.com/weapi/v1/artist/${artistItem.id}?csrf_token=`,
headers,
data: paeData,
})).data;
return {
isEnd: true,
data: res.hotSongs.map(formatMusicItem),
};
}
else if (type === "album") {
const res = (await (0, axios_1.default)({
method: "post",
url: `https://music.163.com/weapi/artist/albums/${artistItem.id}?csrf_token=`,
headers,
data: paeData,
})).data;
return {
isEnd: true,
data: res.hotAlbums.map(formatAlbumItem),
};
}
}
async function getTopListDetail(topListItem) {
const musicList = await getSheetMusicById(topListItem.id);
return Object.assign(Object.assign({}, topListItem), { musicList });
}
async function getLyric(musicItem) {
const headers = {
Referer: "https://y.music.163.com/",
Origin: "https://y.music.163.com/",
authority: "music.163.com",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.135 Safari/537.36",
"Content-Type": "application/x-www-form-urlencoded",
};
const data = { id: musicItem.id, lv: -1, tv: -1, csrf_token: "" };
const pae = getParamsAndEnc(JSON.stringify(data));
const paeData = qs.stringify(pae);
const result = (await (0, axios_1.default)({
method: "post",
url: `https://interface.music.163.com/weapi/song/lyric?csrf_token=`,
headers,
data: paeData,
})).data;
return {
rawLrc: result.lrc.lyric,
};
}
async function getMusicInfo(musicItem) {
const headers = {
Referer: "https://y.music.163.com/",
Origin: "https://y.music.163.com/",
authority: "music.163.com",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.135 Safari/537.36",
"Content-Type": "application/x-www-form-urlencoded",
};
const data = { id: musicItem.id, ids: `[${musicItem.id}]` };
const result = (await axios_1.get('http://music.163.com/api/song/detail',
{
headers,
params: data
})).data;
return {
artwork: result.songs[0].album.picUrl,
};
}
async function getAlbumInfo(albumItem) {
const headers = {
Referer: "https://y.music.163.com/",
Origin: "https://y.music.163.com/",
authority: "music.163.com",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.135 Safari/537.36",
"Content-Type": "application/x-www-form-urlencoded",
};
const data = {
resourceType: 3,
resourceId: albumItem.id,
limit: 15,
csrf_token: "",
};
const pae = getParamsAndEnc(JSON.stringify(data));
const paeData = qs.stringify(pae);
const res = (await (0, axios_1.default)({
method: "post",
url: `https://interface.music.163.com/weapi/v1/album/${albumItem.id}?csrf_token=`,
headers,
data: paeData,
})).data;
return {
albumItem: { description: res.album.description },
musicList: (res.songs || [])
.map(formatMusicItem),
};
}
async function getValidMusicItems(trackIds) {
const headers = {
Referer: "https://y.music.163.com/",
Origin: "https://y.music.163.com/",
authority: "music.163.com",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.135 Safari/537.36",
"Content-Type": "application/x-www-form-urlencoded",
};
try {
// 获取歌曲详情数据
const res = (await axios_1.default.get(`https://music.163.com/api/song/detail/?ids=[${trackIds.join(",")}]`, { headers })).data;
// 直接格式化歌曲项,不检查 URL
const validMusicItems = res.songs.map(formatMusicItem);
return validMusicItems;
}
catch (e) {
console.error(e);
return [];
}
}
async function getSheetMusicById(id) {
const headers = {
Referer: "https://y.music.163.com/",
Origin: "https://y.music.163.com/",
authority: "music.163.com",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.135 Safari/537.36",
};
const sheetDetail = (await axios_1.default.get(`https://music.163.com/api/v3/playlist/detail?id=${id}&n=5000`, {
headers,
})).data;
const trackIds = sheetDetail.playlist.trackIds.map((_) => _.id);
let result = [];
let idx = 0;
while (idx * 200 < trackIds.length) {
const res = await getValidMusicItems(trackIds.slice(idx * 200, (idx + 1) * 200));
result = result.concat(res);
++idx;
}
return result;
}
async function importMusicSheet(urlLike) {
const matchResult = urlLike.match(/(?:https:\/\/y\.music\.163.com\/m\/playlist\?id=([0-9]+))|(?:https?:\/\/music\.163\.com\/playlist\/([0-9]+)\/.*)|(?:https?:\/\/music.163.com(?:\/#)?\/playlist\?id=(\d+))|(?:^\s*(\d+)\s*$)/);
const id = matchResult[1] || matchResult[2] || matchResult[3] || matchResult[4];
return getSheetMusicById(id);
}
async function getTopLists() {
const res = await axios_1.default.get("https://music.163.com/discover/toplist", {
headers: {
referer: "https://music.163.com/",
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36 Edg/108.0.1462.54",
},
});
const $ = cheerio.load(res.data);
const children = $(".n-minelst").children();
const groups = [];
let currentGroup = {};
for (let c of children) {
if (c.tagName == "h2") {
if (currentGroup.title) {
groups.push(currentGroup);
}
currentGroup = {};
currentGroup.title = $(c).text();
currentGroup.data = [];
}
else if (c.tagName === "ul") {
let sections = $(c).children();
currentGroup.data = sections
.map((index, element) => {
const ele = $(element);
const id = ele.attr("data-res-id");
const coverImg = ele.find("img").attr("src").replace(/(\.jpg\?).*/, ".jpg?param=800y800");
const title = ele.find("p.name").text();
const description = ele.find("p.s-fc4").text();
return {
id,
coverImg,
title,
description,
};
})
.toArray();
}
}
if (currentGroup.title) {
groups.push(currentGroup);
}
return groups;
}
const qualityLevels = {
low: "128k",
standard: "320k",
high: "flac",
super: "wav",
};
async function getMediaSource(musicItem, quality) {
const targetQualities = [
qualityLevels[quality] || "320k",
"320k",
"128k",
].filter((item, index, arr) => !!item && arr.indexOf(item) === index);
for (const targetQuality of targetQualities) {
const res = (
await axios_1.default.get(`https://lxmusicapi.onrender.com/url/wy/${musicItem.id}/${targetQuality}`, {
headers: {
"X-Request-Key": "share-v3"
},
})
).data;
if (res === null || res === void 0 ? void 0 : res.url) {
return {
url: res.url,
};
}
}
return null;
}
const headers = {
authority: "music.163.com",
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.135 Safari/537.36",
"content-type": "application/x-www-form-urlencoded",
accept: "*/*",
origin: "https://music.163.com",
"sec-fetch-site": "same-origin",
"sec-fetch-mode": "cors",
"sec-fetch-dest": "empty",
referer: "https://music.163.com/",
"accept-language": "zh-CN,zh;q=0.9",
};
async function getRecommendSheetTags() {
const data = {
csrf_token: "",
};
const pae = getParamsAndEnc(JSON.stringify(data));
const paeData = qs.stringify(pae);
const res = (await (0, axios_1.default)({
method: "post",
url: "https://music.163.com/weapi/playlist/catalogue",
headers,
data: paeData,
})).data;
const cats = res.categories;
const map = {};
const catData = Object.entries(cats).map((_) => {
const tagData = {
title: _[1],
data: [],
};
map[_[0]] = tagData;
return tagData;
});
const pinned = [];
res.sub.forEach((tag) => {
const _tag = {
id: tag.name,
title: tag.name,
};
if (tag.hot) {
pinned.push(_tag);
}
map[tag.category].data.push(_tag);
});
return {
pinned,
data: catData,
};
}
async function getRecommendSheetsByTag(tag, page) {
const pageSize = 20;
const data = {
cat: tag.id || "全部",
order: "hot",
limit: pageSize,
offset: (page - 1) * pageSize,
total: true,
csrf_token: "",
};
const pae = getParamsAndEnc(JSON.stringify(data));
const paeData = qs.stringify(pae);
const res = (await (0, axios_1.default)({
method: "post",
url: "https://music.163.com/weapi/playlist/list",
headers,
data: paeData,
})).data;
const playLists = res.playlists.map((_) => ({
id: _.id,
artist: _.creator.nickname,
title: _.name,
artwork: _.coverImgUrl,
playCount: _.playCount,
createUserId: _.userId,
createTime: _.createTime,
description: _.description,
}));
return {
isEnd: !(res.more === true),
data: playLists,
};
}
async function getMusicSheetInfo(sheet, page) {
let trackIds = sheet._trackIds;
if (!trackIds) {
const id = sheet.id;
const headers = {
Referer: "https://y.music.163.com/",
Origin: "https://y.music.163.com/",
authority: "music.163.com",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.135 Safari/537.36",
};
const sheetDetail = (await axios_1.default.get(`https://music.163.com/api/v3/playlist/detail?id=${id}&n=5000`, {
headers,
})).data;
trackIds = sheetDetail.playlist.trackIds.map((_) => _.id);
}
const pageSize = 40;
const currentPageIds = trackIds.slice((page - 1) * pageSize, page * pageSize);
const res = await getValidMusicItems(currentPageIds);
let extra = {};
if (page <= 1) {
extra = {
_trackIds: trackIds,
};
}
return Object.assign({ isEnd: trackIds.length <= page * pageSize, musicList: res }, extra);
}
module.exports = {
platform: "小芸音乐",
author: 'Huibq',
version: "0.3.0",
appVersion: ">0.1.0-alpha.0",
srcUrl: "https://fastly.jsdelivr.net/gh/Huibq/keep-alive/Music_Free/xiaoyun.js",
cacheControl: "no-store",
hints: {
importMusicSheet: [
"网易云:APP点击分享,然后复制链接",
"默认歌单无法导入,先新建一个空白歌单复制过去再导入新歌单即可"
],
},
supportedSearchType: ["music", "album", "sheet", "artist", "lyric"],
async search(query, page, type) {
if (type === "music") {
return await searchMusic(query, page);
}
if (type === "album") {
return await searchAlbum(query, page);
}
if (type === "artist") {
return await searchArtist(query, page);
}
if (type === "sheet") {
return await searchMusicSheet(query, page);
}
if (type === "lyric") {
return await searchLyric(query, page);
}
},
getMediaSource,
getMusicInfo,
getAlbumInfo,
getLyric,
getArtistWorks,
importMusicSheet,
getTopLists,
getTopListDetail,
getRecommendSheetTags,
getMusicSheetInfo,
getRecommendSheetsByTag,
};