From 8cdf2a66553f46c3c3c316e0fe32eab63dcf35ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=A5=87=E8=B6=A3=E4=BF=9D=E7=BD=97?= Date: Sun, 17 Apr 2022 00:49:45 +0800 Subject: [PATCH] Feat: DJI & Bili API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 大疆和哔哩哔哩的 iframe API --- public/assets/bili.css | 121 +++++++++++++++++++++++++++++++++++++++++ src/api/bili.ts | 93 +++++++++++++++++++++++++++++++ src/api/dji.ts | 27 +++++++++ src/api/netease.ts | 6 +- src/api/utils/bili.ts | 23 ++++++++ 5 files changed, 267 insertions(+), 3 deletions(-) create mode 100644 public/assets/bili.css create mode 100644 src/api/bili.ts create mode 100644 src/api/dji.ts create mode 100644 src/api/utils/bili.ts diff --git a/public/assets/bili.css b/public/assets/bili.css new file mode 100644 index 0000000..6e0fa10 --- /dev/null +++ b/public/assets/bili.css @@ -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; + } +} diff --git a/src/api/bili.ts b/src/api/bili.ts new file mode 100644 index 0000000..a90a7f7 --- /dev/null +++ b/src/api/bili.ts @@ -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 (` + + + + + 哔哩小窗 iFrame API + + + + +
+ + ${result.title} + +
+

${result.title}

+

${result.desc}

+
+ +
Cached: ${cached.length ? true : false}
+
+ +`) + } +); diff --git a/src/api/dji.ts b/src/api/dji.ts new file mode 100644 index 0000000..7f71531 --- /dev/null +++ b/src/api/dji.ts @@ -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 (` + + + + 大疆视频外链 + + + + + ${ctx.query.id ? `` : "

没有输入 ID

"} + +`) + } +); diff --git a/src/api/netease.ts b/src/api/netease.ts index b966cdd..2cd4bfc 100644 --- a/src/api/netease.ts +++ b/src/api/netease.ts @@ -14,9 +14,6 @@ export default Api( ctx.set("access-control-allow-origin", "*"); ctx.set("access-control-allow-headers", "x-requested-with"); - // 增加使用数量 - await client.incr("api-next:stat:netease"); - const { id } = ctx.query; if (!id) { @@ -26,6 +23,9 @@ export default Api( } } + // 增加使用数量 + await client.incr("api-next:stat:netease"); + // 直接播放 if ("play" in ctx.query) { ctx.status = 302; diff --git a/src/api/utils/bili.ts b/src/api/utils/bili.ts new file mode 100644 index 0000000..f01fc78 --- /dev/null +++ b/src/api/utils/bili.ts @@ -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, + } +}