362 lines
12 KiB
JavaScript
362 lines
12 KiB
JavaScript
"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",
|
|
});
|
|
});
|
|
});
|