}
+ */
+ async getBiliSummary(bvid, cid, up_mid) {
+ // 这个有点用,但不多
+ let wbi = "wts=1701546363&w_rid=1073871926b3ccd99bd790f0162af634"
+ if (!_.isEmpty(this.biliSessData)) {
+ wbi = await getWbi({ bvid, cid, up_mid }, this.biliSessData);
+ }
+ // 构造API
+ const summaryUrl = `${ BILI_SUMMARY }?${ wbi }`;
+ logger.info(summaryUrl)
+ // 构造结果:https://api.bilibili.com/x/web-interface/view/conclusion/get?bvid=BV1L94y1H7CV&cid=1335073288&up_mid=297242063&wts=1701546363&w_rid=1073871926b3ccd99bd790f0162af634
+ return axios.get(summaryUrl)
+ .then(resp => {
+ const data = resp.data.data?.model_result;
+ // logger.info(data)
+ const summary = data?.summary;
+ const outline = data?.outline;
+ let resReply = "";
+ // 总体总结
+ if (summary) {
+ resReply = `摘要:${ summary }\n`
+ }
+ // 分段总结
+ if (outline) {
+ const specificTimeSummary = outline.map(item => {
+ const smallTitle = item.title;
+ const keyPoint = item?.part_outline;
+ // 时间点的总结
+ const specificContent = keyPoint.map(point => {
+ const { timestamp, content } = point
+ const specificTime = secondsToTime(timestamp)
+ return `${ specificTime } ${ content }\n`;
+ }).join("");
+ return `- ${ smallTitle }\n${ specificContent }\n`;
+ });
+ resReply += specificTimeSummary.join("");
+ }
+ return resReply;
+ })
+ }
+
// 百科
async wiki(e) {
const key = e.msg.replace(/#|百科|wiki/g, "").trim();
- const url = `https://xiaoapi.cn/API/bk.php?m=json&type=sg&msg=${encodeURI(key)}`;
- const bdUrl = `https://xiaoapi.cn/API/bk.php?m=json&type=bd&msg=${encodeURI(key)}`;
+ const url = `https://xiaoapi.cn/API/bk.php?m=json&type=sg&msg=${ encodeURI(key) }`;
+ const bdUrl = `https://xiaoapi.cn/API/bk.php?m=json&type=bd&msg=${ encodeURI(key) }`;
const bkRes = await Promise.all([
axios
.get(bdUrl, {
@@ -440,8 +496,8 @@ export class tools extends plugin {
return res.map(item => {
return {
message: `
- 解释:${_.get(item, "msg")}\n
- 详情:${_.get(item, "more")}\n
+ 解释:${ _.get(item, "msg") }\n
+ 详情:${ _.get(item, "more") }\n
`,
nickname: e.sender.card || e.user_id,
user_id: e.user_id,
@@ -473,8 +529,8 @@ export class tools extends plugin {
expansions: ["entities.mentions.username", "attachments.media_keys"],
})
.then(async resp => {
- e.reply(`识别:小蓝鸟学习版,${resp.data.text}`);
- const downloadPath = `${this.defaultPath}${this.e.group_id || this.e.user_id}`;
+ e.reply(`识别:小蓝鸟学习版,${ resp.data.text }`);
+ const downloadPath = `${ this.defaultPath }${ this.e.group_id || this.e.user_id }`;
// 创建文件夹(如果没有过这个群)
if (!fs.existsSync(downloadPath)) {
mkdirsSync(downloadPath);
@@ -489,7 +545,7 @@ export class tools extends plugin {
// 视频
await this.downloadVideo(resp.includes.media[0].variants[0].url, true).then(
_ => {
- e.reply(segment.video(`${downloadPath}/temp.mp4`));
+ e.reply(segment.video(`${ downloadPath }/temp.mp4`));
},
);
}
@@ -524,21 +580,21 @@ export class tools extends plugin {
// acfun解析
async acfun(e) {
- const path = `${this.defaultPath}${this.e.group_id || this.e.user_id}/temp/`;
+ const path = `${ this.defaultPath }${ this.e.group_id || this.e.user_id }/temp/`;
await mkdirIfNotExists(path);
let inputMsg = e.msg;
// 适配手机分享:https://m.acfun.cn/v/?ac=32838812&sid=d2b0991bd6ad9c09
if (inputMsg.includes("m.acfun.cn")) {
- inputMsg = `https://www.acfun.cn/v/ac${/ac=([^&?]*)/.exec(inputMsg)[1]}`;
+ inputMsg = `https://www.acfun.cn/v/ac${ /ac=([^&?]*)/.exec(inputMsg)[1] }`;
}
parseUrl(inputMsg).then(res => {
- e.reply(`识别:猴山,${res.videoName}`);
+ e.reply(`识别:猴山,${ res.videoName }`);
parseM3u8(res.urlM3u8s[res.urlM3u8s.length - 1]).then(res2 => {
downloadM3u8Videos(res2.m3u8FullUrls, path).then(_ => {
- mergeAcFileToMp4(res2.tsNames, path, `${path}out.mp4`).then(_ => {
- e.reply(segment.video(`${path}out.mp4`));
+ mergeAcFileToMp4(res2.tsNames, path, `${ path }out.mp4`).then(_ => {
+ e.reply(segment.video(`${ path }out.mp4`));
});
});
});
@@ -550,9 +606,9 @@ export class tools extends plugin {
async redbook(e) {
// 正则说明:匹配手机链接、匹配小程序、匹配PC链接
let msgUrl =
- /(http:|https:)\/\/(xhslink|xiaohongshu).com\/[A-Za-z\d._?%&+\-=\/#@]*/.exec(
- e.msg,
- )?.[0]
+ /(http:|https:)\/\/(xhslink|xiaohongshu).com\/[A-Za-z\d._?%&+\-=\/#@]*/.exec(
+ e.msg,
+ )?.[0]
|| /(http:|https:)\/\/www\.xiaohongshu\.com\/discovery\/item\/(\w+)/.exec(
e.message[0].data,
)?.[0]
@@ -574,9 +630,9 @@ export class tools extends plugin {
} else {
id = /explore\/(\w+)/.exec(msgUrl)?.[1] || /discovery\/item\/(\w+)/.exec(msgUrl)?.[1];
}
- const downloadPath = `${this.defaultPath}${this.e.group_id || this.e.user_id}`;
+ const downloadPath = `${ this.defaultPath }${ this.e.group_id || this.e.user_id }`;
// 获取信息
- fetch(`https://www.xiaohongshu.com/discovery/item/${id}`, {
+ fetch(`https://www.xiaohongshu.com/discovery/item/${ id }`, {
headers: {
"user-agent":
"Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1 Edg/110.0.0.0",
@@ -589,7 +645,7 @@ export class tools extends plugin {
const res = JSON.parse(resJson.match(reg)[1]);
const noteData = res.noteData.data.noteData;
const { title, desc, type } = noteData;
- e.reply(`识别:小红书, ${title}\n${desc}`);
+ e.reply(`识别:小红书, ${ title }\n${ desc }`);
let imgPromise = [];
if (type === "video") {
const url = noteData.video.url;
@@ -661,9 +717,9 @@ export class tools extends plugin {
}
const rTempFileLen = await deleteFolderRecursive(this.toolsConfig.defaultPath)
e.reply(
- `数据统计:\n`+
- `- 当前清理了${dataDirectory}下总计:${dataClearFileLen} 个垃圾文件\n`+
- `- 当前清理了${ this.toolsConfig.defaultPath}下文件夹:${rTempFileLen} 个群的所有临时文件`
+ `数据统计:\n` +
+ `- 当前清理了${ dataDirectory }下总计:${ dataClearFileLen } 个垃圾文件\n` +
+ `- 当前清理了${ this.toolsConfig.defaultPath }下文件夹:${ rTempFileLen } 个群的所有临时文件`
);
} catch (err) {
logger.error(err);
@@ -677,10 +733,10 @@ export class tools extends plugin {
if (suffix.startsWith("reel")) {
suffix = suffix.replace("reel/", "p/");
}
- const API = `https://imginn.com/${suffix}`;
- logger.info(API);
+ const API = `https://imginn.com/${ suffix }`;
+ // logger.info(API);
let imgPromise = [];
- const downloadPath = `${this.defaultPath}${this.e.group_id || this.e.user_id}`;
+ const downloadPath = `${ this.defaultPath }${ this.e.group_id || this.e.user_id }`;
// 简单封装图片下载
const downloadImg = (url, destination) => {
return new Promise((resolve, reject) => {
@@ -708,13 +764,13 @@ export class tools extends plugin {
const desc = html.match(/(?<=content=").*?(?=\")/g)?.[2];
const images = html.match(//g);
if (!_.isNull(images)) {
- e.reply(`识别:Insta,${desc || "暂无描述"}\n`);
+ e.reply(`识别:Insta,${ desc || "暂无描述" }\n`);
images.map((item, index) => {
const imgUrl = /(?<=data-src=").*?(?=")/
.exec(item)[0]
.replace(/#38/g, "")
.replace(/;/g, "");
- imgPromise.push(downloadImg(imgUrl, `${downloadPath}/${index}.jpg`));
+ imgPromise.push(downloadImg(imgUrl, `${ downloadPath }/${ index }.jpg`));
});
}
// TODO 视频,会出bug暂时不做
@@ -755,18 +811,18 @@ export class tools extends plugin {
/(?=mvId).*?(?=&)/.exec(e.msg.trim())?.[0].replace("mvId=", "");
const { name, album, artist, albumPic120, categorys } = await getBodianMusicInfo(id);
e.reply([
- `识别:波点音乐,${name}-${album}-${artist}\n标签:${categorys
+ `识别:波点音乐,${ name }-${ album }-${ artist }\n标签:${ categorys
.map(item => item.name)
- .join(" | ")}`,
+ .join(" | ") }`,
segment.image(albumPic120),
]);
if (e.msg.includes("musicId")) {
- const path = `${this.defaultPath}${this.e.group_id || this.e.user_id}`;
+ const path = `${ this.defaultPath }${ this.e.group_id || this.e.user_id }`;
await getBodianAudio(id, path).then(_ => {
Bot.acquireGfs(e.group_id).upload(
fs.readFileSync(path + "/temp.mp3"),
"/",
- `${name}-${album}-${artist}.mp3`,
+ `${ name }-${ album }-${ artist }.mp3`,
);
});
} else if (e.msg.includes("mvId")) {
@@ -800,7 +856,7 @@ export class tools extends plugin {
return
}
// 提取视频
- const videoUrl = `https://www.kuaishou.com/short-video/${video_id}`;
+ const videoUrl = `https://www.kuaishou.com/short-video/${ video_id }`;
// 发送GET请求
const response = await axios.get(videoUrl, {
@@ -870,32 +926,10 @@ export class tools extends plugin {
),
),
]).then(data => {
- return mergeFileToMp4(data[0].fullFileName, data[1].fullFileName, `${title}.mp4`);
+ return mergeFileToMp4(data[0].fullFileName, data[1].fullFileName, `${ title }.mp4`);
});
}
- /**
- * 哔哩哔哩总结
- * @returns Promise{string}
- * @param videoInfo
- */
- async getBiliSummary(videoInfo) {
- if (this.biliSessData && this.openaiAccessToken) {
- try {
- const prompt = await getBiliGptInputText(videoInfo, this.biliSessData);
-
- const response = await this.chatGptClient.sendMessage(prompt);
- // 暂时不设计上下文
- return response.response
- } catch (err) {
- logger.error("总结失败,可能是没有弹幕或者网络问题!\n", err);
- return ""
- }
- } else {
- return ""
- }
- }
-
/**
* 下载一张网络图片(自动以url的最后一个为名字)
* @param img
@@ -908,7 +942,7 @@ export class tools extends plugin {
if (fileName === "") {
fileName = img.split("/").pop();
}
- const filepath = `${dir}/${fileName}`;
+ const filepath = `${ dir }/${ fileName }`;
await mkdirIfNotExists(dir)
const writer = fs.createWriteStream(filepath);
const axiosConfig = {
@@ -976,8 +1010,8 @@ export class tools extends plugin {
* @returns {{groupPath: string, target: string}}
*/
getGroupPathAndTarget() {
- const groupPath = `${this.defaultPath}${this.e.group_id || this.e.user_id}`;
- const target = `${groupPath}/temp.mp4`;
+ const groupPath = `${ this.defaultPath }${ this.e.group_id || this.e.user_id }`;
+ const target = `${ groupPath }/temp.mp4`;
return { groupPath, target };
}
@@ -1012,7 +1046,7 @@ export class tools extends plugin {
await checkAndRemoveFile(target);
const res = await axios.get(url, axiosConfig);
- logger.mark(`开始下载: ${url}`);
+ logger.mark(`开始下载: ${ url }`);
const writer = fs.createWriteStream(target);
res.data.pipe(writer);
diff --git a/config/help.yaml b/config/help.yaml
index 9f3188e..d674494 100644
--- a/config/help.yaml
+++ b/config/help.yaml
@@ -38,6 +38,9 @@
- icon: bilibili
title: "bilibili/b23"
desc: 哔哩哔哩分享实时下载
+ - icon: bilimusic
+ title: "bili音乐+链接"
+ desc: 哔哩哔哩音乐分享实时下载
- icon: 推特
title: "小蓝鸟"
desc: 推特学习版分享实时下载
diff --git a/config/tools.yaml b/config/tools.yaml
index 7f2a926..c741a8c 100644
--- a/config/tools.yaml
+++ b/config/tools.yaml
@@ -8,8 +8,4 @@ translateSecret: '' # 百度翻译密匙
biliSessData: '' # 哔哩哔哩的SESSDATA
biliDuration: 480 # 哔哩哔哩限制的最大视频时长(默认8分钟),单位:秒
-openaiAccessToken: '' # 通过获取:https://chat.openai.com/api/auth/session
-openaiApiKey: '' # sk...
-openaiModel: 'gpt-3.5-turbo' # 目前gpt-3.5-turbo效果比较好,廉价,适合群友
-
douyinCookie: '' # douyin's cookie, 格式:odin_tt=xxx;sessionid_ss=xxx;ttwid=xxx;passport_csrf_token=xxx;msToken=xxx;
\ No newline at end of file
diff --git a/config/version.yaml b/config/version.yaml
index 970a54b..8ab169f 100644
--- a/config/version.yaml
+++ b/config/version.yaml
@@ -1,11 +1,11 @@
- {
- version: 1.1.3,
+ version: 1.2.0,
data:
[
+ 新增哔哩哔哩官方AI总结功能,
+ 新增哔哩哔哩音乐提取功能,
新增快手解析功能,
- 新增竹白百科功能,
- 重构翻译功能,
- 适配锅巴插件,方便查看和修改配置,
+ 支持锅巴插件,方便查看和修改配置,
添加#R帮助获取插件帮助,
添加#R版本获取插件版本,
],
diff --git a/guoba.support.js b/guoba.support.js
index 8198d91..5b23af7 100644
--- a/guoba.support.js
+++ b/guoba.support.js
@@ -79,7 +79,7 @@ export function supportGuoba() {
field: "tools.biliSessData",
label: "哔哩哔哩SESSDATA",
bottomHelpMessage:
- "如何获取具体参考我的文档说明:https://gitee.com/kyrzy0416/rconsole-plugin",
+ "如何获取具体参考我的文档说明:https://gitee.com/kyrzy0416/rconsole-plugin#Q&A",
component: "Input",
required: false,
componentProps: {
@@ -97,17 +97,6 @@ export function supportGuoba() {
placeholder: "请输入哔哩哔哩的视频最大限制时长(默认15分钟)",
},
},
- {
- field: "tools.openaiAccessToken",
- label: "OpenAI的AccessToken",
- bottomHelpMessage:
- "ey....,先登录:https://chat.openai.com/,再复制里面的accessToken:https://chat.openai.com/api/auth/session",
- component: "Input",
- required: false,
- componentProps: {
- placeholder: "请输入OpenAI的AccessToken(ey.....)",
- },
- },
{
field: "tools.douyinCookie",
label: "抖音的Cookie",
diff --git a/utils/biliInfo.js b/utils/biliInfo.js
index 1fa3caa..482b3fd 100644
--- a/utils/biliInfo.js
+++ b/utils/biliInfo.js
@@ -1,11 +1,12 @@
import fetch from "node-fetch";
import axios from "axios";
+import { BILI_VIDEO_INFO } from "../constants/bili.js";
async function getVideoInfo(url) {
- const baseVideoInfo = "http://api.bilibili.com/x/web-interface/view";
+ // const baseVideoInfo = "http://api.bilibili.com/x/web-interface/view";
const videoId = /video\/[^\?\/ ]+/.exec(url)[0].split("/")[1];
// 获取视频信息,然后发送
- return fetch(`${baseVideoInfo}?bvid=${videoId}`)
+ return fetch(`${BILI_VIDEO_INFO}?bvid=${videoId}`)
.then(async resp => {
const respJson = await resp.json();
const respData = respJson.data;
@@ -16,8 +17,10 @@ async function getVideoInfo(url) {
duration: respData.duration,
dynamic: respJson.data.dynamic,
stat: respData.stat,
+ bvid: respData.bvid,
aid: respData.aid,
cid: respData.pages?.[0].cid,
+ owner: respData.owner,
pages: respData?.pages,
};
});
diff --git a/utils/biliSummary.js b/utils/biliSummary.js
deleted file mode 100644
index 73fe4a9..0000000
--- a/utils/biliSummary.js
+++ /dev/null
@@ -1,208 +0,0 @@
-/**
- * 获取gpt提取视频信息的文字
- * @param videoInfo
- * @param biliSessData
- * @param shouldShowTimestamp 是否在每段字幕前面加入时间标识
- * @returns {Promise}
- */
-export async function getBiliGptInputText(videoInfo, biliSessData, shouldShowTimestamp = false) {
- const headers = {
- Accept: "application/json",
- "Content-Type": "application/json",
- "User-Agent":
- "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36",
- Host: "api.bilibili.com",
- Cookie: `SESSDATA=${biliSessData}`,
- };
- const commonConfig = {
- method: "GET",
- cache: "no-cache",
- headers,
- referrerPolicy: "no-referrer",
- };
- const { title, desc, dynamic, aid, cid } = videoInfo;
- // https://api.bilibili.com/x/player/v2?aid=438937138&cid=1066979272
- const resp = await fetch(
- `https://api.bilibili.com/x/player/v2?aid=${aid}&cid=${cid}`,
- commonConfig,
- );
- const subtitles = (await resp.json()).data.subtitle.subtitles;
- const subtitlesUrl = subtitles?.subtitle_url?.startsWith("//")
- ? `https:${subtitles?.subtitle_url}`
- : subtitles?.subtitle_url;
- let inputText = "";
- logger.mark(subtitlesUrl);
- if (subtitlesUrl !== undefined) {
- const res = await fetch(subtitlesUrl);
- const subtitlesData = (await res.json()).body;
- const subtitleTimestamp = reduceBilibiliSubtitleTimestamp(
- subtitlesData,
- shouldShowTimestamp,
- );
- inputText = getSmallSizeTranscripts(subtitleTimestamp, subtitleTimestamp);
- } else {
- inputText = `${desc} ${dynamic}`;
- }
- const videoConfig = {
- showEmoji: true,
- };
- return shouldShowTimestamp
- ? getUserSubtitleWithTimestampPrompt(title, inputText, videoConfig)
- : getUserSubtitlePrompt(title, inputText, videoConfig);
-}
-
-// 以下拼接算法来自:https://github.com/JimmyLv/BibiGPT
-function reduceBilibiliSubtitleTimestamp(subtitles = [], shouldShowTimestamp) {
- return reduceSubtitleTimestamp(
- subtitles,
- i => i.from,
- i => i.content,
- shouldShowTimestamp,
- );
-}
-function reduceSubtitleTimestamp(subtitles, getStart, getText, shouldShowTimestamp) {
- // 把字幕数组总共分成 20 组
- const TOTAL_GROUP_COUNT = 30;
- // 如果字幕不够多,就每7句话合并一下
- const MINIMUM_COUNT_ONE_GROUP = 7;
- const eachGroupCount =
- subtitles.length > TOTAL_GROUP_COUNT
- ? subtitles.length / TOTAL_GROUP_COUNT
- : MINIMUM_COUNT_ONE_GROUP;
-
- return subtitles.reduce((accumulator, current, index) => {
- // 计算当前元素在哪一组
- const groupIndex = Math.floor(index / MINIMUM_COUNT_ONE_GROUP);
-
- // 如果是当前组的第一个元素,初始化这一组的字符串
- if (!accumulator[groupIndex]) {
- accumulator[groupIndex] = {
- // 5.88 -> 5.9
- // text: current.start.toFixed() + ": ",
- index: groupIndex,
- s: getStart(current),
- text: shouldShowTimestamp ? getStart(current) + " - " : "",
- };
- }
-
- // 将当前元素添加到当前组的字符串末尾
- accumulator[groupIndex].text = accumulator[groupIndex].text + getText(current) + " ";
-
- return accumulator;
- }, []);
-}
-
-function getSmallSizeTranscripts(newTextData, oldTextData, byteLimit = 6200) {
- const text = newTextData
- .sort((a, b) => a.index - b.index)
- .map(t => t.text)
- .join(" ");
- const byteLength = getByteLength(text);
-
- if (byteLength > byteLimit) {
- const filtedData = filterHalfRandomly(newTextData);
- return getSmallSizeTranscripts(filtedData, oldTextData, byteLimit);
- }
-
- let resultData = newTextData.slice();
- let resultText = text;
- let lastByteLength = byteLength;
-
- for (let i = 0; i < oldTextData.length; i++) {
- const obj = oldTextData[i];
- if (itemInIt(newTextData, obj.text)) {
- continue;
- }
-
- const nextTextByteLength = getByteLength(obj.text);
- const isOverLimit = lastByteLength + nextTextByteLength > byteLimit;
- if (isOverLimit) {
- const overRate = (lastByteLength + nextTextByteLength - byteLimit) / nextTextByteLength;
- const chunkedText = obj.text.substring(0, Math.floor(obj.text.length * overRate));
- resultData.push({ text: chunkedText, index: obj.index });
- } else {
- resultData.push(obj);
- }
- resultText = resultData
- .sort((a, b) => a.index - b.index)
- .map(t => t.text)
- .join(" ");
- lastByteLength = getByteLength(resultText);
- }
-
- return resultText;
-}
-
-function filterHalfRandomly(arr) {
- const filteredArr = [];
- const halfLength = Math.floor(arr.length / 2);
- const indicesToFilter = new Set();
-
- // 随机生成要过滤掉的元素的下标
- while (indicesToFilter.size < halfLength) {
- const index = Math.floor(Math.random() * arr.length);
- if (!indicesToFilter.has(index)) {
- indicesToFilter.add(index);
- }
- }
-
- // 过滤掉要过滤的元素
- for (let i = 0; i < arr.length; i++) {
- if (!indicesToFilter.has(i)) {
- filteredArr.push(arr[i]);
- }
- }
-
- return filteredArr;
-}
-
-function getByteLength(text) {
- return unescape(encodeURIComponent(text)).length;
-}
-
-function itemInIt(textData, text) {
- return textData.find(t => t.text === text) !== undefined;
-}
-
-function getUserSubtitlePrompt(title, transcript, videoConfig) {
- const videoTitle = title?.replace(/\n+/g, " ").trim();
- const videoTranscript = limitTranscriptByteLength(transcript).replace(/\n+/g, " ").trim();
- const language = "zh-CN";
- const sentenceCount = videoConfig.sentenceNumber || 7;
- const emojiTemplateText = videoConfig.showEmoji ? "[Emoji] " : "";
- const emojiDescriptionText = videoConfig.showEmoji
- ? "Choose an appropriate emoji for each bullet point. "
- : "";
- const shouldShowAsOutline = Number(videoConfig.outlineLevel) > 1;
- const wordsCount = videoConfig.detailLevel ? (Number(videoConfig.detailLevel) / 100) * 2 : 15;
- const outlineTemplateText = shouldShowAsOutline ? `\n - Child points` : "";
- const outlineDescriptionText = shouldShowAsOutline
- ? `Use the outline list, which can have a hierarchical structure of up to ${videoConfig.outlineLevel} levels. `
- : "";
- const prompt = `Your output should use the following template:\n## Summary\n## Highlights\n- ${emojiTemplateText}Bulletpoint${outlineTemplateText}\n\nYour task is to summarise the text I have given you in up to ${sentenceCount} concise bullet points, starting with a short highlight, each bullet point is at least ${wordsCount} words. ${outlineDescriptionText}${emojiDescriptionText}Use the text above: {{Title}} {{Transcript}}.\n\nReply in ${language} Language.`;
-
- return `Title: "${videoTitle}"\nTranscript: "${videoTranscript}"\n\nInstructions: ${prompt}`;
-}
-
-export function getUserSubtitleWithTimestampPrompt(title, transcript, videoConfig) {
- const videoTitle = title?.replace(/\n+/g, " ").trim();
- const videoTranscript = limitTranscriptByteLength(transcript).replace(/\n+/g, " ").trim();
- const language = "zh-CN";
- const sentenceCount = videoConfig.sentenceNumber || 7;
- const emojiTemplateText = videoConfig.showEmoji ? "[Emoji] " : "";
- const wordsCount = videoConfig.detailLevel ? (Number(videoConfig.detailLevel) / 100) * 2 : 15;
- const promptWithTimestamp = `Act as the author and provide exactly ${sentenceCount} bullet points for the text transcript given in the format [seconds] - [text] \nMake sure that:\n - Please start by summarizing the whole video in one short sentence\n - Then, please summarize with each bullet_point is at least ${wordsCount} words\n - each bullet_point start with \"- \" or a number or a bullet point symbol\n - each bullet_point should has the start timestamp, use this template: - seconds - ${emojiTemplateText}[bullet_point]\n - there may be typos in the subtitles, please correct them\n - Reply all in ${language} Language.`;
- const videoTranscripts = limitTranscriptByteLength(JSON.stringify(videoTranscript));
- return `Title: ${videoTitle}\nTranscript: ${videoTranscripts}\n\nInstructions: ${promptWithTimestamp}`;
-}
-
-function limitTranscriptByteLength(str, byteLimit = 6200) {
- const utf8str = unescape(encodeURIComponent(str));
- const byteLength = utf8str.length;
- if (byteLength > byteLimit) {
- const ratio = byteLimit / byteLength;
- const newStr = str.substring(0, Math.floor(str.length * ratio));
- return newStr;
- }
- return str;
-}
diff --git a/utils/bilibili.js b/utils/bilibili.js
index 0e2bcde..431f0a3 100644
--- a/utils/bilibili.js
+++ b/utils/bilibili.js
@@ -65,6 +65,33 @@ async function getDownloadUrl (url) {
});
}
+async function getAudioUrl (url) {
+ return axios
+ .get(url, {
+ headers: {
+ 'User-Agent':
+ 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 Safari/537.36',
+ referer: 'https://www.bilibili.com',
+ },
+ })
+ .then(({ data }) => {
+ const info = JSON.parse(
+ data.match(/