mirror of
https://github.com/Jerryplusy/rc-plugin.git
synced 2025-10-14 16:19:18 +00:00
✨ feat: 新增哔哩哔哩官方AI总结、哔哩哔哩音乐提取
1. 新增哔哩哔哩音乐提取,使用“bili音乐+链接”即可提取视频中的音乐 2. 重构部分代码 3. 更换tiktok API 4. 更换视频总结方式GPT为官方的视频摘要(免费使用) 5. 删除GPT相关内容
This commit is contained in:
parent
444830003c
commit
d0e6e6e5bd
38
README.md
38
README.md
@ -61,7 +61,7 @@ sudo apt-get install ffmpeg
|
|||||||
|
|
||||||
<img src="./img/example6.webp" alt="小程序解析" width="50%" height="50%" />
|
<img src="./img/example6.webp" alt="小程序解析" width="50%" height="50%" />
|
||||||
|
|
||||||
5. 【可选】对哔哩哔哩解析进行总结:需要填写accessToken和哔哩哔哩的SESSDATA
|
5. 【可选】对哔哩哔哩解析进行总结:需要填写哔哩哔哩的SESSDATA
|
||||||
|
|
||||||
<img src="./img/example7.webp" alt="小程序解析" width="50%" height="50%" />
|
<img src="./img/example7.webp" alt="小程序解析" width="50%" height="50%" />
|
||||||
|
|
||||||
@ -70,10 +70,28 @@ sudo apt-get install ffmpeg
|
|||||||
- 锅巴设置
|
- 锅巴设置
|
||||||
|
|
||||||
## 🐤 Q&A
|
## 🐤 Q&A
|
||||||
|
### bilibili问题
|
||||||
> 哔哩哔哩的SESSDATA?
|
> 哔哩哔哩的SESSDATA?
|
||||||
> 进入哔哩哔哩网站 -- 打开F12开发者选项 -- 应用 -- 找到Cookie -- 找到SESSDATA -- 复制 -- 粘贴到plugins/rconsole-plugin/config/tools.yaml or 锅巴
|
> 进入哔哩哔哩网站 -- 打开F12开发者选项 -- 应用 -- 找到Cookie -- 找到SESSDATA -- 复制 -- 粘贴到plugins/rconsole-plugin/config/tools.yaml or 锅巴
|
||||||
> [图文教程【群友推荐!】](https://www.bilibili.com/read/cv12349604)
|
> [图文教程【群友推荐!】](https://www.bilibili.com/read/cv12349604)
|
||||||
|
|
||||||
|
### 🎵 douyin问题
|
||||||
|
由于douyin的解析变化莫测,现版本需要填入自己的cookie,具体步骤如下:
|
||||||
|
1. 打开`https://www.douyin.com/` 扫码登入自己的账号
|
||||||
|
2. F12进入控制台,或者下载一个[Cookie-Editor](https://www.crxsoso.com/webstore/detail/hlkenndednhfkekhgcdicdfddnkalmdm)
|
||||||
|
3. 如果是F12,就将以下参数填入到`tools.yaml - douyinCookie`,或者使用锅巴
|
||||||
|
> odin_tt=xxx;sessionid_ss=xxx;ttwid=xxx;passport_csrf_token=xxx;msToken=xxx;
|
||||||
|
|
||||||
|
3. 如果是`Cookie-Editor`就直接到插件复制到`tools.yaml - douyinCookie`,或者锅巴
|
||||||
|
|
||||||
|
具体图示,找以下这几个:
|
||||||
|
- odin_tt
|
||||||
|
- sessionid_ss
|
||||||
|
- ttwid
|
||||||
|
- passport_csrf_token
|
||||||
|
- msToken
|
||||||
|

|
||||||
|
|
||||||
## 🤺 R插件交流群
|
## 🤺 R插件交流群
|
||||||
扫码不行就:575663150
|
扫码不行就:575663150
|
||||||
|
|
||||||
@ -88,22 +106,10 @@ sudo apt-get install ffmpeg
|
|||||||
`proxyAddr: '127.0.0.1' # 魔法地址`
|
`proxyAddr: '127.0.0.1' # 魔法地址`
|
||||||
`proxyPort: '7890' # 魔法端口`
|
`proxyPort: '7890' # 魔法端口`
|
||||||
|
|
||||||
## 🎵 douyin_cookie问题
|
> 海外服务器示例:
|
||||||
由于douyin的解析变化莫测,现版本需要填入自己的cookie,具体步骤如下:
|
`proxyAddr: '127.0.0.1' # 魔法地址`
|
||||||
1. 打开`https://www.douyin.com/` 扫码登入自己的账号
|
`proxyPort: '80' # 魔法端口`
|
||||||
2. F12进入控制台,或者下载一个[Cookie-Editor](https://www.crxsoso.com/webstore/detail/hlkenndednhfkekhgcdicdfddnkalmdm)
|
|
||||||
3. 如果是F12,就将以下参数填入到`tools.yaml - douyinCookie`,或者使用锅巴
|
|
||||||
> odin_tt=xxx;sessionid_ss=xxx;ttwid=xxx;passport_csrf_token=xxx;msToken=xxx;
|
|
||||||
|
|
||||||
3. 如果是`Cookie-Editor`就直接到插件复制到`tools.yaml - douyinCookie`,或者锅巴
|
|
||||||
|
|
||||||
具体图示,找以下这几个:
|
|
||||||
- odin_tt
|
|
||||||
- sessionid_ss
|
|
||||||
- ttwid
|
|
||||||
- passport_csrf_token
|
|
||||||
- msToken
|
|
||||||

|
|
||||||
|
|
||||||
## 📦 业务
|
## 📦 业务
|
||||||

|

|
||||||
|
@ -6,7 +6,7 @@ import puppeteer from "../../../lib/puppeteer/puppeteer.js";
|
|||||||
// http库
|
// http库
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
// 常量
|
// 常量
|
||||||
import { CAT_LIMIT } from "../utils/constant.js";
|
import { CAT_LIMIT } from "../constants/constant.js";
|
||||||
// 书库
|
// 书库
|
||||||
import { getZHelper, getYiBook, getZBook } from "../utils/books.js";
|
import { getZHelper, getYiBook, getZBook } from "../utils/books.js";
|
||||||
// 工具类
|
// 工具类
|
||||||
|
306
apps/tools.js
306
apps/tools.js
@ -7,10 +7,15 @@ import _ from "lodash";
|
|||||||
import tunnel from "tunnel";
|
import tunnel from "tunnel";
|
||||||
import HttpProxyAgent from "https-proxy-agent";
|
import HttpProxyAgent from "https-proxy-agent";
|
||||||
import { mkdirIfNotExists, checkAndRemoveFile, deleteFolderRecursive } from "../utils/file.js";
|
import { mkdirIfNotExists, checkAndRemoveFile, deleteFolderRecursive } from "../utils/file.js";
|
||||||
import { downloadBFile, getDownloadUrl, mergeFileToMp4 } from "../utils/bilibili.js";
|
import { downloadBFile, getAudioUrl, getDownloadUrl, mergeFileToMp4 } from "../utils/bilibili.js";
|
||||||
import { parseUrl, parseM3u8, downloadM3u8Videos, mergeAcFileToMp4 } from "../utils/acfun.js";
|
import { parseUrl, parseM3u8, downloadM3u8Videos, mergeAcFileToMp4 } from "../utils/acfun.js";
|
||||||
import { transMap, douyinTypeMap, XHS_CK, TEN_THOUSAND } from "../utils/constant.js";
|
import {
|
||||||
import { getIdVideo } from "../utils/common.js";
|
transMap,
|
||||||
|
douyinTypeMap,
|
||||||
|
XHS_CK,
|
||||||
|
RESTRICTION_DESCRIPTION,
|
||||||
|
} from "../constants/constant.js";
|
||||||
|
import { dataProcessing, formatBiliInfo, getIdVideo, secondsToTime } from "../utils/common.js";
|
||||||
import config from "../model/index.js";
|
import config from "../model/index.js";
|
||||||
import Translate from "../utils/trans-strategy.js";
|
import Translate from "../utils/trans-strategy.js";
|
||||||
import * as xBogus from "../utils/x-bogus.cjs";
|
import * as xBogus from "../utils/x-bogus.cjs";
|
||||||
@ -21,6 +26,8 @@ import { ChatGPTBrowserClient, ChatGPTClient } from "@waylaidwanderer/chatgpt-ap
|
|||||||
import { av2BV } from "../utils/bilibili-bv-av-convert.js";
|
import { av2BV } from "../utils/bilibili-bv-av-convert.js";
|
||||||
import querystring from "querystring";
|
import querystring from "querystring";
|
||||||
import TokenBucket from "../utils/token-bucket.js";
|
import TokenBucket from "../utils/token-bucket.js";
|
||||||
|
import { getWbi } from "../utils/biliWbi.js";
|
||||||
|
import { BILI_SUMMARY } from "../constants/bili.js";
|
||||||
|
|
||||||
export class tools extends plugin {
|
export class tools extends plugin {
|
||||||
constructor() {
|
constructor() {
|
||||||
@ -31,7 +38,7 @@ export class tools extends plugin {
|
|||||||
priority: 300,
|
priority: 300,
|
||||||
rule: [
|
rule: [
|
||||||
{
|
{
|
||||||
reg: `^(翻|trans)[${tools.Constants.existsTransKey}]`,
|
reg: `^(翻|trans)[${ tools.Constants.existsTransKey }]`,
|
||||||
fnc: "trans",
|
fnc: "trans",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -92,30 +99,13 @@ export class tools extends plugin {
|
|||||||
// 代理接口
|
// 代理接口
|
||||||
this.proxyAddr = this.toolsConfig.proxyAddr;
|
this.proxyAddr = this.toolsConfig.proxyAddr;
|
||||||
this.proxyPort = this.toolsConfig.proxyPort;
|
this.proxyPort = this.toolsConfig.proxyPort;
|
||||||
this.myProxy = `http://${this.proxyAddr}:${this.proxyPort}`;
|
this.myProxy = `http://${ this.proxyAddr }:${ this.proxyPort }`;
|
||||||
// 加载哔哩哔哩配置
|
// 加载哔哩哔哩配置
|
||||||
this.biliSessData = this.toolsConfig.biliSessData;
|
this.biliSessData = this.toolsConfig.biliSessData;
|
||||||
// 加载哔哩哔哩的限制时长
|
// 加载哔哩哔哩的限制时长
|
||||||
this.biliDuration = this.toolsConfig.biliDuration;
|
this.biliDuration = this.toolsConfig.biliDuration;
|
||||||
// 加载抖音Cookie
|
// 加载抖音Cookie
|
||||||
this.douyinCookie = this.toolsConfig.douyinCookie;
|
this.douyinCookie = this.toolsConfig.douyinCookie;
|
||||||
// 加载gpt配置:accessToken、apiKey、模型
|
|
||||||
this.openaiAccessToken = this.toolsConfig.openaiAccessToken;
|
|
||||||
this.openaiApiKey = this.toolsConfig.openaiApiKey;
|
|
||||||
this.openaiModel = this.toolsConfig.openaiModel;
|
|
||||||
// 加载gpt客户端(默认加载sk,如果填了AccessToken就用AccessToken)
|
|
||||||
this.chatGptClient = this.openaiAccessToken === '' ? new ChatGPTClient(this.openaiApiKey, {
|
|
||||||
modelOptions: {
|
|
||||||
model: this.openaiModel,
|
|
||||||
temperature: 0,
|
|
||||||
},
|
|
||||||
proxy: this.myProxy,
|
|
||||||
debug: false,
|
|
||||||
}) : new ChatGPTBrowserClient({
|
|
||||||
reverseProxyUrl: "https://bypass.churchless.tech/api/conversation",
|
|
||||||
accessToken: this.openaiAccessToken,
|
|
||||||
model: this.openaiModel,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 翻译插件
|
// 翻译插件
|
||||||
@ -164,15 +154,15 @@ export class tools extends plugin {
|
|||||||
Referer: "https://www.douyin.com/",
|
Referer: "https://www.douyin.com/",
|
||||||
cookie: this.douyinCookie,
|
cookie: this.douyinCookie,
|
||||||
};
|
};
|
||||||
const dyApi = `https://www.douyin.com/aweme/v1/web/aweme/detail/?device_platform=webapp&aid=6383&channel=channel_pc_web&aweme_id=${douId}&pc_client_type=1&version_code=190500&version_name=19.5.0&cookie_enabled=true&screen_width=1344&screen_height=756&browser_language=zh-CN&browser_platform=Win32&browser_name=Firefox&browser_version=118.0&browser_online=true&engine_name=Gecko&engine_version=109.0&os_name=Windows&os_version=10&cpu_core_num=16&device_memory=&platform=PC&webid=7284189800734082615&msToken=B1N9FM825TkvFbayDsDvZxM8r5suLrsfQbC93TciS0O9Iii8iJpAPd__FM2rpLUJi5xtMencSXLeNn8xmOS9q7bP0CUsrt9oVTL08YXLPRzZm0dHKLc9PGRlyEk=`;
|
const dyApi = `https://www.douyin.com/aweme/v1/web/aweme/detail/?device_platform=webapp&aid=6383&channel=channel_pc_web&aweme_id=${ douId }&pc_client_type=1&version_code=190500&version_name=19.5.0&cookie_enabled=true&screen_width=1344&screen_height=756&browser_language=zh-CN&browser_platform=Win32&browser_name=Firefox&browser_version=118.0&browser_online=true&engine_name=Gecko&engine_version=109.0&os_name=Windows&os_version=10&cpu_core_num=16&device_memory=&platform=PC&webid=7284189800734082615&msToken=B1N9FM825TkvFbayDsDvZxM8r5suLrsfQbC93TciS0O9Iii8iJpAPd__FM2rpLUJi5xtMencSXLeNn8xmOS9q7bP0CUsrt9oVTL08YXLPRzZm0dHKLc9PGRlyEk=`;
|
||||||
// xg参数
|
// xg参数
|
||||||
const xbParam = xBogus.sign(
|
const xbParam = xBogus.sign(
|
||||||
new URLSearchParams(new URL(dyApi).search).toString(),
|
new URLSearchParams(new URL(dyApi).search).toString(),
|
||||||
headers["User-Agent"],
|
headers["User-Agent"],
|
||||||
);
|
);
|
||||||
// const param = resp.data.result[0].paramsencode;
|
// const param = resp.data.result[0].paramsencode;
|
||||||
const resDyApi = `${dyApi}&X-Bogus=${xbParam}`;
|
const resDyApi = `${ dyApi }&X-Bogus=${ xbParam }`;
|
||||||
headers['Referer'] = `https://www.douyin.com/video/${douId}`
|
headers['Referer'] = `https://www.douyin.com/video/${ douId }`
|
||||||
axios
|
axios
|
||||||
.get(resDyApi, {
|
.get(resDyApi, {
|
||||||
headers,
|
headers,
|
||||||
@ -182,9 +172,9 @@ export class tools extends plugin {
|
|||||||
e.reply("解析失败,请重试!");
|
e.reply("解析失败,请重试!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
console.log(resp.data)
|
// console.log(resp.data)
|
||||||
const item = resp.data.aweme_detail;
|
const item = resp.data.aweme_detail;
|
||||||
e.reply(`识别:抖音, ${item.desc}`);
|
e.reply(`识别:抖音, ${ item.desc }`);
|
||||||
const urlTypeCode = item.aweme_type;
|
const urlTypeCode = item.aweme_type;
|
||||||
const urlType = douyinTypeMap[urlTypeCode];
|
const urlType = douyinTypeMap[urlTypeCode];
|
||||||
if (urlType === "video") {
|
if (urlType === "video") {
|
||||||
@ -192,7 +182,7 @@ export class tools extends plugin {
|
|||||||
"http",
|
"http",
|
||||||
"https",
|
"https",
|
||||||
);
|
);
|
||||||
const path = `${this.defaultPath}${
|
const path = `${ this.defaultPath }${
|
||||||
this.e.group_id || this.e.user_id
|
this.e.group_id || this.e.user_id
|
||||||
}/temp.mp4`;
|
}/temp.mp4`;
|
||||||
await this.downloadVideo(resUrl).then(() => {
|
await this.downloadVideo(resUrl).then(() => {
|
||||||
@ -253,10 +243,10 @@ export class tools extends plugin {
|
|||||||
} else {
|
} else {
|
||||||
url = urlRex.exec(url)[0];
|
url = urlRex.exec(url)[0];
|
||||||
}
|
}
|
||||||
let idVideo = await getIdVideo(url);
|
let tiktokVideoId = await getIdVideo(url);
|
||||||
idVideo = idVideo.replace(/\//g, "");
|
tiktokVideoId = tiktokVideoId.replace(/\//g, "");
|
||||||
// API链接
|
// API链接
|
||||||
const API_URL = `https://api16-normal-c-useast1a.tiktokv.com/aweme/v1/feed/?aweme_id=${idVideo}&version_code=262&app_name=musical_ly&channel=App&device_id=null&os_version=14.4.2&device_platform=iphone&device_type=iPhone9`;
|
const API_URL = `https://api16-normal-c-useast1a.tiktokv.com/aweme/v1/feed/?aweme_id=${ tiktokVideoId }`;
|
||||||
|
|
||||||
await axios
|
await axios
|
||||||
.get(API_URL, {
|
.get(API_URL, {
|
||||||
@ -277,11 +267,11 @@ export class tools extends plugin {
|
|||||||
})
|
})
|
||||||
.then(resp => {
|
.then(resp => {
|
||||||
const data = resp.data.aweme_list[0];
|
const data = resp.data.aweme_list[0];
|
||||||
e.reply(`识别:tiktok, ${data.desc}`);
|
e.reply(`识别:tiktok, ${ data.desc }`);
|
||||||
this.downloadVideo(data.video.play_addr.url_list[0], true).then(video => {
|
this.downloadVideo(data.video.play_addr.url_list[0], true).then(video => {
|
||||||
e.reply(
|
e.reply(
|
||||||
segment.video(
|
segment.video(
|
||||||
`${this.defaultPath}${this.e.group_id || this.e.user_id}/temp.mp4`,
|
`${ this.defaultPath }${ this.e.group_id || this.e.user_id }/temp.mp4`,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@ -295,6 +285,7 @@ export class tools extends plugin {
|
|||||||
this.biliCore(e);
|
this.biliCore(e);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async biliCore(e) {
|
async biliCore(e) {
|
||||||
const urlRex = /(?:https?:\/\/)?www\.bilibili\.com\/[A-Za-z\d._?%&+\-=\/#]*/g;
|
const urlRex = /(?:https?:\/\/)?www\.bilibili\.com\/[A-Za-z\d._?%&+\-=\/#]*/g;
|
||||||
const bShortRex = /(http:|https:)\/\/b23.tv\/[A-Za-z\d._?%&+\-=\/#]*/g;
|
const bShortRex = /(http:|https:)\/\/b23.tv\/[A-Za-z\d._?%&+\-=\/#]*/g;
|
||||||
@ -317,81 +308,68 @@ export class tools extends plugin {
|
|||||||
if (matched) {
|
if (matched) {
|
||||||
url = url.replace(matched[0], av2BV(Number(matched[2])));
|
url = url.replace(matched[0], av2BV(Number(matched[2])));
|
||||||
}
|
}
|
||||||
// 动态
|
// 动态处理
|
||||||
if (url.includes("t.bilibili.com")) {
|
if (url.includes("t.bilibili.com")) {
|
||||||
// 去除多余参数
|
url = this.biliDynamic(url, e);
|
||||||
if (url.includes("?")) {
|
|
||||||
url = url.substring(0, url.indexOf("?"));
|
|
||||||
}
|
|
||||||
const dynamicId = /[^/]+(?!.*\/)/.exec(url)[0];
|
|
||||||
getDynamic(dynamicId).then(async resp => {
|
|
||||||
if (resp.dynamicSrc.length > 0) {
|
|
||||||
e.reply(`识别:哔哩哔哩动态, ${resp.dynamicDesc}`);
|
|
||||||
let dynamicSrcMsg = [];
|
|
||||||
resp.dynamicSrc.forEach(item => {
|
|
||||||
dynamicSrcMsg.push({
|
|
||||||
message: segment.image(item),
|
|
||||||
nickname: e.sender.card || e.user_id,
|
|
||||||
user_id: e.user_id,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
await this.reply(await Bot.makeForwardMsg(dynamicSrcMsg));
|
|
||||||
} else {
|
|
||||||
e.reply(`识别:哔哩哔哩动态, 但是失败!`);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 视频信息获取例子:http://api.bilibili.com/x/web-interface/view?bvid=BV1hY411m7cB
|
// 视频信息获取例子:http://api.bilibili.com/x/web-interface/view?bvid=BV1hY411m7cB
|
||||||
// 请求视频信息
|
// 请求视频信息
|
||||||
const videoInfo = await getVideoInfo(url);
|
const videoInfo = await getVideoInfo(url);
|
||||||
const { title, pic, desc, duration, dynamic, stat, aid, cid, pages } = videoInfo;
|
const { title, pic, desc, duration, dynamic, stat, bvid, aid, cid, owner, pages } = videoInfo;
|
||||||
// 视频信息
|
// 视频信息
|
||||||
let { view, danmaku, reply, favorite, coin, share, like } = stat;
|
let { view, danmaku, reply, favorite, coin, share, like } = stat;
|
||||||
// 数据处理
|
|
||||||
const dataProcessing = data => {
|
|
||||||
return Number(data) >= TEN_THOUSAND ? (data / TEN_THOUSAND).toFixed(1) + "万" : data;
|
|
||||||
};
|
|
||||||
// 限制时长 & 考虑分页视频情况
|
// 限制时长 & 考虑分页视频情况
|
||||||
const query = querystring.parse(url);
|
const query = querystring.parse(url);
|
||||||
const curPage = query?.p || 0;
|
const curPage = query?.p || 0;
|
||||||
const curDuration = pages?.[curPage]?.duration || duration;
|
const curDuration = pages?.[curPage]?.duration || duration;
|
||||||
const isLimitDuration = curDuration > this.biliDuration
|
const isLimitDuration = curDuration > this.biliDuration
|
||||||
|
// 构造一个可扩展的Map
|
||||||
|
const dataProcessMap = {
|
||||||
|
"点赞": like,
|
||||||
|
"硬币": coin,
|
||||||
|
"收藏": favorite,
|
||||||
|
"分享": share,
|
||||||
|
"总播放量": view,
|
||||||
|
"弹幕数量": danmaku,
|
||||||
|
"评论": reply
|
||||||
|
};
|
||||||
// 格式化数据
|
// 格式化数据
|
||||||
const combineContent =
|
const combineContent = `\n${ formatBiliInfo(dataProcessMap) }\n简介:${ desc }`;
|
||||||
`\n点赞:${dataProcessing(like)} | 硬币:${dataProcessing(
|
let biliInfo = [`识别:哔哩哔哩:${ title }`, combineContent]
|
||||||
coin,
|
// 只提取音乐处理
|
||||||
)} | 收藏:${dataProcessing(favorite)} | 分享:${dataProcessing(share)}\n` +
|
if (e.msg.includes("bili音乐")) {
|
||||||
`总播放量:${dataProcessing(view)} | 弹幕数量:${dataProcessing(
|
return await this.biliMusic(url, e, biliInfo);
|
||||||
danmaku,
|
}
|
||||||
)} | 评论:${dataProcessing(reply)}\n` +
|
// 不提取音乐,正常处理
|
||||||
`简介:${desc}`;
|
|
||||||
let biliInfo = [`识别:哔哩哔哩:${title}`, combineContent]
|
|
||||||
if (isLimitDuration) {
|
if (isLimitDuration) {
|
||||||
// 加入图片
|
// 加入图片
|
||||||
biliInfo.unshift(segment.image(pic))
|
biliInfo.unshift(segment.image(pic))
|
||||||
// 限制视频解析
|
// 限制视频解析
|
||||||
const durationInMinutes = (curDuration / 60).toFixed(0);
|
const durationInMinutes = (curDuration / 60).toFixed(0);
|
||||||
biliInfo.push(`\n-----------------------限制说明-----------------------\n当前视频时长约:${durationInMinutes}分钟,\n大于管理员设置的最大时长 ${this.biliDuration / 60} 分钟!`)
|
biliInfo.push(`${RESTRICTION_DESCRIPTION}\n当前视频时长约:${ durationInMinutes }分钟,\n大于管理员设置的最大时长 ${ this.biliDuration / 60 } 分钟!`)
|
||||||
e.reply(biliInfo);
|
e.reply(biliInfo);
|
||||||
// 总结
|
// 总结
|
||||||
const summary = await this.getBiliSummary(videoInfo);
|
const summary = await this.getBiliSummary(bvid, cid, owner.mid);
|
||||||
summary && e.reply(summary);
|
summary && e.reply(summary);
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
|
// 总结
|
||||||
|
const summary = await this.getBiliSummary(bvid, cid, owner.mid);
|
||||||
|
summary && biliInfo.push(`\n${summary}`)
|
||||||
|
//
|
||||||
e.reply(biliInfo);
|
e.reply(biliInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 创建文件,如果不存在
|
// 创建文件,如果不存在
|
||||||
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 mkdirIfNotExists(path);
|
await mkdirIfNotExists(path);
|
||||||
// 下载文件
|
// 下载文件
|
||||||
getDownloadUrl(url)
|
getDownloadUrl(url)
|
||||||
.then(data => {
|
.then(data => {
|
||||||
this.downBili(`${path}temp`, data.videoUrl, data.audioUrl)
|
this.downBili(`${ path }temp`, data.videoUrl, data.audioUrl)
|
||||||
.then(_ => {
|
.then(_ => {
|
||||||
e.reply(segment.video(`${path}temp.mp4`));
|
e.reply(segment.video(`${ path }temp.mp4`));
|
||||||
})
|
})
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
logger.error(err);
|
logger.error(err);
|
||||||
@ -402,17 +380,95 @@ export class tools extends plugin {
|
|||||||
logger.error(err);
|
logger.error(err);
|
||||||
e.reply("解析失败,请重试一下");
|
e.reply("解析失败,请重试一下");
|
||||||
});
|
});
|
||||||
// 总结
|
|
||||||
const summary = await this.getBiliSummary(videoInfo);
|
|
||||||
summary && e.reply(summary);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async biliMusic(url, e, biliInfo) {
|
||||||
|
const { audioUrl, title } = await getAudioUrl(url);
|
||||||
|
e.reply(biliInfo)
|
||||||
|
e.reply(segment.record(audioUrl))
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 发送哔哩哔哩动态的算法
|
||||||
|
biliDynamic(url, e) {
|
||||||
|
// 去除多余参数
|
||||||
|
if (url.includes("?")) {
|
||||||
|
url = url.substring(0, url.indexOf("?"));
|
||||||
|
}
|
||||||
|
const dynamicId = /[^/]+(?!.*\/)/.exec(url)[0];
|
||||||
|
getDynamic(dynamicId).then(async resp => {
|
||||||
|
if (resp.dynamicSrc.length > 0) {
|
||||||
|
e.reply(`识别:哔哩哔哩动态, ${ resp.dynamicDesc }`);
|
||||||
|
let dynamicSrcMsg = [];
|
||||||
|
resp.dynamicSrc.forEach(item => {
|
||||||
|
dynamicSrcMsg.push({
|
||||||
|
message: segment.image(item),
|
||||||
|
nickname: e.sender.card || e.user_id,
|
||||||
|
user_id: e.user_id,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
await this.reply(await Bot.makeForwardMsg(dynamicSrcMsg));
|
||||||
|
} else {
|
||||||
|
e.reply(`识别:哔哩哔哩动态, 但是失败!`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 哔哩哔哩总结
|
||||||
|
* @author zhiyu1998
|
||||||
|
* @param bvid 稿件
|
||||||
|
* @param cid 视频 cid
|
||||||
|
* @param up_mid UP主 mid
|
||||||
|
* @return {Promise<void>}
|
||||||
|
*/
|
||||||
|
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) {
|
async wiki(e) {
|
||||||
const key = e.msg.replace(/#|百科|wiki/g, "").trim();
|
const key = e.msg.replace(/#|百科|wiki/g, "").trim();
|
||||||
const url = `https://xiaoapi.cn/API/bk.php?m=json&type=sg&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 bdUrl = `https://xiaoapi.cn/API/bk.php?m=json&type=bd&msg=${ encodeURI(key) }`;
|
||||||
const bkRes = await Promise.all([
|
const bkRes = await Promise.all([
|
||||||
axios
|
axios
|
||||||
.get(bdUrl, {
|
.get(bdUrl, {
|
||||||
@ -440,8 +496,8 @@ export class tools extends plugin {
|
|||||||
return res.map(item => {
|
return res.map(item => {
|
||||||
return {
|
return {
|
||||||
message: `
|
message: `
|
||||||
解释:${_.get(item, "msg")}\n
|
解释:${ _.get(item, "msg") }\n
|
||||||
详情:${_.get(item, "more")}\n
|
详情:${ _.get(item, "more") }\n
|
||||||
`,
|
`,
|
||||||
nickname: e.sender.card || e.user_id,
|
nickname: e.sender.card || e.user_id,
|
||||||
user_id: e.user_id,
|
user_id: e.user_id,
|
||||||
@ -473,8 +529,8 @@ export class tools extends plugin {
|
|||||||
expansions: ["entities.mentions.username", "attachments.media_keys"],
|
expansions: ["entities.mentions.username", "attachments.media_keys"],
|
||||||
})
|
})
|
||||||
.then(async resp => {
|
.then(async resp => {
|
||||||
e.reply(`识别:小蓝鸟学习版,${resp.data.text}`);
|
e.reply(`识别:小蓝鸟学习版,${ resp.data.text }`);
|
||||||
const downloadPath = `${this.defaultPath}${this.e.group_id || this.e.user_id}`;
|
const downloadPath = `${ this.defaultPath }${ this.e.group_id || this.e.user_id }`;
|
||||||
// 创建文件夹(如果没有过这个群)
|
// 创建文件夹(如果没有过这个群)
|
||||||
if (!fs.existsSync(downloadPath)) {
|
if (!fs.existsSync(downloadPath)) {
|
||||||
mkdirsSync(downloadPath);
|
mkdirsSync(downloadPath);
|
||||||
@ -489,7 +545,7 @@ export class tools extends plugin {
|
|||||||
// 视频
|
// 视频
|
||||||
await this.downloadVideo(resp.includes.media[0].variants[0].url, true).then(
|
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解析
|
// acfun解析
|
||||||
async acfun(e) {
|
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);
|
await mkdirIfNotExists(path);
|
||||||
|
|
||||||
let inputMsg = e.msg;
|
let inputMsg = e.msg;
|
||||||
// 适配手机分享:https://m.acfun.cn/v/?ac=32838812&sid=d2b0991bd6ad9c09
|
// 适配手机分享:https://m.acfun.cn/v/?ac=32838812&sid=d2b0991bd6ad9c09
|
||||||
if (inputMsg.includes("m.acfun.cn")) {
|
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 => {
|
parseUrl(inputMsg).then(res => {
|
||||||
e.reply(`识别:猴山,${res.videoName}`);
|
e.reply(`识别:猴山,${ res.videoName }`);
|
||||||
parseM3u8(res.urlM3u8s[res.urlM3u8s.length - 1]).then(res2 => {
|
parseM3u8(res.urlM3u8s[res.urlM3u8s.length - 1]).then(res2 => {
|
||||||
downloadM3u8Videos(res2.m3u8FullUrls, path).then(_ => {
|
downloadM3u8Videos(res2.m3u8FullUrls, path).then(_ => {
|
||||||
mergeAcFileToMp4(res2.tsNames, path, `${path}out.mp4`).then(_ => {
|
mergeAcFileToMp4(res2.tsNames, path, `${ path }out.mp4`).then(_ => {
|
||||||
e.reply(segment.video(`${path}out.mp4`));
|
e.reply(segment.video(`${ path }out.mp4`));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -550,9 +606,9 @@ export class tools extends plugin {
|
|||||||
async redbook(e) {
|
async redbook(e) {
|
||||||
// 正则说明:匹配手机链接、匹配小程序、匹配PC链接
|
// 正则说明:匹配手机链接、匹配小程序、匹配PC链接
|
||||||
let msgUrl =
|
let msgUrl =
|
||||||
/(http:|https:)\/\/(xhslink|xiaohongshu).com\/[A-Za-z\d._?%&+\-=\/#@]*/.exec(
|
/(http:|https:)\/\/(xhslink|xiaohongshu).com\/[A-Za-z\d._?%&+\-=\/#@]*/.exec(
|
||||||
e.msg,
|
e.msg,
|
||||||
)?.[0]
|
)?.[0]
|
||||||
|| /(http:|https:)\/\/www\.xiaohongshu\.com\/discovery\/item\/(\w+)/.exec(
|
|| /(http:|https:)\/\/www\.xiaohongshu\.com\/discovery\/item\/(\w+)/.exec(
|
||||||
e.message[0].data,
|
e.message[0].data,
|
||||||
)?.[0]
|
)?.[0]
|
||||||
@ -574,9 +630,9 @@ export class tools extends plugin {
|
|||||||
} else {
|
} else {
|
||||||
id = /explore\/(\w+)/.exec(msgUrl)?.[1] || /discovery\/item\/(\w+)/.exec(msgUrl)?.[1];
|
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: {
|
headers: {
|
||||||
"user-agent":
|
"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",
|
"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 res = JSON.parse(resJson.match(reg)[1]);
|
||||||
const noteData = res.noteData.data.noteData;
|
const noteData = res.noteData.data.noteData;
|
||||||
const { title, desc, type } = noteData;
|
const { title, desc, type } = noteData;
|
||||||
e.reply(`识别:小红书, ${title}\n${desc}`);
|
e.reply(`识别:小红书, ${ title }\n${ desc }`);
|
||||||
let imgPromise = [];
|
let imgPromise = [];
|
||||||
if (type === "video") {
|
if (type === "video") {
|
||||||
const url = noteData.video.url;
|
const url = noteData.video.url;
|
||||||
@ -661,9 +717,9 @@ export class tools extends plugin {
|
|||||||
}
|
}
|
||||||
const rTempFileLen = await deleteFolderRecursive(this.toolsConfig.defaultPath)
|
const rTempFileLen = await deleteFolderRecursive(this.toolsConfig.defaultPath)
|
||||||
e.reply(
|
e.reply(
|
||||||
`数据统计:\n`+
|
`数据统计:\n` +
|
||||||
`- 当前清理了${dataDirectory}下总计:${dataClearFileLen} 个垃圾文件\n`+
|
`- 当前清理了${ dataDirectory }下总计:${ dataClearFileLen } 个垃圾文件\n` +
|
||||||
`- 当前清理了${ this.toolsConfig.defaultPath}下文件夹:${rTempFileLen} 个群的所有临时文件`
|
`- 当前清理了${ this.toolsConfig.defaultPath }下文件夹:${ rTempFileLen } 个群的所有临时文件`
|
||||||
);
|
);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
logger.error(err);
|
logger.error(err);
|
||||||
@ -677,10 +733,10 @@ export class tools extends plugin {
|
|||||||
if (suffix.startsWith("reel")) {
|
if (suffix.startsWith("reel")) {
|
||||||
suffix = suffix.replace("reel/", "p/");
|
suffix = suffix.replace("reel/", "p/");
|
||||||
}
|
}
|
||||||
const API = `https://imginn.com/${suffix}`;
|
const API = `https://imginn.com/${ suffix }`;
|
||||||
logger.info(API);
|
// logger.info(API);
|
||||||
let imgPromise = [];
|
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) => {
|
const downloadImg = (url, destination) => {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
@ -708,13 +764,13 @@ export class tools extends plugin {
|
|||||||
const desc = html.match(/(?<=content=").*?(?=\")/g)?.[2];
|
const desc = html.match(/(?<=content=").*?(?=\")/g)?.[2];
|
||||||
const images = html.match(/<div class=\"swiper-slide.*?\">/g);
|
const images = html.match(/<div class=\"swiper-slide.*?\">/g);
|
||||||
if (!_.isNull(images)) {
|
if (!_.isNull(images)) {
|
||||||
e.reply(`识别:Insta,${desc || "暂无描述"}\n`);
|
e.reply(`识别:Insta,${ desc || "暂无描述" }\n`);
|
||||||
images.map((item, index) => {
|
images.map((item, index) => {
|
||||||
const imgUrl = /(?<=data-src=").*?(?=")/
|
const imgUrl = /(?<=data-src=").*?(?=")/
|
||||||
.exec(item)[0]
|
.exec(item)[0]
|
||||||
.replace(/#38/g, "")
|
.replace(/#38/g, "")
|
||||||
.replace(/;/g, "");
|
.replace(/;/g, "");
|
||||||
imgPromise.push(downloadImg(imgUrl, `${downloadPath}/${index}.jpg`));
|
imgPromise.push(downloadImg(imgUrl, `${ downloadPath }/${ index }.jpg`));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
// TODO 视频,会出bug暂时不做
|
// TODO 视频,会出bug暂时不做
|
||||||
@ -755,18 +811,18 @@ export class tools extends plugin {
|
|||||||
/(?=mvId).*?(?=&)/.exec(e.msg.trim())?.[0].replace("mvId=", "");
|
/(?=mvId).*?(?=&)/.exec(e.msg.trim())?.[0].replace("mvId=", "");
|
||||||
const { name, album, artist, albumPic120, categorys } = await getBodianMusicInfo(id);
|
const { name, album, artist, albumPic120, categorys } = await getBodianMusicInfo(id);
|
||||||
e.reply([
|
e.reply([
|
||||||
`识别:波点音乐,${name}-${album}-${artist}\n标签:${categorys
|
`识别:波点音乐,${ name }-${ album }-${ artist }\n标签:${ categorys
|
||||||
.map(item => item.name)
|
.map(item => item.name)
|
||||||
.join(" | ")}`,
|
.join(" | ") }`,
|
||||||
segment.image(albumPic120),
|
segment.image(albumPic120),
|
||||||
]);
|
]);
|
||||||
if (e.msg.includes("musicId")) {
|
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(_ => {
|
await getBodianAudio(id, path).then(_ => {
|
||||||
Bot.acquireGfs(e.group_id).upload(
|
Bot.acquireGfs(e.group_id).upload(
|
||||||
fs.readFileSync(path + "/temp.mp3"),
|
fs.readFileSync(path + "/temp.mp3"),
|
||||||
"/",
|
"/",
|
||||||
`${name}-${album}-${artist}.mp3`,
|
`${ name }-${ album }-${ artist }.mp3`,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
} else if (e.msg.includes("mvId")) {
|
} else if (e.msg.includes("mvId")) {
|
||||||
@ -800,7 +856,7 @@ export class tools extends plugin {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
// 提取视频
|
// 提取视频
|
||||||
const videoUrl = `https://www.kuaishou.com/short-video/${video_id}`;
|
const videoUrl = `https://www.kuaishou.com/short-video/${ video_id }`;
|
||||||
|
|
||||||
// 发送GET请求
|
// 发送GET请求
|
||||||
const response = await axios.get(videoUrl, {
|
const response = await axios.get(videoUrl, {
|
||||||
@ -870,32 +926,10 @@ export class tools extends plugin {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
]).then(data => {
|
]).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的最后一个为名字)
|
* 下载一张网络图片(自动以url的最后一个为名字)
|
||||||
* @param img
|
* @param img
|
||||||
@ -908,7 +942,7 @@ export class tools extends plugin {
|
|||||||
if (fileName === "") {
|
if (fileName === "") {
|
||||||
fileName = img.split("/").pop();
|
fileName = img.split("/").pop();
|
||||||
}
|
}
|
||||||
const filepath = `${dir}/${fileName}`;
|
const filepath = `${ dir }/${ fileName }`;
|
||||||
await mkdirIfNotExists(dir)
|
await mkdirIfNotExists(dir)
|
||||||
const writer = fs.createWriteStream(filepath);
|
const writer = fs.createWriteStream(filepath);
|
||||||
const axiosConfig = {
|
const axiosConfig = {
|
||||||
@ -976,8 +1010,8 @@ export class tools extends plugin {
|
|||||||
* @returns {{groupPath: string, target: string}}
|
* @returns {{groupPath: string, target: string}}
|
||||||
*/
|
*/
|
||||||
getGroupPathAndTarget() {
|
getGroupPathAndTarget() {
|
||||||
const groupPath = `${this.defaultPath}${this.e.group_id || this.e.user_id}`;
|
const groupPath = `${ this.defaultPath }${ this.e.group_id || this.e.user_id }`;
|
||||||
const target = `${groupPath}/temp.mp4`;
|
const target = `${ groupPath }/temp.mp4`;
|
||||||
return { groupPath, target };
|
return { groupPath, target };
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1012,7 +1046,7 @@ export class tools extends plugin {
|
|||||||
await checkAndRemoveFile(target);
|
await checkAndRemoveFile(target);
|
||||||
|
|
||||||
const res = await axios.get(url, axiosConfig);
|
const res = await axios.get(url, axiosConfig);
|
||||||
logger.mark(`开始下载: ${url}`);
|
logger.mark(`开始下载: ${ url }`);
|
||||||
const writer = fs.createWriteStream(target);
|
const writer = fs.createWriteStream(target);
|
||||||
res.data.pipe(writer);
|
res.data.pipe(writer);
|
||||||
|
|
||||||
|
@ -38,6 +38,9 @@
|
|||||||
- icon: bilibili
|
- icon: bilibili
|
||||||
title: "bilibili/b23"
|
title: "bilibili/b23"
|
||||||
desc: 哔哩哔哩分享实时下载
|
desc: 哔哩哔哩分享实时下载
|
||||||
|
- icon: bilimusic
|
||||||
|
title: "bili音乐+链接"
|
||||||
|
desc: 哔哩哔哩音乐分享实时下载
|
||||||
- icon: 推特
|
- icon: 推特
|
||||||
title: "小蓝鸟"
|
title: "小蓝鸟"
|
||||||
desc: 推特学习版分享实时下载
|
desc: 推特学习版分享实时下载
|
||||||
|
@ -8,8 +8,4 @@ translateSecret: '' # 百度翻译密匙
|
|||||||
biliSessData: '' # 哔哩哔哩的SESSDATA
|
biliSessData: '' # 哔哩哔哩的SESSDATA
|
||||||
biliDuration: 480 # 哔哩哔哩限制的最大视频时长(默认8分钟),单位:秒
|
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;
|
douyinCookie: '' # douyin's cookie, 格式:odin_tt=xxx;sessionid_ss=xxx;ttwid=xxx;passport_csrf_token=xxx;msToken=xxx;
|
@ -1,11 +1,11 @@
|
|||||||
- {
|
- {
|
||||||
version: 1.1.3,
|
version: 1.2.0,
|
||||||
data:
|
data:
|
||||||
[
|
[
|
||||||
|
新增<span class="cmd">哔哩哔哩官方AI总结</span>功能,
|
||||||
|
新增<span class="cmd">哔哩哔哩音乐提取</span>功能,
|
||||||
新增<span class="cmd">快手解析</span>功能,
|
新增<span class="cmd">快手解析</span>功能,
|
||||||
新增<span class="cmd">竹白百科</span>功能,
|
支持<span class="cmd">锅巴</span>插件,方便查看和修改配置,
|
||||||
重构<span class="cmd">翻译</span>功能,
|
|
||||||
适配<span class="cmd">锅巴</span>插件,方便查看和修改配置,
|
|
||||||
添加<span class="cmd">#R帮助</span>获取插件帮助,
|
添加<span class="cmd">#R帮助</span>获取插件帮助,
|
||||||
添加<span class="cmd">#R版本</span>获取插件版本,
|
添加<span class="cmd">#R版本</span>获取插件版本,
|
||||||
],
|
],
|
||||||
|
@ -79,7 +79,7 @@ export function supportGuoba() {
|
|||||||
field: "tools.biliSessData",
|
field: "tools.biliSessData",
|
||||||
label: "哔哩哔哩SESSDATA",
|
label: "哔哩哔哩SESSDATA",
|
||||||
bottomHelpMessage:
|
bottomHelpMessage:
|
||||||
"如何获取具体参考我的文档说明:https://gitee.com/kyrzy0416/rconsole-plugin",
|
"如何获取具体参考我的文档说明:https://gitee.com/kyrzy0416/rconsole-plugin#Q&A",
|
||||||
component: "Input",
|
component: "Input",
|
||||||
required: false,
|
required: false,
|
||||||
componentProps: {
|
componentProps: {
|
||||||
@ -97,17 +97,6 @@ export function supportGuoba() {
|
|||||||
placeholder: "请输入哔哩哔哩的视频最大限制时长(默认15分钟)",
|
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",
|
field: "tools.douyinCookie",
|
||||||
label: "抖音的Cookie",
|
label: "抖音的Cookie",
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
import fetch from "node-fetch";
|
import fetch from "node-fetch";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
|
import { BILI_VIDEO_INFO } from "../constants/bili.js";
|
||||||
|
|
||||||
async function getVideoInfo(url) {
|
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];
|
const videoId = /video\/[^\?\/ ]+/.exec(url)[0].split("/")[1];
|
||||||
// 获取视频信息,然后发送
|
// 获取视频信息,然后发送
|
||||||
return fetch(`${baseVideoInfo}?bvid=${videoId}`)
|
return fetch(`${BILI_VIDEO_INFO}?bvid=${videoId}`)
|
||||||
.then(async resp => {
|
.then(async resp => {
|
||||||
const respJson = await resp.json();
|
const respJson = await resp.json();
|
||||||
const respData = respJson.data;
|
const respData = respJson.data;
|
||||||
@ -16,8 +17,10 @@ async function getVideoInfo(url) {
|
|||||||
duration: respData.duration,
|
duration: respData.duration,
|
||||||
dynamic: respJson.data.dynamic,
|
dynamic: respJson.data.dynamic,
|
||||||
stat: respData.stat,
|
stat: respData.stat,
|
||||||
|
bvid: respData.bvid,
|
||||||
aid: respData.aid,
|
aid: respData.aid,
|
||||||
cid: respData.pages?.[0].cid,
|
cid: respData.pages?.[0].cid,
|
||||||
|
owner: respData.owner,
|
||||||
pages: respData?.pages,
|
pages: respData?.pages,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
@ -1,208 +0,0 @@
|
|||||||
/**
|
|
||||||
* 获取gpt提取视频信息的文字
|
|
||||||
* @param videoInfo
|
|
||||||
* @param biliSessData
|
|
||||||
* @param shouldShowTimestamp 是否在每段字幕前面加入时间标识
|
|
||||||
* @returns {Promise<string>}
|
|
||||||
*/
|
|
||||||
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;
|
|
||||||
}
|
|
@ -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(/<script>window\.__playinfo__=({.*})<\/script><script>/)?.[1],
|
||||||
|
);
|
||||||
|
// 获取音频
|
||||||
|
const audioUrl =
|
||||||
|
info?.data?.dash?.audio?.[0]?.baseUrl ?? info?.data?.dash?.audio?.[0]?.backupUrl?.[0];
|
||||||
|
const title = data.match(/title="(.*?)"/)?.[1]?.replaceAll?.(/\\|\/|:|\*|\?|"|<|>|\|/g, '');
|
||||||
|
|
||||||
|
|
||||||
|
if (audioUrl) {
|
||||||
|
return { audioUrl, title };
|
||||||
|
}
|
||||||
|
|
||||||
|
return Promise.reject('获取下载地址失败');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
async function mergeFileToMp4 (vFullFileName, aFullFileName, outputFileName, shouldDelete = true) {
|
async function mergeFileToMp4 (vFullFileName, aFullFileName, outputFileName, shouldDelete = true) {
|
||||||
// 判断当前环境
|
// 判断当前环境
|
||||||
let env;
|
let env;
|
||||||
@ -95,4 +122,4 @@ async function mergeFileToMp4 (vFullFileName, aFullFileName, outputFileName, sho
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export { downloadBFile, getDownloadUrl, mergeFileToMp4 }
|
export { downloadBFile, getDownloadUrl, getAudioUrl, mergeFileToMp4 }
|
||||||
|
@ -4,6 +4,7 @@ import axios from "axios";
|
|||||||
import fs from "node:fs";
|
import fs from "node:fs";
|
||||||
import fetch from "node-fetch";
|
import fetch from "node-fetch";
|
||||||
import { mkdirIfNotExists } from "./file.js";
|
import { mkdirIfNotExists } from "./file.js";
|
||||||
|
import {TEN_THOUSAND} from "../constants/constant.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 请求模板
|
* 请求模板
|
||||||
@ -163,4 +164,41 @@ async function downloadMp3(mp3Url, path, redirect = "manual") {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export { jFeatch, autoTask, retry, getIdVideo, generateRandomStr, downloadMp3 };
|
/**
|
||||||
|
* 千位数的数据处理
|
||||||
|
* @param data
|
||||||
|
* @return {string|*}
|
||||||
|
*/
|
||||||
|
const dataProcessing = data => {
|
||||||
|
return Number(data) >= TEN_THOUSAND ? (data / TEN_THOUSAND).toFixed(1) + "万" : data;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 哔哩哔哩解析的数据处理
|
||||||
|
* @param data
|
||||||
|
* @return {string}
|
||||||
|
*/
|
||||||
|
function formatBiliInfo(data) {
|
||||||
|
return Object.keys(data).map(key => `${key}:${dataProcessing(data[key])}`).join(' | ');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 数字转换成具体时间
|
||||||
|
* @param seconds
|
||||||
|
* @return {string}
|
||||||
|
*/
|
||||||
|
function secondsToTime(seconds) {
|
||||||
|
const pad = (num, size) => num.toString().padStart(size, '0');
|
||||||
|
|
||||||
|
let hours = Math.floor(seconds / 3600);
|
||||||
|
let minutes = Math.floor((seconds % 3600) / 60);
|
||||||
|
let secs = seconds % 60;
|
||||||
|
|
||||||
|
// 如果你只需要分钟和秒钟,你可以返回下面这行:
|
||||||
|
// return `${pad(minutes, 2)}:${pad(secs, 2)}`;
|
||||||
|
|
||||||
|
// 完整的 HH:MM:SS 格式
|
||||||
|
return `${pad(hours, 2)}:${pad(minutes, 2)}:${pad(secs, 2)}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export { jFeatch, autoTask, retry, getIdVideo, generateRandomStr, downloadMp3, dataProcessing, formatBiliInfo, secondsToTime };
|
||||||
|
@ -1,41 +0,0 @@
|
|||||||
/**
|
|
||||||
* 用于百度翻译的常量控制
|
|
||||||
*
|
|
||||||
* @type {{英: string, 日: string, 文: string, 中: string}}
|
|
||||||
*/
|
|
||||||
export const transMap = { 中: "zh", 日: "jp", 文: "wyw", 英: "en", 俄: "ru", 韩: "kr" };
|
|
||||||
/**
|
|
||||||
* 用于腾讯交互式翻译的常量控制
|
|
||||||
*
|
|
||||||
* @type {{英: string, 俄: string, 日: string, 韩: string, 中: string}}
|
|
||||||
*/
|
|
||||||
export const tencentTransMap = { 中: "zh", 日: "ja", 韩: "ko", 英: "en", 俄: "ru" };
|
|
||||||
/**
|
|
||||||
* 用于腾讯交互式翻译的常量控制
|
|
||||||
*
|
|
||||||
* @type {{英: string, 俄: string, 日: string, 韩: string, 中: string}}
|
|
||||||
*/
|
|
||||||
export const googleTransMap = { 中: "zh-CN", 日: "jp", 韩: "ko", 英: "en", 俄: "ru" };
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 以下为抖音/TikTok类型代码
|
|
||||||
*
|
|
||||||
* @type {{"0": string, "55": string, "2": string, "68": string, "58": string, "4": string, "61": string, "51": string, "150": string}}
|
|
||||||
*/
|
|
||||||
export const douyinTypeMap = {
|
|
||||||
2: "image",
|
|
||||||
4: "video",
|
|
||||||
68: "image",
|
|
||||||
0: "video",
|
|
||||||
51: "video",
|
|
||||||
55: "video",
|
|
||||||
58: "video",
|
|
||||||
61: "video",
|
|
||||||
150: "image",
|
|
||||||
};
|
|
||||||
|
|
||||||
export const TEN_THOUSAND = 10000;
|
|
||||||
|
|
||||||
export const CAT_LIMIT = 10;
|
|
||||||
|
|
||||||
export const XHS_CK = 'eGhzVHJhY2tlcklkPTczODhhYmY2LTI0MDgtNGU5YS04MTUyLTE0MGVhOGY1MTQ5ZjsgeGhzVHJhY2tlcklkLnNpZz1UcGUxTkNaX3B3UkFYdG01SVJmVEs0SWUxM0xBaGZuNmNZU2N4Vi1JYWxFOyBhMT0xODY2ZDkwMDM0NmI2NmppcjMzcGpxZ2MwM3JvcG1mczAydXMxdWNoeDEwMDAwMTM1MDUzOyB3ZWJJZD1mMTNkOGJkYjhiZGM3ZGE0MzY0NjA4NWJjYzQ1MDQ1YTsgZ2lkPXlZS0tmajg4SzA4MnlZS0tmajg4cUo3UzRLREtLVjNGcXFVVjd4Q0FrUzhxRk15OGxVNmlNeTg4OHlxMjgycThmMlk0UzAySjsgZ2lkLnNpZ249YlpzcFFzSUxEUmN5akZLQmN2L1FMWVhkU3lvPTsgd2ViX3Nlc3Npb249MDMwMDM3YTRjMDQyYjE1ZTVjMTg4OTUwOGIyNDRhZDExM2UwNTM7IHhoc1RyYWNrZXI9dXJsPW5vdGVEZXRhaWwmeGhzc2hhcmU9V2VpeGluU2Vzc2lvbjsgeGhzVHJhY2tlci5zaWc9YzdmcDVRclk2SGNvVERhUzluX2N3Z2RCRHh2MFZmWnpSU1NTcnlzbG5lQTsgZXh0cmFfZXhwX2lkcz1oNV8yMzAyMDExX29yaWdpbixoNV8xMjA4X2NsdCxoNV8xMTMwX2NsdCxpb3Nfd3hfbGF1bmNoX29wZW5fYXBwX2V4cCxoNV92aWRlb191aV9leHAzLHd4X2xhdW5jaF9vcGVuX2FwcF9kdXJhdGlvbl9vcmlnaW4scXVlc19jbHQyOyBleHRyYV9leHBfaWRzLnNpZz1DVUdrR3NYT3lBZmpVSXkyVGo3SjN4YmRNakFfSnpoR1JkYWd6cVlkbmJnOyB3ZWJCdWlsZD0xLjEuMjE7IHhzZWNhcHBpZD14aHMtcGMtd2ViOyB3ZWJzZWN0aWdhPTU5ZDNlZjFlNjBjNGFhMzdhN2RmM2MyMzQ2N2JkNDZkN2YxZGEwYjE5MThjZjMzNWVlN2YyZTllNTJhYzA0Y2Y7IHNlY19wb2lzb25faWQ9MTI0OTE1NWQtOWU5ZS00MzkyLTg2NTgtNTA1Yzc0YTUzMTM1'
|
|
@ -1,4 +1,4 @@
|
|||||||
import {transMap, tencentTransMap, googleTransMap} from "./constant.js";
|
import {transMap, tencentTransMap, googleTransMap} from "../constants/constant.js";
|
||||||
import md5 from "md5";
|
import md5 from "md5";
|
||||||
import fetch from "node-fetch";
|
import fetch from "node-fetch";
|
||||||
import HttpProxyAgent from "https-proxy-agent";
|
import HttpProxyAgent from "https-proxy-agent";
|
||||||
|
Loading…
x
Reference in New Issue
Block a user