diff --git a/README.md b/README.md index d5a7f3b..7be1e27 100644 --- a/README.md +++ b/README.md @@ -48,7 +48,7 @@ git clone https://gitee.com/kyrzy0416/rconsole-plugin.git ./plugins/rconsole-plu git clone https://github.com/zhiyu1998/rconsole-plugin.git ./plugins/rconsole-plugin/ ``` -2.【必要】在`Yunzai-Bot / Miao-Yunzai`目录下安装axios(0.27.2)、魔法工具(tunnel) +2.【必要】在`Yunzai-Bot / Miao-Yunzai`目录下安装axios(0.27.2)、魔法工具(tunnel)、二维码处理工具(qrcode)、高性能下载队列(p-queue) ```shell diff --git a/apps/tools.js b/apps/tools.js index 259cd10..4fbe2a2 100644 --- a/apps/tools.js +++ b/apps/tools.js @@ -10,7 +10,7 @@ import HttpProxyAgent from "https-proxy-agent"; import { checkAndRemoveFile, deleteFolderRecursive, mkdirIfNotExists, readCurrentDir } from "../utils/file.js"; import { downloadBFile, - getBiliAudio, + getBiliAudio, getBiliVideoWithSession, getDownloadUrl, getDynamic, getScanCodeData, getVideoInfo, @@ -42,6 +42,7 @@ import { getBodianAudio, getBodianMusicInfo, getBodianMv } from "../utils/bodian import { av2BV } from "../utils/bilibili-bv-av-convert.js"; import querystring from "querystring"; import TokenBucket from "../utils/token-bucket.js"; +import PQueue from 'p-queue'; import { getWbi } from "../utils/biliWbi.js"; import { BILI_SUMMARY, @@ -183,6 +184,8 @@ export class tools extends plugin { translateSecret: this.toolsConfig.translateSecret, proxy: this.myProxy, }); + // 并发队列 + this.queue = new PQueue({concurrency: this.toolsConfig.queueConcurrency}); } // 翻译插件 @@ -389,6 +392,16 @@ export class tools extends plugin { if (matched) { url = url.replace(matched[0].replace("\/", ""), av2BV(Number(matched[2]))); } + // 处理下载逻辑 + if (e.msg !== undefined && e.msg.startsWith("下载")) { + // 检测是否扫码了,如果没有扫码数据终止下载 + if (_.isEmpty(this.biliSessData)) { + e.reply("检测到没有填写biliSessData,下载终止!"); + return true; + } + await this.downloadBiliVideo(e, url, this.biliSessData); + return true; + } // 只提取音乐处理 if (e.msg !== undefined && e.msg.includes("bili音乐")) { return await this.biliMusic(e, url); @@ -465,6 +478,52 @@ export class tools extends plugin { return true; } + /** + * 下载哔哩哔哩最高画质视频 + * @param e 交互事件 + * @param url 下载链接 + * @param SESSDATA ck + * @returns {Promise} + */ + async downloadBiliVideo(e, url, SESSDATA) { + const videoId = /video\/[^\?\/ ]+/.exec(url)[0].split("/")[1]; + const dash = await getBiliVideoWithSession(videoId, "", SESSDATA); + // 限制时长,防止下载大视频卡死。暂时这样设计 + const curDuration = dash.duration; + const isLimitDuration = curDuration > this.biliDuration; + if (isLimitDuration) { + const durationInMinutes = (curDuration / 60).toFixed(0); + e.reply(`当前视频(${ videoId })时长为 ${ durationInMinutes },大于管理员设置的时长 ${ this.biliDuration / 60 }`); + return true; + } + // 获取关键信息 + const { video, audio } = dash; + const videoData = video?.[0]; + const audioData = audio?.[0]; + // 提取信息 + const { height, frameRate, baseUrl: videoBaseUrl } = videoData; + const { baseUrl: audioBaseUrl } = audioData; + e.reply(`正在下载${ height }p ${ Math.trunc(frameRate) }帧数 视频,请稍候...`); + const path = `${ this.getCurDownloadPath(e) }/`; + // 添加下载任务到并发队列 + this.queue.add(() => this.downBili(`${ path }${ videoId }`, videoBaseUrl, audioBaseUrl) + .then(_ => { + Bot.acquireGfs(e.group_id).upload(fs.readFileSync(`${ path }${ videoId }.mp4`)) + }) + .then(_ => { + // 清除文件 + fs.unlinkSync(`${ path }${ videoId }.mp4`); + }) + .catch(err => { + logger.error(`${[R插件][B站下载引擎] }err`); + e.reply("解析失败,请重试一下"); + }) + ); + logger.mark(`[R插件][B站下载引擎] 当前下载队列大小${ this.queue.size }`); + + return true; + } + // 下载哔哩哔哩音乐 async biliMusic(e, url) { const videoId = /video\/[^\?\/ ]+/.exec(url)[0].split("/")[1]; diff --git a/config/tools.yaml b/config/tools.yaml index 29f6bba..97ce3f0 100644 --- a/config/tools.yaml +++ b/config/tools.yaml @@ -9,4 +9,6 @@ biliSessData: '' # 哔哩哔哩的SESSDATA biliIntroLenLimit: 50 # 哔哩哔哩简介长度限制,填 0 或者 -1 可以不做任何限制,显示完整简介 biliDuration: 480 # 哔哩哔哩限制的最大视频时长(默认8分钟),单位:秒 -douyinCookie: '' # douyin's cookie, 格式:odin_tt=xxx;sessionid_ss=xxx;ttwid=xxx;passport_csrf_token=xxx;msToken=xxx; \ No newline at end of file +douyinCookie: '' # douyin's cookie, 格式:odin_tt=xxx;sessionid_ss=xxx;ttwid=xxx;passport_csrf_token=xxx;msToken=xxx; + +queueConcurrency: 1 # 【目前只涉及哔哩哔哩的下载】根据服务器性能设置可以并发下载的个数,如果你的服务器比较强劲,就选择4~12,较弱就一个一个下载,选择1 \ No newline at end of file diff --git a/config/version.yaml b/config/version.yaml index 11549b5..25a87cd 100644 --- a/config/version.yaml +++ b/config/version.yaml @@ -1,10 +1,10 @@ - { - version: 1.6.5, + version: 1.6.6, data: [ + 新增B站下载功能, 新增B站扫码功能, 新增即刻解析功能, - 新增微视解析功能, 支持锅巴插件,方便查看和修改配置, 添加#R帮助获取插件帮助, 添加#R版本获取插件版本, diff --git a/guoba.support.js b/guoba.support.js index 48d4ed6..d95ee85 100644 --- a/guoba.support.js +++ b/guoba.support.js @@ -119,6 +119,17 @@ export function supportGuoba() { placeholder: "请输入抖音的Cookie", }, }, + { + field: "tools.queueConcurrency", + label: "并发下载个数", + bottomHelpMessage: + "【目前只涉及哔哩哔哩的下载】根据服务器性能设置可以并发下载的个数,如果你的服务器比较强劲,就选择4~12,较弱就一个一个下载,选择1", + component: "Input", + required: false, + componentProps: { + placeholder: "如果你的服务器比较强劲,就写4~12(比如4,就是可以4个人同时下载),较弱就一个一个下载,写1", + }, + }, ], getConfigData() { const toolsData = { diff --git a/package.json b/package.json index b34dd01..46188d9 100644 --- a/package.json +++ b/package.json @@ -5,6 +5,7 @@ "dependencies": { "axios": "^1.3.4", "tunnel": "^0.0.6", - "qrcode": "^1.5.3" + "qrcode": "^1.5.3", + "p-queue": "^8.0.1" } } diff --git a/utils/bilibili.js b/utils/bilibili.js index b805369..85564fc 100644 --- a/utils/bilibili.js +++ b/utils/bilibili.js @@ -187,6 +187,23 @@ export async function getBiliAudio(bvid, cid) { })) } +export async function getBiliVideoWithSession(bvid, cid, SESSDATA) { + if (!cid) { + cid = await fetchCID(bvid).catch((err) => console.log(err)) + } + // 返回一个fetch的promise + return (new Promise((resolve, reject) => { + fetch(BILI_PLAY_STREAM.replace("{bvid}", bvid).replace("{cid}", cid), { + headers: { + // SESSDATA 字段 + Cookie: `SESSDATA=${SESSDATA}` + } + }) + .then(res => res.json()) + .then(json => resolve(json.data.dash)); + })) +} + /** * bvid转换成cid * @param bvid