Initial import: Music_Server, MusicFree, catalog-sync
This commit is contained in:
@@ -0,0 +1,41 @@
|
||||
name: keep-alive
|
||||
|
||||
# 触发条件
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
schedule:
|
||||
- cron: '0 0 */3 * *'
|
||||
|
||||
env:
|
||||
TZ: Asia/Shanghai
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: 迁出代码
|
||||
uses: actions/checkout@v2
|
||||
- name: 安装Python
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: '3.10'
|
||||
- name: 加载缓存
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ~/.cache/pip
|
||||
key: ${{ runner.os }}-pip-${{ hashFiles('**/run_in_Actions/requirements.txt') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-pip-
|
||||
- name: 设置时区
|
||||
run: sudo timedatectl set-timezone 'Asia/Shanghai'
|
||||
- name: 安装依赖
|
||||
run: |
|
||||
pip install -r ./requirements.txt
|
||||
- name: 执行任务
|
||||
env:
|
||||
render_url: ${{secrets.render_url}}
|
||||
render_key: ${{secrets.render_key}}
|
||||
header_key: ${{secrets.header_key}}
|
||||
run: |
|
||||
python ./main.py
|
||||
@@ -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©rightId=${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¬ice=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,
|
||||
};
|
||||
@@ -0,0 +1,59 @@
|
||||
# <p align="center">注意事项&使用教程</p>
|
||||
|
||||
|
||||
## 一. 注意
|
||||
|
||||
### 1.1 关于音源
|
||||
|
||||
> 音质最高支持320k。
|
||||
>
|
||||
> 不支持数字专辑,反复请求可能会导致`封禁IP`!
|
||||
>
|
||||
> 仅供在线试听,禁止批量下载,批量下载会被`封禁IP`,请规范使用!
|
||||
>
|
||||
> 尽量避免频繁切换歌曲,否则将导致`封禁IP`
|
||||
|
||||
### 1.2 关于使用
|
||||
|
||||
- `MusicFree`请在设置里选择:`播放失败时暂停`!
|
||||
|
||||
> 否则在某些情况下会被封禁IP
|
||||
|
||||

|
||||
|
||||
### 1.3 关于解封
|
||||
|
||||
> 由于邮件太多,来不及看,如果被封,数据流量情况下开启飞行模式切换IP。
|
||||
|
||||
## 二. 洛雪音乐
|
||||
|
||||
> 软件请转跳下载: [安卓 -> 洛雪音乐](https://github.com/lyswhut/lx-music-mobile/releases)
|
||||
>
|
||||
> 软件请转跳下载: [Windows&MacOS -> 洛雪音乐](https://github.com/lyswhut/lx-music-desktop/releases)
|
||||
|
||||
- 洛雪音乐音源链接:
|
||||
```any
|
||||
https://fastly.jsdelivr.net/gh/Huibq/keep-alive/render_api.js
|
||||
```
|
||||
|
||||
- 教程请看png!
|
||||
|
||||

|
||||
|
||||
## 三. MusicFree
|
||||
|
||||
> 软件请转跳下载: [安卓-> MusicFree](https://github.com/maotoumao/MusicFree/releases)
|
||||
>
|
||||
> 软件请转跳下载: [Windows&MacOS -> MusicFree](https://github.com/maotoumao/MusicFreeDesktop/releases)
|
||||
|
||||
- MusicFree音源链接:
|
||||
```any
|
||||
https://fastly.jsdelivr.net/gh/Huibq/keep-alive/Music_Free/myPlugins.json
|
||||
```
|
||||
|
||||
- 教程请看png!
|
||||
|
||||

|
||||
|
||||
## Star History
|
||||
[](https://starchart.cc/Huibq/keep-alive)
|
||||
@@ -0,0 +1,19 @@
|
||||
import os
|
||||
import traceback
|
||||
import requests
|
||||
|
||||
render_url = os.environ['render_url']
|
||||
render_key = os.environ['render_key']
|
||||
header_key = os.environ['header_key']
|
||||
|
||||
headers = {
|
||||
'User-Agent': "Reqable/9.9.9",
|
||||
'X-Request-Key': render_key,
|
||||
'X-Forwarded-For': header_key
|
||||
}
|
||||
|
||||
try:
|
||||
req = requests.get(render_url, headers=headers, timeout=10)
|
||||
print(req.text)
|
||||
except:
|
||||
traceback.print_exc()
|
||||
@@ -0,0 +1,89 @@
|
||||
/*!
|
||||
* @name Huibq_lxmusic源
|
||||
* @description Github搜索“洛雪音乐音源”,禁止批量下载!
|
||||
* @version v1.2.0
|
||||
* @author Huibq
|
||||
*/
|
||||
const DEV_ENABLE = false
|
||||
const API_URL = 'https://lxmusicapi.onrender.com'
|
||||
const API_KEY = 'share-v3'
|
||||
const MUSIC_QUALITY = {
|
||||
kw: ['128k', '320k', 'flac', 'wav'],
|
||||
kg: ['128k', '320k', 'flac', 'wav'],
|
||||
tx: ['128k', '320k', 'flac', 'wav'],
|
||||
wy: ['128k', '320k', 'flac', 'wav'],
|
||||
mg: ['128k', '320k', 'flac', 'wav'],
|
||||
}
|
||||
const MUSIC_SOURCE = Object.keys(MUSIC_QUALITY)
|
||||
const { EVENT_NAMES, request, on, send, utils, env, version } = globalThis.lx
|
||||
const httpFetch = (url, options = { method: 'GET' }) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
request(url, options, (err, resp) => {
|
||||
if (err) return reject(err)
|
||||
resolve(resp)
|
||||
})
|
||||
})
|
||||
}
|
||||
const handleGetMusicUrl = async (source, musicInfo, quality) => {
|
||||
const songId = musicInfo.hash ?? musicInfo.songmid
|
||||
|
||||
const request = await httpFetch(`${API_URL}/url/${source}/${songId}/${quality}`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'User-Agent': `${env ? `lx-music-${env}/${version}` : `lx-usic-request/${version}`}`,
|
||||
'X-Request-Key': API_KEY,
|
||||
},
|
||||
})
|
||||
const { body } = request
|
||||
if (!body || isNaN(Number(body.code))) throw new Error('unknow error')
|
||||
switch (body.code) {
|
||||
case 0:
|
||||
return body.url
|
||||
case 1:
|
||||
throw new Error('block ip')
|
||||
case 2:
|
||||
throw new Error('get music url failed')
|
||||
case 4:
|
||||
throw new Error('internal server error')
|
||||
case 5:
|
||||
throw new Error('too many requests')
|
||||
case 6:
|
||||
throw new Error('param error')
|
||||
default:
|
||||
throw new Error(body.msg ?? 'unknow error')
|
||||
}
|
||||
}
|
||||
const musicSources = {}
|
||||
MUSIC_SOURCE.forEach(item => {
|
||||
musicSources[item] = {
|
||||
name: item,
|
||||
type: 'music',
|
||||
actions: ['musicUrl'],
|
||||
qualitys: MUSIC_QUALITY[item],
|
||||
}
|
||||
})
|
||||
on(EVENT_NAMES.request, ({ action, source, info }) => {
|
||||
switch (action) {
|
||||
case 'musicUrl':
|
||||
if (env != 'mobile') {
|
||||
console.group(`Handle Action(musicUrl)`)
|
||||
console.log('source', source)
|
||||
console.log('quality', info.type)
|
||||
console.log('musicInfo', info.musicInfo)
|
||||
console.groupEnd()
|
||||
} else {
|
||||
console.log(`Handle Action(musicUrl)`)
|
||||
console.log('source', source)
|
||||
console.log('quality', info.type)
|
||||
console.log('musicInfo', info.musicInfo)
|
||||
}
|
||||
return handleGetMusicUrl(source, info.musicInfo, info.type)
|
||||
.then(data => Promise.resolve(data))
|
||||
.catch(err => Promise.reject(err))
|
||||
default:
|
||||
console.error(`action(${action}) not support`)
|
||||
return Promise.reject('action not support')
|
||||
}
|
||||
})
|
||||
send(EVENT_NAMES.inited, { status: true, openDevTools: DEV_ENABLE, sources: musicSources })
|
||||
Reference in New Issue
Block a user