mirror of
https://github.com/Jerryplusy/rc-plugin.git
synced 2025-10-14 08:09:19 +00:00
✨ feat: 1.6.6 [#I9E25J] 增强downloadVideo
下载函数功能 & 删除青年大学习
& 格式化PR代码
1. `downloadVideo`函数现在支持多线程下载,速度巨tm快,比之前的速度快了200%以上 2. `青年大学习`功能退役 3. 格式化PR代码
This commit is contained in:
parent
98cff7eec1
commit
b70b5f5e89
103
apps/query.js
103
apps/query.js
@ -46,10 +46,6 @@ export class query extends plugin {
|
||||
reg: "^#累了$",
|
||||
fnc: "cospro",
|
||||
},
|
||||
{
|
||||
reg: "^#青年大学习$",
|
||||
fnc: "youthLearning",
|
||||
},
|
||||
{
|
||||
reg: "^#搜书(.*)$",
|
||||
fnc: "searchBook",
|
||||
@ -181,105 +177,6 @@ export class query extends plugin {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 青年大学习
|
||||
async youthLearning(e) {
|
||||
await axios
|
||||
.get(
|
||||
"https://qczj.h5yunban.com/qczj-youth-learning/cgi-bin/common-api/course/current",
|
||||
{
|
||||
headers: {
|
||||
"User-Agent":
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.69 Safari/537.36 Edg/95.0.1020.53",
|
||||
},
|
||||
},
|
||||
)
|
||||
.then(resp => {
|
||||
// logger.info(resp.data);
|
||||
return resp.data.result.uri.replace("index.html", "m.html");
|
||||
})
|
||||
.then(async uri => {
|
||||
axios
|
||||
.get(uri, {
|
||||
headers: {
|
||||
"User-Agent":
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.69 Safari/537.36 Edg/95.0.1020.53",
|
||||
},
|
||||
})
|
||||
.then(resp => {
|
||||
const content = resp.data;
|
||||
const resList = content.match(/<div class="w\d option" (.*)><\/div>/g);
|
||||
const valueList = resList.map(item => {
|
||||
return item.match(/data-a="(\d+)"/)[1];
|
||||
});
|
||||
let result = [];
|
||||
// 转换ABCD
|
||||
const digitToLetter = {
|
||||
0: "A",
|
||||
1: "B",
|
||||
2: "C",
|
||||
3: "D",
|
||||
};
|
||||
for (let i = 0; i < valueList.length; i += 4) {
|
||||
const group = valueList.slice(i, i + 4);
|
||||
if (group.length < 4) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const letters = group
|
||||
.map((d, indx) => {
|
||||
if (d === "1") {
|
||||
return digitToLetter[indx];
|
||||
}
|
||||
})
|
||||
.join("");
|
||||
result.push(letters);
|
||||
}
|
||||
// 封装答案
|
||||
let ans = "";
|
||||
for (let i = 0; i < result.length; i++) {
|
||||
ans += `${ i + 1 }. ${ result[i] }\n`;
|
||||
}
|
||||
e.reply(ans);
|
||||
const imgMatch = uri.match(/[^\/]+/g);
|
||||
const imgId = imgMatch[imgMatch.length - 2];
|
||||
|
||||
axios
|
||||
.get(`https://h5.cyol.com/special/daxuexi/${ imgId }/images/end.jpg`, {
|
||||
headers: {
|
||||
"User-Agent":
|
||||
"Mozilla/5.0 (Linux; Android 5.0; SM-G900P Build/LRX21T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.25 Mobile Safari/537.36",
|
||||
},
|
||||
responseType: "stream",
|
||||
})
|
||||
.then(resp => {
|
||||
const filePath = "./youthLearning.png";
|
||||
const writer = fs.createWriteStream(filePath);
|
||||
resp.data.pipe(writer);
|
||||
return new Promise((resolve, reject) => {
|
||||
writer.on("finish", () => {
|
||||
writer.close(() => {
|
||||
resolve(filePath);
|
||||
});
|
||||
});
|
||||
writer.on("error", err => {
|
||||
fs.unlink(filePath, () => {
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
});
|
||||
})
|
||||
.then(filePath => {
|
||||
e.reply(segment.image(fs.readFileSync(filePath)));
|
||||
fs.unlinkSync(filePath, err => {
|
||||
if (err) throw err;
|
||||
logger.error("删除青年大学习文件失败");
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
async cospro(e) {
|
||||
let [res1, res2] = (
|
||||
await Promise.allSettled([
|
||||
|
176
apps/tools.js
176
apps/tools.js
@ -186,6 +186,8 @@ export class tools extends plugin {
|
||||
});
|
||||
// 并发队列
|
||||
this.queue = new PQueue({concurrency: Number(this.toolsConfig.queueConcurrency)});
|
||||
// 视频下载的并发数量
|
||||
this.videoDownloadConcurrency = this.toolsConfig.videoDownloadConcurrency;
|
||||
}
|
||||
|
||||
// 翻译插件
|
||||
@ -260,7 +262,7 @@ export class tools extends plugin {
|
||||
);
|
||||
const path = `${ this.getCurDownloadPath(e) }/temp.mp4`;
|
||||
await this.downloadVideo(resUrl).then(() => {
|
||||
this.video_file(e, path)
|
||||
this.sendVideoToUpload(e, path)
|
||||
});
|
||||
} else if (urlType === "image") {
|
||||
// 无水印图片列表
|
||||
@ -464,7 +466,7 @@ export class tools extends plugin {
|
||||
.then(data => {
|
||||
this.downBili(`${ path }temp`, data.videoUrl, data.audioUrl)
|
||||
.then(_ => {
|
||||
this.video_file(e, `${ path }temp.mp4`)
|
||||
this.sendVideoToUpload(e, `${ path }temp.mp4`)
|
||||
})
|
||||
.catch(err => {
|
||||
logger.error(err);
|
||||
@ -754,7 +756,7 @@ export class tools extends plugin {
|
||||
parseM3u8(res.urlM3u8s[res.urlM3u8s.length - 1]).then(res2 => {
|
||||
downloadM3u8Videos(res2.m3u8FullUrls, path).then(_ => {
|
||||
mergeAcFileToMp4(res2.tsNames, path, `${ path }out.mp4`).then(_ => {
|
||||
this.video_file(e, `${ path }out.mp4`)
|
||||
this.sendVideoToUpload(e, `${ path }out.mp4`)
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -814,7 +816,7 @@ export class tools extends plugin {
|
||||
// 创建文件,如果不存在
|
||||
path = `${ this.getCurDownloadPath(e) }/`;
|
||||
}
|
||||
this.video_file(e, `${ path }/temp.mp4`)
|
||||
this.sendVideoToUpload(e, `${ path }/temp.mp4`)
|
||||
});
|
||||
return true;
|
||||
} else if (type === "normal") {
|
||||
@ -1093,7 +1095,7 @@ export class tools extends plugin {
|
||||
"accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",
|
||||
"referer": "https://weibo.com/",
|
||||
}).then(path => {
|
||||
this.video_file(e, `${ path }/temp.mp4`)
|
||||
this.sendVideoToUpload(e, `${ path }/temp.mp4`)
|
||||
});
|
||||
} catch (err) {
|
||||
e.reply("视频资源获取失败");
|
||||
@ -1127,7 +1129,7 @@ export class tools extends plugin {
|
||||
// 视频:https://www.kuaishou.com/short-video/3xhjgcmir24m4nm
|
||||
const url = adapter.video;
|
||||
this.downloadVideo(url).then(path => {
|
||||
this.video_file(e, `${ path }/temp.mp4`)
|
||||
this.sendVideoToUpload(e, `${ path }/temp.mp4`)
|
||||
});
|
||||
} else {
|
||||
e.reply("解析失败:无法获取到资源");
|
||||
@ -1262,7 +1264,7 @@ export class tools extends plugin {
|
||||
// 暂时选取分辨率较低的video进行解析
|
||||
const videoUrl = resolutions[i].url;
|
||||
this.downloadVideo(videoUrl).then(path => {
|
||||
this.video_file(e, `${ path }/temp.mp4`)
|
||||
this.sendVideoToUpload(e, `${ path }/temp.mp4`)
|
||||
});
|
||||
break;
|
||||
}
|
||||
@ -1377,7 +1379,7 @@ export class tools extends plugin {
|
||||
e.reply([segment.image(cover), `识别:微视,${ title }`]);
|
||||
|
||||
this.downloadVideo(noWatermarkDownloadUrl).then(path => {
|
||||
this.video_file(e, `${ path }/temp.mp4`)
|
||||
this.sendVideoToUpload(e, `${ path }/temp.mp4`)
|
||||
});
|
||||
} catch (err) {
|
||||
logger.error(err);
|
||||
@ -1441,7 +1443,7 @@ export class tools extends plugin {
|
||||
}
|
||||
if (shortVideoInfo.noWatermarkDownloadUrl) {
|
||||
this.downloadVideo(shortVideoInfo.noWatermarkDownloadUrl).then(path => {
|
||||
this.video_file(e, `${ path }/temp.mp4`)
|
||||
this.sendVideoToUpload(e, `${ path }/temp.mp4`)
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
@ -1529,46 +1531,113 @@ export class tools extends plugin {
|
||||
}
|
||||
|
||||
/**
|
||||
* 工具:根URL据下载视频 / 音频
|
||||
* @param url 下载地址
|
||||
* @param isProxy 是否需要魔法
|
||||
* @param headers 覆盖头节点
|
||||
* @returns {Promise<unknown>}
|
||||
* 工具:根据URL多线程下载视频 / 音频
|
||||
* @param url
|
||||
* @param isProxy
|
||||
* @param headers
|
||||
* @param numThreads
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async downloadVideo(url, isProxy = false, headers = null) {
|
||||
async downloadVideo(url, isProxy = false, headers = null, numThreads = 1) {
|
||||
const { groupPath, target } = this.getGroupPathAndTarget.call(this);
|
||||
|
||||
await mkdirIfNotExists(groupPath);
|
||||
const userAgent = "Mozilla/5.0 (Linux; Android 5.0; SM-G900P Build/LRX21T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.25 Mobile Safari/537.36";
|
||||
// 用户设置优先策略,逻辑解释:如果使用了这个函数优先查看用户是否设置了大于1的线程,如果设置了优先使用,没设置就开发者设定的函数设置
|
||||
numThreads = this.videoDownloadConcurrency !== 1 ? this.videoDownloadConcurrency : numThreads;
|
||||
|
||||
const userAgent =
|
||||
"Mozilla/5.0 (Linux; Android 5.0; SM-G900P Build/LRX21T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.25 Mobile Safari/537.36";
|
||||
const axiosConfig = {
|
||||
headers: headers || { "User-Agent": userAgent },
|
||||
responseType: "stream",
|
||||
...(isProxy && {
|
||||
httpAgent: tunnel.httpOverHttp({
|
||||
proxy: { host: this.proxyAddr, port: this.proxyPort },
|
||||
// 如果是用户设置了单线程,则不分片下载
|
||||
if (numThreads === 1) {
|
||||
const axiosConfig = {
|
||||
headers: headers || { "User-Agent": userAgent },
|
||||
responseType: "stream",
|
||||
...(isProxy && {
|
||||
httpAgent: tunnel.httpOverHttp({
|
||||
proxy: { host: this.proxyAddr, port: this.proxyPort },
|
||||
}),
|
||||
httpsAgent: tunnel.httpsOverHttp({
|
||||
proxy: { host: this.proxyAddr, port: this.proxyPort },
|
||||
}),
|
||||
}),
|
||||
httpsAgent: tunnel.httpsOverHttp({
|
||||
proxy: { host: this.proxyAddr, port: this.proxyPort },
|
||||
}),
|
||||
}),
|
||||
};
|
||||
};
|
||||
|
||||
try {
|
||||
await checkAndRemoveFile(target);
|
||||
try {
|
||||
await checkAndRemoveFile(target);
|
||||
|
||||
const res = await axios.get(url, axiosConfig);
|
||||
logger.mark(`开始下载: ${ url }`);
|
||||
const writer = fs.createWriteStream(target);
|
||||
res.data.pipe(writer);
|
||||
const res = await axios.get(url, axiosConfig);
|
||||
logger.mark(`开始下载: ${ url }`);
|
||||
const writer = fs.createWriteStream(target);
|
||||
res.data.pipe(writer);
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
writer.on("finish", () => resolve(groupPath));
|
||||
writer.on("error", reject);
|
||||
});
|
||||
} catch (err) {
|
||||
logger.error(`下载视频发生错误!\ninfo:${ err }`);
|
||||
return new Promise((resolve, reject) => {
|
||||
writer.on("finish", () => resolve(groupPath));
|
||||
writer.on("error", reject);
|
||||
});
|
||||
} catch (err) {
|
||||
logger.error(`下载视频发生错误!\ninfo:${ err }`);
|
||||
}
|
||||
} else {
|
||||
// 多线程分片下载
|
||||
try {
|
||||
await checkAndRemoveFile(target);
|
||||
const sizeRes = await axios.head(url);
|
||||
const contentLength = sizeRes.headers['content-length'];
|
||||
const chunkSize = Math.ceil(contentLength / numThreads);
|
||||
let promises = [];
|
||||
|
||||
for (let i = 0; i < numThreads; i++) {
|
||||
let start = i * chunkSize;
|
||||
let end = (i + 1) * chunkSize - 1;
|
||||
if (i === numThreads - 1) end = ''; // Last chunk goes till the end
|
||||
const axiosConfig = {
|
||||
headers: {
|
||||
"User-Agent": userAgent,
|
||||
"Range": `bytes=${start}-${end}`,
|
||||
...headers
|
||||
},
|
||||
responseType: 'stream',
|
||||
...(isProxy && {
|
||||
httpAgent: tunnel.httpOverHttp({
|
||||
proxy: { host: 'proxyAddress', port: 'proxyPort' },
|
||||
}),
|
||||
httpsAgent: tunnel.httpsOverHttp({
|
||||
proxy: { host: 'proxyAddress', port: 'proxyPort' },
|
||||
}),
|
||||
}),
|
||||
};
|
||||
promises.push(axios.get(url, axiosConfig));
|
||||
}
|
||||
|
||||
const writer = fs.createWriteStream(target, { flags: 'a' });
|
||||
|
||||
// 同时下载所有部分
|
||||
await Promise.all(promises.map(async (promise, index) => {
|
||||
const res = await promise;
|
||||
logger.mark(`开始下载部分: ${index + 1}`);
|
||||
res.data.pipe(writer, { end: false });
|
||||
await new Promise((resolve, reject) => {
|
||||
res.data.on('end', () => {
|
||||
logger.mark(`部分 ${index + 1} 下载完成`);
|
||||
resolve();
|
||||
});
|
||||
res.data.on('error', reject);
|
||||
});
|
||||
}));
|
||||
|
||||
// 注意这里不应该在每个分块结束后立即调用writer.end()
|
||||
// 我们只在所有分块都已经pipe完毕后调用writer.end()
|
||||
writer.end();
|
||||
await new Promise((resolve, reject) => {
|
||||
writer.on('finish', () => {
|
||||
logger.mark(`所有部分下载完成,文件已保存至 ${target}`);
|
||||
resolve(target); // 返回目标文件路径
|
||||
});
|
||||
writer.on('error', reject);
|
||||
});
|
||||
} catch (err) {
|
||||
logger.error(`下载视频发生错误!\ninfo:${err}`);
|
||||
// 处理或抛出错误
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1629,20 +1698,21 @@ export class tools extends plugin {
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传视频文件
|
||||
* @param {*} e
|
||||
* @param {*} path
|
||||
* 发送转上传视频
|
||||
* @param e 交互事件
|
||||
* @param path 视频所在路径
|
||||
* @param videoSizeLimit 发送转上传视频的大小限制,默认70MB
|
||||
*/
|
||||
async video_file(e, path) {
|
||||
if(!fs.existsSync(path)) return e.reply('视频不存在')
|
||||
const stats = fs.statSync(path)
|
||||
const Video_size = (stats.size / (1024 * 1024)).toFixed(2)
|
||||
if (Video_size > 70) {
|
||||
e.reply(`当前视频大小:${ Video_size }MB,\n大于设置的最大限制,\n改为上传群文件`)
|
||||
if(this.e.bot?.sendUni) {
|
||||
this.e.group.fs.upload(path)
|
||||
async sendVideoToUpload(e, path, videoSizeLimit = 70) {
|
||||
if (!fs.existsSync(path)) return e.reply('视频不存在');
|
||||
const stats = fs.statSync(path);
|
||||
const videoSize = (stats.size / (1024 * 1024)).toFixed(2);
|
||||
if (videoSize > videoSizeLimit) {
|
||||
e.reply(`当前视频大小:${ videoSize }MB,\n大于设置的最大限制,\n改为上传群文件`);
|
||||
if (this.e.bot?.sendUni) {
|
||||
this.e.group.fs.upload(path);
|
||||
} else {
|
||||
this.e.group.sendFile(path)
|
||||
this.e.group.sendFile(path);
|
||||
}
|
||||
} else {
|
||||
e.reply(segment.video(path));
|
||||
|
@ -11,4 +11,6 @@ biliDuration: 480 # 哔哩哔哩限制的最大视频时长(默认8分钟)
|
||||
|
||||
douyinCookie: '' # douyin's cookie, 格式:odin_tt=xxx;sessionid_ss=xxx;ttwid=xxx;passport_csrf_token=xxx;msToken=xxx;
|
||||
|
||||
queueConcurrency: 1 # 【目前只涉及哔哩哔哩的下载】根据服务器性能设置可以并发下载的个数,如果你的服务器比较强劲,就选择4~12,较弱就一个一个下载,选择1
|
||||
queueConcurrency: 1 # 【目前只涉及哔哩哔哩的下载】根据服务器性能设置可以并发下载的个数,如果你的服务器比较强劲,就选择4~12,较弱就一个一个下载,选择1
|
||||
|
||||
videoDownloadConcurrency: 1 # 下载视频是否使用多线程,如果不使用默认是1,如果使用根据服务器进行选择,如果不确定是否可以用4即可,高性能服务器随意4~12都可以,看CPU的实力
|
@ -121,15 +121,26 @@ export function supportGuoba() {
|
||||
},
|
||||
{
|
||||
field: "tools.queueConcurrency",
|
||||
label: "并发下载个数",
|
||||
label: "允许多用户下载个数",
|
||||
bottomHelpMessage:
|
||||
"【目前只涉及哔哩哔哩的下载】根据服务器性能设置可以并发下载的个数,如果你的服务器比较强劲,就选择4~12,较弱就一个一个下载,选择1",
|
||||
"【目前只涉及哔哩哔哩的下载功能】根据服务器性能设置可以并发下载的个数,如果你的服务器比较强劲,就选择4~12,较弱就一个一个下载,选择1",
|
||||
component: "Input",
|
||||
required: false,
|
||||
componentProps: {
|
||||
placeholder: "如果你的服务器比较强劲,就写4~12(比如4,就是可以4个人同时下载),较弱就一个一个下载,写1",
|
||||
},
|
||||
},
|
||||
{
|
||||
field: "tools.videoDownloadConcurrency",
|
||||
label: "使用下载视频的并发个数",
|
||||
bottomHelpMessage:
|
||||
"与【允许多用户下载个数】不同,这个功能影响下载速度。默认是1,使用根据服务器性能进行选择,如果不确定是否可以用1即可,高性能服务器随意4~12都可以,看CPU的实力",
|
||||
component: "Input",
|
||||
required: false,
|
||||
componentProps: {
|
||||
placeholder: "不确定用1即可,高性能服务器随意4~12都可以,看CPU的实力",
|
||||
},
|
||||
},
|
||||
],
|
||||
getConfigData() {
|
||||
const toolsData = {
|
||||
|
Loading…
x
Reference in New Issue
Block a user