Feat: DJI & Bili API

大疆和哔哩哔哩的 iframe API
This commit is contained in:
奇趣保罗 2022-04-17 00:49:45 +08:00
parent 99c196afad
commit 8cdf2a6655
5 changed files with 267 additions and 3 deletions

121
public/assets/bili.css Normal file
View File

@ -0,0 +1,121 @@
/* 哔哩小窗样式 */
*{
margin: 0;
box-sizing: border-box;
}
body{
font-size: 16px;
line-height: 1.5;
}
img{
max-width: 100%;
vertical-align: middle;
}
a{
color: inherit;
transition: color .3s;
text-decoration: none;
}
.bili-box{
color: #555;
height: 10em;
display: flex;
position: relative;
}
.bili-box.white{ background: #fff }
.bili-box.gray{ background: #f9f9f9 }
.bili-box.black{
color: #eee;
background: #333;
}
.bili-box h1{
overflow: hidden;
font-size: 1.05em;
font-weight: normal;
white-space: nowrap;
margin-bottom: .5em;
text-overflow: ellipsis;
}
.bili-box h1 a:hover{
color: #4cb1f3;
}
.bili-box .bili-img{
flex: 0 0 30%;
max-width: 30%;
}
.bili-box .bili-img img{
width: 100%;
height: 100%;
object-fit: cover;
}
.bili-box .bili-info{
padding: 1em;
max-width: 70%;
}
.bili-info p{
opacity: .75;
font-size: .85em;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 3;
overflow: hidden;
text-overflow: clip;
}
.bili-powered{
right: 0;
bottom: 0;
color: #fff;
opacity: .8;
padding: .5em;
line-height: 1;
font-size: .5em;
position: absolute;
background: #4cb1f3;
transition: opacity .3s;
border-radius: 1em 0 0 0;
}
.bili-powered:hover{ opacity: 1 }
.black .bili-powered{ background: #0d5e92 }
.bili-hidden{ display: none }
@media screen and (max-width: 768px){
.bili-box .bili-info{
left: 0;
right: 0;
bottom: 0;
position: absolute;
color: #fff;
max-width: none;
font-size: .875em;
padding: 1em .75em;
background: rgba(0, 0, 0, .5);
-webkit-backdrop-filter: blur(3px);
backdrop-filter: blur(3px);
}
.bili-info p{
-webkit-line-clamp: 1;
}
.bili-box .bili-img{
flex: 1;
max-width: none;
}
.bili-powered{
top: 0;
bottom: inherit;
border-radius: 0 0 0 1em;
}
}

93
src/api/bili.ts Normal file
View File

@ -0,0 +1,93 @@
import { Api, Get, Query, useContext } from "@midwayjs/hooks";
import { getVideoInfo } from "./utils/bili";
import { client } from "./utils/redis";
const availableStyle = ["gray", "white", "black", "shadow"];
export default Api(
Get(),
Query<{ av?: string, bv?: string }>(),
async () => {
const ctx = useContext();
const id = ctx.query.av || ctx.query.bv;
const type = "av" in ctx.query ? "av" : "bv" in ctx.query ? "bv" : undefined;
if (!id) {
return "请输入 ID";
}
if (!type) {
return "无效的 type";
}
// 增加使用数量
await client.incr("api-next:stat:bili");
// 尝试使用缓存
const cached = await client.lRange(`api-next:bili:${id}`, 0, 3);
let result;
if (cached.length) {
result = {
title: cached[0],
image: cached[1],
desc: cached[2],
};
}
else {
const info = await getVideoInfo(id, type);
if (!info) {
return "获取失败";
}
await client.rPush(`api-next:bili:${id}`, [
info.title,
info.image,
info.desc,
]);
await client.expire(`api-next:bili:${id}`, 21600);
result = info;
}
// 后期参数填补
let classname = "bili-box";
if (ctx.query.style && availableStyle.includes(ctx.query.style)) {
classname += ` ${ctx.query.style}`;
}
const link = `https://www.bilibili.com/video/${type === "av" ? "av" : ""}/${id}`;
return (`
<!DOCTYPE html>
<html lang="zh-cmn-hans">
<head>
<meta charset="UTF-8">
<title> iFrame API</title>
<meta name="referrer" content="no-referrer" />
<link href="/assets/bili.css" rel="stylesheet" type="text/css" />
</head>
<body>
<div class="${classname}">
<a class="bili-img" href="${link}" target="_blank">
<img src="${result.image}" alt="${result.title}" referrerpolicy="no-referrer" />
</a>
<div class="bili-info">
<h1><a href="${link}" target="_blank">${result.title}</a></h1>
<p>${result.desc}</p>
</div>
<div class="bili-powered">
<a href="https://api.paugram.com" target="_blank" title="使用了奇趣保罗的哔哩哔哩小窗接口"></a>
</div>
<div class="bili-hidden">Cached: ${cached.length ? true : false}</div>
</div>
</body>
</html>`)
}
);

27
src/api/dji.ts Normal file
View File

@ -0,0 +1,27 @@
import { Api, Get, Query, useContext } from "@midwayjs/hooks";
import { client } from "./utils/redis";
export default Api(
Get(),
Query<{ id?: string, loop?: string }>(),
async () => {
const ctx = useContext();
// 增加使用数量
await client.incr("api-next:stat:dji");
return (`
<!DOCTYPE html>
<html lang="zh-cmn-hans">
<head>
<title></title>
<meta name="referrer" content="no-referrer"/>
<style>*{ margin: 0 } video{ width: 100%; height: 100%; background: #000; max-width: 100%; outline: none }</style>
</head>
<body>
${ctx.query.id ? `<video src="https://cn-videos.dji.net/video_trans/${ctx.query.id}/720.mp4" controls${"loop" in ctx.query ? " loop" : ""}></video>` : "<p>没有输入 ID</p>"}
</body>
</html>`)
}
);

View File

@ -14,9 +14,6 @@ export default Api(
ctx.set("access-control-allow-origin", "*"); ctx.set("access-control-allow-origin", "*");
ctx.set("access-control-allow-headers", "x-requested-with"); ctx.set("access-control-allow-headers", "x-requested-with");
// 增加使用数量
await client.incr("api-next:stat:netease");
const { id } = ctx.query; const { id } = ctx.query;
if (!id) { if (!id) {
@ -26,6 +23,9 @@ export default Api(
} }
} }
// 增加使用数量
await client.incr("api-next:stat:netease");
// 直接播放 // 直接播放
if ("play" in ctx.query) { if ("play" in ctx.query) {
ctx.status = 302; ctx.status = 302;

23
src/api/utils/bili.ts Normal file
View File

@ -0,0 +1,23 @@
import fetch from "isomorphic-unfetch";
export const getVideoInfo = async (id: number | string, type: "av" | "bv") => {
return fetch(`https://api.bilibili.com/x/web-interface/view?${type === "av" ? "aid" : "bvid"}=${id}`).then(res => {
try{
return res.json();
}
catch(e){
// Todo: 这个地方可能会有问题
return undefined;
}
}).then(json => {
return json ? parseVideoData(json.data) : undefined;
})
}
export const parseVideoData = (json: any) => {
return {
title: json.title,
image: json.pic.replace(/^http:\/\//, "https://"),
desc: json.desc.length > 60 ? `${json.desc.substring(0, 60)}...` : json.desc,
}
}