Feat: Netease & ACGM API
引入 Prisma 作为 ORM,React-Router V6 作为路由,新增网易云和随机动漫音乐 API
This commit is contained in:
@@ -0,0 +1,94 @@
|
||||
import { Api, Get, Query, useContext } from "@midwayjs/hooks";
|
||||
|
||||
import { getSong } from "../utils/netease";
|
||||
|
||||
import { client } from "../utils/redis";
|
||||
import { prisma } from "../utils/prisma";
|
||||
import { ACGM, Prisma } from "@prisma/client";
|
||||
|
||||
export default Api(
|
||||
Get(),
|
||||
Query<{ play?: string }>(),
|
||||
async () => {
|
||||
const ctx = useContext();
|
||||
|
||||
// 增加使用数量
|
||||
await client.incr("api-next:stat:acgm");
|
||||
|
||||
const item = await prisma.$queryRaw<ACGM[]>(
|
||||
Prisma.sql`SELECT * FROM ACGM ORDER BY RAND() LIMIT 1`
|
||||
)
|
||||
|
||||
console.log(item);
|
||||
|
||||
const id = item ?. [0] ?. music_id;
|
||||
|
||||
if (!id) {
|
||||
return {
|
||||
code: 0,
|
||||
msg: "Failed, no data found"
|
||||
}
|
||||
}
|
||||
|
||||
if ("play" in ctx.query) {
|
||||
ctx.status = 302;
|
||||
ctx.set("location", `https://music.163.com/song/media/outer/url?id=${id}`);
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
// 尝试使用缓存
|
||||
const cached = await client.lRange(`api-next:163:${id}`, 0, 7);
|
||||
|
||||
if (cached.length) {
|
||||
return {
|
||||
code: 1,
|
||||
msg: "Success",
|
||||
data: {
|
||||
id: Number(id),
|
||||
title: cached[0],
|
||||
artist: cached[1],
|
||||
album: cached[2],
|
||||
cover: cached[3],
|
||||
lyric: cached[4],
|
||||
sub_lyric: cached[5],
|
||||
link: cached[6],
|
||||
served: Boolean(cached[7]),
|
||||
cached: true
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// 全新获取
|
||||
const song = await getSong(id);
|
||||
|
||||
if (song) {
|
||||
await client.rPush(`api-next:163:${id}`, [
|
||||
song.title,
|
||||
song.artist,
|
||||
song.album,
|
||||
song.cover,
|
||||
song.lyric,
|
||||
song.sub_lyric,
|
||||
song.link,
|
||||
song.served
|
||||
]);
|
||||
await client.expire(`api-next:163:${id}`, 21600);
|
||||
|
||||
return {
|
||||
code: 1,
|
||||
msg: "Success",
|
||||
data: {
|
||||
...song,
|
||||
cached: false
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
code: 1,
|
||||
msg: "Failed",
|
||||
data: undefined
|
||||
};
|
||||
}
|
||||
);
|
||||
@@ -52,6 +52,8 @@ export default Api(
|
||||
if (!("info" in ctx.query)) {
|
||||
ctx.status = 302;
|
||||
ctx.set("location", image.url);
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
import { Api, Get, Query, useContext } from '@midwayjs/hooks';
|
||||
|
||||
import { getSong } from "../utils/netease";
|
||||
|
||||
import { client } from '../utils/redis';
|
||||
|
||||
export default Api(
|
||||
Get(),
|
||||
Query<{ id?: string }>(),
|
||||
async () => {
|
||||
const ctx = useContext();
|
||||
|
||||
// 增加使用数量
|
||||
await client.incr("api-next:stat:netease");
|
||||
|
||||
const { id } = ctx.query;
|
||||
|
||||
// 尝试使用缓存
|
||||
const cached = await client.lRange(`api-next:163:${id}`, 0, 7);
|
||||
|
||||
if (cached.length) {
|
||||
return {
|
||||
code: 1,
|
||||
msg: "Success",
|
||||
data: {
|
||||
id: Number(id),
|
||||
title: cached[0],
|
||||
artist: cached[1],
|
||||
album: cached[2],
|
||||
cover: cached[3],
|
||||
lyric: cached[4],
|
||||
sub_lyric: cached[5],
|
||||
link: cached[6],
|
||||
served: Boolean(cached[7]),
|
||||
cached: true
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// 全新获取
|
||||
const song = await getSong(id);
|
||||
|
||||
if (song) {
|
||||
await client.rPush(`api-next:163:${id}`, [
|
||||
song.title,
|
||||
song.artist,
|
||||
song.album,
|
||||
song.cover,
|
||||
song.lyric,
|
||||
song.sub_lyric,
|
||||
song.link,
|
||||
song.served
|
||||
]);
|
||||
await client.expire(`api-next:163:${id}`, 21600);
|
||||
|
||||
return {
|
||||
code: 1,
|
||||
msg: "Success",
|
||||
data: {
|
||||
...song,
|
||||
cached: false
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
code: 1,
|
||||
msg: "Failed",
|
||||
data: undefined
|
||||
};
|
||||
}
|
||||
);
|
||||
+6
-5
@@ -1,20 +1,21 @@
|
||||
import { Api, Get } from '@midwayjs/hooks';
|
||||
import { Api, Get } from "@midwayjs/hooks";
|
||||
|
||||
import { client } from '../utils/redis';
|
||||
import { client } from "../utils/redis";
|
||||
|
||||
export default Api(
|
||||
Get(),
|
||||
async () => {
|
||||
const wallpaperStat = await client.get("api-next:stat:wallpaper");
|
||||
const neteaseStat = await client.get("api-next:stat:netease");
|
||||
const bingStat = await client.get("api-next:stat:bing");
|
||||
const acgmStat = await client.get("api-next:stat:acgm");
|
||||
|
||||
return {
|
||||
code: 1,
|
||||
data: {
|
||||
wallpaper: -1,
|
||||
wallpaper: Number(wallpaperStat),
|
||||
netease: Number(neteaseStat),
|
||||
music: -1,
|
||||
avatar: -1,
|
||||
acgm: Number(acgmStat),
|
||||
bili: -1,
|
||||
bing: Number(bingStat),
|
||||
}
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
import fetch from "isomorphic-unfetch";
|
||||
|
||||
export const getSong = async (id: number) => {
|
||||
var urlencoded = new URLSearchParams();
|
||||
urlencoded.append("ids", `['${id}']`);
|
||||
|
||||
return fetch("http://music.163.com/api/song/detail/", {
|
||||
method: "POST",
|
||||
body: urlencoded,
|
||||
headers: {
|
||||
"Content-Type": "application/x-www-form-urlencoded",
|
||||
"Accept-language": "en",
|
||||
"user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.51 Safari/537.36"
|
||||
}
|
||||
}).then(res => {
|
||||
try{
|
||||
return res.json();
|
||||
}
|
||||
catch(e){
|
||||
// Todo: 这个地方可能会有问题
|
||||
return undefined;
|
||||
}
|
||||
}).then(json => {
|
||||
return json ? parseSongData(json.songs[0]) : undefined;
|
||||
})
|
||||
}
|
||||
|
||||
export const getLyric = async (id: number) => {
|
||||
return fetch(`http://music.163.com/api/song/lyric?id=${id}&lv=-1&kv=-1&tv=-1`).then(res => {
|
||||
try{
|
||||
return res.json();
|
||||
}
|
||||
catch(e){
|
||||
// Todo: 这个地方可能会有问题
|
||||
return undefined;
|
||||
}
|
||||
}).then(json => {
|
||||
return json ? [json.lrc ?. lyric, json.tlyric ?. lyric || ""] : ["", ""];
|
||||
})
|
||||
}
|
||||
|
||||
export const parseSongData = async (item: any) => {
|
||||
const [lyric, sub_lyric] = await getLyric(item.id);
|
||||
|
||||
return {
|
||||
id: item.id,
|
||||
title: item.name,
|
||||
artist: item.artists ?. [0].name || "",
|
||||
album: item.album.name || "",
|
||||
cover: `${item.album.picUrl.replace("http://", "https://")}?param=250y250"`,
|
||||
lyric,
|
||||
sub_lyric,
|
||||
link: `https://music.163.com/song/media/outer/url?id=${item.id}`,
|
||||
served: false
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
import { PrismaClient } from "@prisma/client";
|
||||
|
||||
export const prisma = new PrismaClient();
|
||||
Reference in New Issue
Block a user