mirror of
https://github.com/BTMuli/TeyvatGuide.git
synced 2026-04-22 21:59:49 +08:00
♻️ 重构Bili插件请求逻辑,完善错误处理
This commit is contained in:
@@ -39,6 +39,8 @@ import showSnackbar from "@comp/func/snackbar.js";
|
||||
import { saveImgBlob } from "@utils/TGShare.js";
|
||||
import { getVideoDuration, timestampToDate } from "@utils/toolFunc.js";
|
||||
import { onMounted, onUnmounted, ref, shallowRef } from "vue";
|
||||
import TGHttps from "@utils/TGHttps.js";
|
||||
import TGLogger from "@utils/TGLogger.js";
|
||||
|
||||
type TpVideo = { insert: { video: string } };
|
||||
type TpVideoProps = { data: TpVideo };
|
||||
@@ -47,7 +49,7 @@ const props = defineProps<TpVideoProps>();
|
||||
const videoAspectRatio = ref<number>(16 / 9);
|
||||
const videoCover = ref<string>();
|
||||
const videoCoverLink = ref<string>();
|
||||
const videoData = shallowRef<TGApp.Plugins.Bili.Video.ViewData>();
|
||||
const videoData = shallowRef<TGApp.Plugins.Bili.Video.ViewRes>();
|
||||
|
||||
console.log("tpVideo", props.data.insert.video);
|
||||
|
||||
@@ -55,9 +57,17 @@ onMounted(async () => {
|
||||
const url = new URL(props.data.insert.video);
|
||||
const aid = url.searchParams.get("aid") ?? undefined;
|
||||
const bvid = url.searchParams.get("bvid") ?? undefined;
|
||||
videoData.value = await Bili.video.view(aid, bvid);
|
||||
if (!videoData.value) {
|
||||
showSnackbar.error(`获取B站视频信息失败:${props.data.insert.video}`);
|
||||
try {
|
||||
const viewResp = await Bili.video.view(aid, bvid);
|
||||
videoData.value = viewResp.data;
|
||||
} catch (e) {
|
||||
let errMsg = String(e);
|
||||
if (TGHttps.isHttpErr(e)) {
|
||||
errMsg = e.status ? `[${e.status}] ${e.statusText}` : e.message;
|
||||
}
|
||||
showSnackbar.error(`获取视频信息失败: ${errMsg}`);
|
||||
await TGLogger.Error(`[TpVideo][onMounted] 获取视频信息异常: ${url}`);
|
||||
await TGLogger.Error(`[TpVideo][onMounted] ${e}`);
|
||||
return;
|
||||
}
|
||||
console.log(`videoData ${props.data.insert.video}`, videoData.value);
|
||||
@@ -74,7 +84,7 @@ onUnmounted(() => {
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<style lang="css" scoped>
|
||||
<style lang="scss" scoped>
|
||||
.tp-video-box {
|
||||
position: relative;
|
||||
max-width: 100%;
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
/**
|
||||
* Bili 插件导航请求文件
|
||||
* @since Beta v0.5.7
|
||||
*/
|
||||
|
||||
import headerBili from "@Bili/utils/getHeader.js";
|
||||
import TGHttp from "@utils/TGHttp.js";
|
||||
|
||||
/**
|
||||
* Bili 插件导航请求
|
||||
* @since Beta v0.5.7
|
||||
* @returns Bili 插件导航请求返回
|
||||
*/
|
||||
async function getNav(): Promise<TGApp.Plugins.Bili.Nav.Data> {
|
||||
const url = "https://api.bilibili.com/x/web-interface/nav";
|
||||
const resp = await TGHttp<TGApp.Plugins.Bili.Nav.Response>(url, {
|
||||
method: "GET",
|
||||
headers: headerBili,
|
||||
});
|
||||
return resp.data;
|
||||
}
|
||||
|
||||
export default getNav;
|
||||
@@ -1,29 +1,29 @@
|
||||
/**
|
||||
* Bili 插件视频请求文件
|
||||
* @since Beta v0.5.0
|
||||
* @since Beta v0.10.0
|
||||
*/
|
||||
|
||||
import headerBili from "@Bili/utils/getHeader.js";
|
||||
import getWrid from "@Bili/utils/getWrid.js";
|
||||
import TGHttp from "@utils/TGHttp.js";
|
||||
import { BILI_HEADER, getWrid } from "../utils.js";
|
||||
import TGHttps from "@utils/TGHttps.js";
|
||||
|
||||
/**
|
||||
* 获取视频播放地址
|
||||
* @since Beta v0.5.0
|
||||
* @since Beta v0.10.0
|
||||
* @todo 完善参数类型
|
||||
* @see https://socialsisteryi.github.io/bilibili-API-collect/docs/video/videostream_url.html#dash%E6%A0%BC%E5%BC%8F
|
||||
* @param bvid - 视频BV号
|
||||
* @param cid - 视频分P号
|
||||
* @returns 视频播放地址
|
||||
*/
|
||||
async function getVideoUrl(cid: number, bvid: string): Promise<TGApp.Plugins.Bili.Video.UrlData> {
|
||||
async function getVideoUrl(cid: number, bvid: string): Promise<TGApp.Plugins.Bili.Video.UrlResp> {
|
||||
const url = "https://api.bilibili.com/x/player/playurl";
|
||||
let params: Record<string, string> = { bvid, cid: cid.toString(), fnval: "16", platform: "pc" };
|
||||
const wridRes = await getWrid(params);
|
||||
params = { ...params, wts: wridRes[0], w_rid: wridRes[1] };
|
||||
const resp = await TGHttp<TGApp.Plugins.Bili.Video.UrlResponse>(url, {
|
||||
const [wts, w_rid] = await getWrid(params);
|
||||
params = { ...params, wts: wts, w_rid: w_rid };
|
||||
const resp = await TGHttps.get<TGApp.Plugins.Bili.Video.UrlResp>(url, {
|
||||
method: "GET",
|
||||
query: params,
|
||||
headers: headerBili,
|
||||
headers: BILI_HEADER,
|
||||
});
|
||||
return resp.data;
|
||||
}
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
/**
|
||||
* Bili插件-获取视频基本信息
|
||||
* @since Beta v0.5.0
|
||||
* @since Beta v0.10.0
|
||||
*/
|
||||
|
||||
import headerBili from "@Bili/utils/getHeader.js";
|
||||
import getWrid from "@Bili/utils/getWrid.js";
|
||||
import TGHttp from "@utils/TGHttp.js";
|
||||
import TGLogger from "@utils/TGLogger.js";
|
||||
import { BILI_HEADER, getWrid } from "../utils.js";
|
||||
import TGHttps from "@utils/TGHttps.js";
|
||||
|
||||
/**
|
||||
* 获取视频基本信息
|
||||
* @since Beta v0.5.0
|
||||
* @since Beta v0.10.0
|
||||
* @todo 完善参数类型
|
||||
* @param aid - 视频 AV 号
|
||||
* @param bvid - 视频 BV 号
|
||||
* @returns 视频基本信息
|
||||
@@ -18,26 +17,19 @@ import TGLogger from "@utils/TGLogger.js";
|
||||
async function getVideoView(
|
||||
aid?: string,
|
||||
bvid?: string,
|
||||
): Promise<TGApp.Plugins.Bili.Video.ViewData> {
|
||||
): Promise<TGApp.Plugins.Bili.Video.ViewResp> {
|
||||
const url = "https://api.bilibili.com/x/web-interface/wbi/view";
|
||||
let params: Record<string, string | number | boolean> = { need_view: 1, isGaiaAvoided: true };
|
||||
if (aid) params.aid = aid;
|
||||
if (bvid) params.bvid = bvid;
|
||||
if (!aid && !bvid) throw new Error("aid和bVid不能同时为空");
|
||||
const wrid = await getWrid(params);
|
||||
params = { ...params, wts: wrid[0], w_rid: wrid[1] };
|
||||
try {
|
||||
const resp = await TGHttp<TGApp.Plugins.Bili.Video.ViewResponse>(url, {
|
||||
method: "GET",
|
||||
query: params,
|
||||
headers: headerBili,
|
||||
});
|
||||
return resp.data;
|
||||
} catch (error) {
|
||||
if (error instanceof Error) await TGLogger.Error(`获取视频基本信息失败: ${error.message}`);
|
||||
else await TGLogger.Error(`获取视频基本信息失败: ${error}`);
|
||||
}
|
||||
throw new Error("获取视频基本信息失败");
|
||||
const [wts, w_rid] = await getWrid(params);
|
||||
params = { ...params, wts: wts, w_rid: w_rid };
|
||||
const resp = await TGHttps.get<TGApp.Plugins.Bili.Video.ViewResp>(url, {
|
||||
query: params,
|
||||
headers: BILI_HEADER,
|
||||
});
|
||||
return resp.data;
|
||||
}
|
||||
|
||||
export default getVideoView;
|
||||
|
||||
7
src/plugins/Bili/types/Base.d.ts
vendored
7
src/plugins/Bili/types/Base.d.ts
vendored
@@ -7,7 +7,6 @@ declare namespace TGApp.Plugins.Bili.Base {
|
||||
/**
|
||||
* Bili Response 统一接口
|
||||
* @since Beta v0.4.0
|
||||
* @returns 响应结构体
|
||||
*/
|
||||
type Resp<T = unknown> = {
|
||||
/** 状态码 */
|
||||
@@ -19,4 +18,10 @@ declare namespace TGApp.Plugins.Bili.Base {
|
||||
/** 数据 */
|
||||
data: T;
|
||||
};
|
||||
|
||||
/**
|
||||
* Wrid返回
|
||||
* @since Beta v0.10.0
|
||||
*/
|
||||
type WridArrRes = Array<string> & { length: 2 };
|
||||
}
|
||||
|
||||
8
src/plugins/Bili/types/Nav.d.ts
vendored
8
src/plugins/Bili/types/Nav.d.ts
vendored
@@ -6,17 +6,17 @@
|
||||
declare namespace TGApp.Plugins.Bili.Nav {
|
||||
/**
|
||||
* Bili 导航基本信息返回
|
||||
* @since Beta v0.5.7
|
||||
* @since Beta v0.10.0
|
||||
*/
|
||||
type Response = TGApp.Plugins.Bili.Base.Resp<Data>;
|
||||
type Resp = TGApp.Plugins.Bili.Base.Resp<Res>;
|
||||
|
||||
/**
|
||||
* Bili 导航基本信息
|
||||
* @since Beta v0.5.7
|
||||
* @since Beta v0.10.0
|
||||
* @see https://api.bilibili.com/x/web-interface/nav
|
||||
* @remarks 只写了用到的部分
|
||||
*/
|
||||
type Data = {
|
||||
type Res = {
|
||||
/** 网站图标 */
|
||||
wbi_img: {
|
||||
/** 网站图标 */
|
||||
|
||||
8
src/plugins/Bili/types/Video.d.ts
vendored
8
src/plugins/Bili/types/Video.d.ts
vendored
@@ -8,14 +8,14 @@ declare namespace TGApp.Plugins.Bili.Video {
|
||||
* Bili 视频基本信息返回
|
||||
* @since Beta v0.4.0
|
||||
*/
|
||||
type ViewResponse = TGApp.Plugins.Bili.Base.Resp<ViewData>;
|
||||
type ViewResp = TGApp.Plugins.Bili.Base.Resp<ViewRes>;
|
||||
|
||||
/**
|
||||
* Bili 视频基本信息
|
||||
* @since Beta v0.4.0
|
||||
* @see https://api.bilibili.com/x/web-interface/view?aid=540893019
|
||||
*/
|
||||
type ViewData = {
|
||||
type ViewRes = {
|
||||
/** 视频 BV 号 */
|
||||
bvid: string;
|
||||
/** 视频 AV 号 */
|
||||
@@ -150,13 +150,13 @@ declare namespace TGApp.Plugins.Bili.Video {
|
||||
* Bili 视频链接返回
|
||||
* @since Beta v0.4.0
|
||||
*/
|
||||
type UrlResponse = TGApp.Plugins.Bili.Base.Resp<UrlData>;
|
||||
type UrlResp = TGApp.Plugins.Bili.Base.Resp<UrlRes>;
|
||||
|
||||
/**
|
||||
* Bili 视频播放地址返回数据
|
||||
* @since Beta v0.4.1
|
||||
*/
|
||||
type UrlData = {
|
||||
type UrlRes = {
|
||||
/** 视频来源 */
|
||||
from: string;
|
||||
/** 视频播放地址 */
|
||||
|
||||
96
src/plugins/Bili/utils.ts
Normal file
96
src/plugins/Bili/utils.ts
Normal file
@@ -0,0 +1,96 @@
|
||||
/**
|
||||
* Bili 插件工具函数
|
||||
* @since Beta v0.10.0
|
||||
*/
|
||||
import TGHttps from "@utils/TGHttps.js";
|
||||
import md5 from "js-md5";
|
||||
import TGLogger from "@utils/TGLogger.js";
|
||||
|
||||
export const BILI_HEADER: Readonly<Record<string, string>> = {
|
||||
cookie: "",
|
||||
"user-agent": "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/115.0",
|
||||
origin: "https://www.bilibili.com",
|
||||
};
|
||||
|
||||
/**
|
||||
* Bili 插件导航请求
|
||||
* @since Beta v0.10.0
|
||||
* @returns Bili 插件导航请求返回
|
||||
*/
|
||||
async function getNavResp(): Promise<TGApp.Plugins.Bili.Nav.Resp> {
|
||||
const url = "https://api.bilibili.com/x/web-interface/nav";
|
||||
const resp = await TGHttps.get<TGApp.Plugins.Bili.Nav.Resp>(url, {
|
||||
method: "GET",
|
||||
headers: BILI_HEADER,
|
||||
});
|
||||
return resp.data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 key 值
|
||||
* @since Beta v0.4.0
|
||||
* @param key - key 名称
|
||||
* @returns key 值
|
||||
*/
|
||||
function getKeyVal(key: string): string {
|
||||
return key.split("/").pop()?.split(".")[0] ?? "";
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 mixin_key
|
||||
* @since Beta v0.10.0
|
||||
* @returns mixin_key
|
||||
*/
|
||||
async function getMixinKey(): Promise<string> {
|
||||
let navResp: TGApp.Plugins.Bili.Nav.Resp;
|
||||
try {
|
||||
navResp = await getNavResp();
|
||||
} catch (e) {
|
||||
let errMsg = String(e);
|
||||
if (TGHttps.isHttpErr(e)) {
|
||||
errMsg = e.status ? `[${e.status}] ${e.statusText}` : e.message;
|
||||
}
|
||||
await TGLogger.Error(`[Bili][GetMixinKey] Nav 请求异常:${errMsg}`);
|
||||
return "";
|
||||
}
|
||||
if (!navResp) return "";
|
||||
if (navResp.code !== 0) {
|
||||
await TGLogger.Warn(`[Bili][GetMixinKey] Nav 返回异常: ${navResp.code}-${navResp.message}`);
|
||||
return "";
|
||||
}
|
||||
const key1 = getKeyVal(navResp.data.wbi_img.img_url);
|
||||
const key2 = getKeyVal(navResp.data.wbi_img.sub_url);
|
||||
const key = key1 + key2;
|
||||
const MIXIN_KEY_ENC_TAB = [
|
||||
46, 47, 18, 2, 53, 8, 23, 32, 15, 50, 10, 31, 58, 3, 45, 35, 27, 43, 5, 49, 33, 9, 42, 19, 29,
|
||||
28, 14, 39, 12, 38, 41, 13, 37, 48, 7, 16, 24, 55, 40, 61, 26, 17, 0, 1, 60, 51, 30, 4, 22, 25,
|
||||
54, 21, 56, 59, 6, 63, 57, 62, 11, 36, 20, 34, 44, 52,
|
||||
];
|
||||
const res = [];
|
||||
for (const i of MIXIN_KEY_ENC_TAB) res.push(key[i]);
|
||||
return res.join("").slice(0, 32);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 wrid
|
||||
* @since Beta v0.4.1
|
||||
* @param params - 请求参数
|
||||
* @returns wrid
|
||||
*/
|
||||
export async function getWrid(
|
||||
params: Record<string, string | number | boolean>,
|
||||
): Promise<TGApp.Plugins.Bili.Base.WridArrRes> {
|
||||
const mixin_key = await getMixinKey();
|
||||
if (mixin_key === "") return ["", ""];
|
||||
const wts = Math.floor(Date.now() / 1000);
|
||||
const obj: Record<string, string | number> = { ...params, wts };
|
||||
const keys = Object.keys(obj).sort();
|
||||
let md5Str = "";
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
const key = keys[i];
|
||||
if (i === keys.length - 1) md5Str += `${key}=${obj[key]}`;
|
||||
else md5Str += `${key}=${obj[key]}&`;
|
||||
}
|
||||
const wrid = md5.md5(`${md5Str}${mixin_key}`);
|
||||
return [wts.toString(), wrid];
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
/**
|
||||
* 获取请求头
|
||||
* @since Beta v0.5.7
|
||||
*/
|
||||
|
||||
const headerBili = {
|
||||
cookie: "",
|
||||
"user-agent": "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/115.0",
|
||||
origin: "https://www.bilibili.com",
|
||||
};
|
||||
|
||||
export default headerBili;
|
||||
@@ -1,62 +0,0 @@
|
||||
/**
|
||||
* Bili 插件获取 wrid 工具函数
|
||||
* @since Beta v0.4.1
|
||||
*/
|
||||
|
||||
import getNav from "@Bili/request/getNav.js";
|
||||
import md5 from "js-md5";
|
||||
|
||||
/**
|
||||
* 获取 key 值
|
||||
* @since Beta v0.4.0
|
||||
* @param key - key 名称
|
||||
* @returns key 值
|
||||
*/
|
||||
function getKeyVal(key: string): string {
|
||||
return key.split("/").pop()?.split(".")[0] ?? "";
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 mixin_key
|
||||
* @since Beta v0.4.1
|
||||
* @returns mixin_key
|
||||
*/
|
||||
async function getMixinKey(): Promise<string> {
|
||||
const nav = await getNav();
|
||||
const key1 = getKeyVal(nav.wbi_img.img_url);
|
||||
const key2 = getKeyVal(nav.wbi_img.sub_url);
|
||||
const key = key1 + key2;
|
||||
const MIXIN_KEY_ENC_TAB = [
|
||||
46, 47, 18, 2, 53, 8, 23, 32, 15, 50, 10, 31, 58, 3, 45, 35, 27, 43, 5, 49, 33, 9, 42, 19, 29,
|
||||
28, 14, 39, 12, 38, 41, 13, 37, 48, 7, 16, 24, 55, 40, 61, 26, 17, 0, 1, 60, 51, 30, 4, 22, 25,
|
||||
54, 21, 56, 59, 6, 63, 57, 62, 11, 36, 20, 34, 44, 52,
|
||||
];
|
||||
const res = [];
|
||||
for (const i of MIXIN_KEY_ENC_TAB) res.push(key[i]);
|
||||
return res.join("").slice(0, 32);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 wrid
|
||||
* @since Beta v0.4.1
|
||||
* @param params - 请求参数
|
||||
* @returns wrid
|
||||
*/
|
||||
async function getWrid(
|
||||
params: Record<string, string | number | boolean>,
|
||||
): Promise<[string, string]> {
|
||||
const mixin_key = await getMixinKey();
|
||||
const wts = Math.floor(Date.now() / 1000);
|
||||
const obj: Record<string, string | number> = { ...params, wts };
|
||||
const keys = Object.keys(obj).sort();
|
||||
let md5Str = "";
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
const key = keys[i];
|
||||
if (i === keys.length - 1) md5Str += `${key}=${obj[key]}`;
|
||||
else md5Str += `${key}=${obj[key]}&`;
|
||||
}
|
||||
const wrid = md5.md5(`${md5Str}${mixin_key}`);
|
||||
return [wts.toString(), wrid];
|
||||
}
|
||||
|
||||
export default getWrid;
|
||||
Reference in New Issue
Block a user