mirror of
https://github.com/ChatGPTNextWeb/ChatGPT-Next-Web.git
synced 2025-05-24 22:50:22 +09:00
123 lines
3.5 KiB
TypeScript
123 lines
3.5 KiB
TypeScript
import { Tool } from "@langchain/core/tools";
|
|
import { getRandomUserAgent } from "./ua_tools";
|
|
import { encWbi, getWbiKeys } from "./bili_wbi_tools";
|
|
|
|
export interface Headers {
|
|
[key: string]: string;
|
|
}
|
|
|
|
export interface RequestTool {
|
|
headers: Headers;
|
|
maxOutputLength?: number;
|
|
timeout: number;
|
|
}
|
|
|
|
export class BilibiliVideoSearchTool extends Tool implements RequestTool {
|
|
name = "bilibili_video_search";
|
|
|
|
maxOutputLength = Infinity;
|
|
|
|
timeout = 10000;
|
|
|
|
constructor(
|
|
public headers: Headers = {},
|
|
{ maxOutputLength }: { maxOutputLength?: number } = {},
|
|
{ timeout }: { timeout?: number } = {},
|
|
) {
|
|
super(...arguments);
|
|
|
|
this.maxOutputLength = maxOutputLength ?? this.maxOutputLength;
|
|
this.timeout = timeout ?? this.timeout;
|
|
}
|
|
|
|
/** @ignore */
|
|
async _call(searchQuery: string) {
|
|
try {
|
|
let result = await this.searchFromBilibili(searchQuery);
|
|
// console.log(result)
|
|
return JSON.stringify(result);
|
|
} catch (error) {
|
|
console.error(error);
|
|
return (error as Error).toString();
|
|
}
|
|
}
|
|
|
|
async searchFromBilibili(searchQuery: string, searchType: string = "video") {
|
|
const headers = new Headers();
|
|
headers.append("User-Agent", getRandomUserAgent());
|
|
headers.append(
|
|
"Referer",
|
|
"https://search.bilibili.com/all?keyword=" +
|
|
encodeURIComponent(searchQuery),
|
|
);
|
|
headers.append("Origin", "https://search.bilibili.com");
|
|
headers.append("Cookie", process.env.BILIBILI_COOKIES || "");
|
|
const { img_key, sub_key } = await getWbiKeys();
|
|
const queryString = encWbi(
|
|
{
|
|
keyword: searchQuery.trim(),
|
|
search_type: searchType,
|
|
},
|
|
img_key,
|
|
sub_key,
|
|
);
|
|
const url = `https://api.bilibili.com/x/web-interface/wbi/search/type?${queryString}`;
|
|
|
|
const resp = await this.fetchWithTimeout(url, {
|
|
headers: headers,
|
|
});
|
|
|
|
let rawData: { [key: string]: any } = await resp.json();
|
|
console.log("[BilibiliVideoSearchTool]", rawData);
|
|
let data: Array<{
|
|
title: string;
|
|
url: string;
|
|
authorName: string;
|
|
authorMid: number;
|
|
viewCount: number;
|
|
durationSeconds: number;
|
|
}> = [];
|
|
|
|
rawData.data.result.forEach((element: { [key: string]: any }) => {
|
|
const rawTitle = element.title;
|
|
const title = rawTitle.replace(/<[^>]+>/g, ""); // remove HTML tags
|
|
const url = `https://www.bilibili.com/video/${element.bvid}`;
|
|
const authorName = element.author;
|
|
const authorMid = element.mid;
|
|
const viewCount = element.play;
|
|
const durationHHMM = element.duration;
|
|
var durationSeconds = 0;
|
|
durationHHMM.split(":").forEach((timetag: string) => {
|
|
durationSeconds = durationSeconds * 60 + parseInt(timetag);
|
|
});
|
|
data.push({
|
|
title,
|
|
url,
|
|
authorName,
|
|
authorMid,
|
|
viewCount,
|
|
durationSeconds,
|
|
});
|
|
});
|
|
console.log("[BilibiliVideoSearchTool]", data);
|
|
return data;
|
|
}
|
|
|
|
async fetchWithTimeout(
|
|
resource: RequestInfo | URL,
|
|
options = {},
|
|
timeout: number = 30000,
|
|
) {
|
|
const controller = new AbortController();
|
|
const id = setTimeout(() => controller.abort(), timeout);
|
|
const response = await fetch(resource, {
|
|
...options,
|
|
signal: controller.signal,
|
|
});
|
|
clearTimeout(id);
|
|
return response;
|
|
}
|
|
|
|
description = `A tool that searches for videos on Bilibili. Input string is the search query (use Chinese characters for better results in most cases). Output is a list of video titles and URLs.`;
|
|
}
|