Feat: Netease & ACGM API
引入 Prisma 作为 ORM,React-Router V6 作为路由,新增网易云和随机动漫音乐 API
This commit is contained in:
parent
e381eb9598
commit
50f76e3947
|
|
@ -0,0 +1,18 @@
|
||||||
|
generator client {
|
||||||
|
provider = "prisma-client-js"
|
||||||
|
}
|
||||||
|
|
||||||
|
datasource db {
|
||||||
|
provider = "mysql"
|
||||||
|
url = "mysql://root:233456@localhost:8889/paul_api_next"
|
||||||
|
}
|
||||||
|
|
||||||
|
model ACGM {
|
||||||
|
id Int @id @default(autoincrement())
|
||||||
|
title String?
|
||||||
|
artist String?
|
||||||
|
album String?
|
||||||
|
music_id Int @unique
|
||||||
|
created_at DateTime @default(now())
|
||||||
|
updated_at DateTime?
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
import { PrismaClient } from '@prisma/client'
|
||||||
|
|
||||||
|
const prisma = new PrismaClient();
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
console.log(`Start seeding ...`);
|
||||||
|
|
||||||
|
const user = await prisma.aCGM.create({
|
||||||
|
data: {
|
||||||
|
title: "Colorful world",
|
||||||
|
music_id: 435289270
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(`Seeding finished.`);
|
||||||
|
}
|
||||||
|
|
||||||
|
main()
|
||||||
|
.catch((e) => {
|
||||||
|
console.error(e);
|
||||||
|
process.exit(1);
|
||||||
|
})
|
||||||
|
.finally(async () => {
|
||||||
|
await prisma.$disconnect();
|
||||||
|
});
|
||||||
|
|
@ -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)) {
|
if (!("info" in ctx.query)) {
|
||||||
ctx.status = 302;
|
ctx.status = 302;
|
||||||
ctx.set("location", image.url);
|
ctx.set("location", image.url);
|
||||||
|
|
||||||
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
||||||
|
};
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
@ -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(
|
export default Api(
|
||||||
Get(),
|
Get(),
|
||||||
async () => {
|
async () => {
|
||||||
|
const wallpaperStat = await client.get("api-next:stat:wallpaper");
|
||||||
const neteaseStat = await client.get("api-next:stat:netease");
|
const neteaseStat = await client.get("api-next:stat:netease");
|
||||||
const bingStat = await client.get("api-next:stat:bing");
|
const bingStat = await client.get("api-next:stat:bing");
|
||||||
|
const acgmStat = await client.get("api-next:stat:acgm");
|
||||||
|
|
||||||
return {
|
return {
|
||||||
code: 1,
|
code: 1,
|
||||||
data: {
|
data: {
|
||||||
wallpaper: -1,
|
wallpaper: Number(wallpaperStat),
|
||||||
netease: Number(neteaseStat),
|
netease: Number(neteaseStat),
|
||||||
music: -1,
|
acgm: Number(acgmStat),
|
||||||
avatar: -1,
|
|
||||||
bili: -1,
|
bili: -1,
|
||||||
bing: Number(bingStat),
|
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();
|
||||||
46
yarn.lock
46
yarn.lock
|
|
@ -629,6 +629,23 @@
|
||||||
"@nodelib/fs.scandir" "2.1.5"
|
"@nodelib/fs.scandir" "2.1.5"
|
||||||
fastq "^1.6.0"
|
fastq "^1.6.0"
|
||||||
|
|
||||||
|
"@prisma/client@^3.12.0":
|
||||||
|
version "3.12.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@prisma/client/-/client-3.12.0.tgz#a0eb49ffea5c128dd11dffb896d7139a60073d12"
|
||||||
|
integrity sha512-4NEQjUcWja/NVBvfuDFscWSk1/rXg3+wj+TSkqXCb1tKlx/bsUE00rxsvOvGg7VZ6lw1JFpGkwjwmsOIc4zvQw==
|
||||||
|
dependencies:
|
||||||
|
"@prisma/engines-version" "3.12.0-37.22b822189f46ef0dc5c5b503368d1bee01213980"
|
||||||
|
|
||||||
|
"@prisma/engines-version@3.12.0-37.22b822189f46ef0dc5c5b503368d1bee01213980":
|
||||||
|
version "3.12.0-37.22b822189f46ef0dc5c5b503368d1bee01213980"
|
||||||
|
resolved "https://registry.yarnpkg.com/@prisma/engines-version/-/engines-version-3.12.0-37.22b822189f46ef0dc5c5b503368d1bee01213980.tgz#829ca3d9d0d92555f44644606d4edfd45b2f5886"
|
||||||
|
integrity sha512-o+jo8d7ZEiVpcpNWUDh3fj2uPQpBxl79XE9ih9nkogJbhw6P33274SHnqheedZ7PyvPIK/mvU8MLNYgetgXPYw==
|
||||||
|
|
||||||
|
"@prisma/engines@3.12.0-37.22b822189f46ef0dc5c5b503368d1bee01213980":
|
||||||
|
version "3.12.0-37.22b822189f46ef0dc5c5b503368d1bee01213980"
|
||||||
|
resolved "https://registry.yarnpkg.com/@prisma/engines/-/engines-3.12.0-37.22b822189f46ef0dc5c5b503368d1bee01213980.tgz#e52e364084c4d05278f62768047b788665e64a45"
|
||||||
|
integrity sha512-zULjkN8yhzS7B3yeEz4aIym4E2w1ChrV12i14pht3ePFufvsAvBSoZ+tuXMvfSoNTgBS5E4bolRzLbMmbwkkMQ==
|
||||||
|
|
||||||
"@rollup/pluginutils@^4.2.0":
|
"@rollup/pluginutils@^4.2.0":
|
||||||
version "4.2.0"
|
version "4.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-4.2.0.tgz#a14bbd058fdbba0a5647143b16ed0d86fb60bd08"
|
resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-4.2.0.tgz#a14bbd058fdbba0a5647143b16ed0d86fb60bd08"
|
||||||
|
|
@ -1860,6 +1877,13 @@ hexoid@1.0.0:
|
||||||
resolved "https://registry.yarnpkg.com/hexoid/-/hexoid-1.0.0.tgz#ad10c6573fb907de23d9ec63a711267d9dc9bc18"
|
resolved "https://registry.yarnpkg.com/hexoid/-/hexoid-1.0.0.tgz#ad10c6573fb907de23d9ec63a711267d9dc9bc18"
|
||||||
integrity sha512-QFLV0taWQOZtvIRIAdBChesmogZrtuXvVWsFHZTk2SU+anspqZ2vMnoLg7IE1+Uk16N19APic1BuF8bC8c2m5g==
|
integrity sha512-QFLV0taWQOZtvIRIAdBChesmogZrtuXvVWsFHZTk2SU+anspqZ2vMnoLg7IE1+Uk16N19APic1BuF8bC8c2m5g==
|
||||||
|
|
||||||
|
history@^5.2.0:
|
||||||
|
version "5.3.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/history/-/history-5.3.0.tgz#1548abaa245ba47992f063a0783db91ef201c73b"
|
||||||
|
integrity sha512-ZqaKwjjrAYUYfLG+htGaIIZ4nioX2L70ZUMIFysS3xvBsSG4x/n1V6TXV3N8ZYNuFGlDirFg32T7B6WOUPDYcQ==
|
||||||
|
dependencies:
|
||||||
|
"@babel/runtime" "^7.7.6"
|
||||||
|
|
||||||
http-assert@^1.3.0:
|
http-assert@^1.3.0:
|
||||||
version "1.5.0"
|
version "1.5.0"
|
||||||
resolved "https://registry.yarnpkg.com/http-assert/-/http-assert-1.5.0.tgz#c389ccd87ac16ed2dfa6246fd73b926aa00e6b8f"
|
resolved "https://registry.yarnpkg.com/http-assert/-/http-assert-1.5.0.tgz#c389ccd87ac16ed2dfa6246fd73b926aa00e6b8f"
|
||||||
|
|
@ -2383,6 +2407,13 @@ postcss@^8.4.12:
|
||||||
picocolors "^1.0.0"
|
picocolors "^1.0.0"
|
||||||
source-map-js "^1.0.2"
|
source-map-js "^1.0.2"
|
||||||
|
|
||||||
|
prisma@^3.12.0:
|
||||||
|
version "3.12.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/prisma/-/prisma-3.12.0.tgz#9675e0e72407122759d3eadcb6d27cdccd3497bd"
|
||||||
|
integrity sha512-ltCMZAx1i0i9xuPM692Srj8McC665h6E5RqJom999sjtVSccHSD8Z+HSdBN2183h9PJKvC5dapkn78dd0NWMBg==
|
||||||
|
dependencies:
|
||||||
|
"@prisma/engines" "3.12.0-37.22b822189f46ef0dc5c5b503368d1bee01213980"
|
||||||
|
|
||||||
proper-url-join@^2.1.1:
|
proper-url-join@^2.1.1:
|
||||||
version "2.1.1"
|
version "2.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/proper-url-join/-/proper-url-join-2.1.1.tgz#ee4146edc08f512a0ee1053a355e06950691d06f"
|
resolved "https://registry.yarnpkg.com/proper-url-join/-/proper-url-join-2.1.1.tgz#ee4146edc08f512a0ee1053a355e06950691d06f"
|
||||||
|
|
@ -2441,6 +2472,21 @@ react-refresh@^0.11.0:
|
||||||
resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.11.0.tgz#77198b944733f0f1f1a90e791de4541f9f074046"
|
resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.11.0.tgz#77198b944733f0f1f1a90e791de4541f9f074046"
|
||||||
integrity sha512-F27qZr8uUqwhWZboondsPx8tnC3Ct3SxZA3V5WyEvujRyyNv0VYPhoBg1gZ8/MV5tubQp76Trw8lTv9hzRBa+A==
|
integrity sha512-F27qZr8uUqwhWZboondsPx8tnC3Ct3SxZA3V5WyEvujRyyNv0VYPhoBg1gZ8/MV5tubQp76Trw8lTv9hzRBa+A==
|
||||||
|
|
||||||
|
react-router-dom@^6.3.0:
|
||||||
|
version "6.3.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-6.3.0.tgz#a0216da813454e521905b5fa55e0e5176123f43d"
|
||||||
|
integrity sha512-uaJj7LKytRxZNQV8+RbzJWnJ8K2nPsOOEuX7aQstlMZKQT0164C+X2w6bnkqU3sjtLvpd5ojrezAyfZ1+0sStw==
|
||||||
|
dependencies:
|
||||||
|
history "^5.2.0"
|
||||||
|
react-router "6.3.0"
|
||||||
|
|
||||||
|
react-router@6.3.0:
|
||||||
|
version "6.3.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/react-router/-/react-router-6.3.0.tgz#3970cc64b4cb4eae0c1ea5203a80334fdd175557"
|
||||||
|
integrity sha512-7Wh1DzVQ+tlFjkeo+ujvjSqSJmkt1+8JO+T5xklPlgrh70y7ogx75ODRW0ThWhY7S+6yEDks8TYrtQe/aoboBQ==
|
||||||
|
dependencies:
|
||||||
|
history "^5.2.0"
|
||||||
|
|
||||||
react@^17.0.2:
|
react@^17.0.2:
|
||||||
version "17.0.2"
|
version "17.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/react/-/react-17.0.2.tgz#d0b5cc516d29eb3eee383f75b62864cfb6800037"
|
resolved "https://registry.yarnpkg.com/react/-/react-17.0.2.tgz#d0b5cc516d29eb3eee383f75b62864cfb6800037"
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue