mirror of
https://github.com/ctrlcvs/xiaoyao-cvs-plugin.git
synced 2024-12-23 11:40:51 +08:00
203 lines
5.4 KiB
JavaScript
203 lines
5.4 KiB
JavaScript
|
import template from "art-template";
|
|||
|
import fs from "fs";
|
|||
|
import puppeteer from "puppeteer";
|
|||
|
import lodash from "lodash";
|
|||
|
|
|||
|
import { Data } from "../../../lib/components/index.js";
|
|||
|
|
|||
|
const _path = process.cwd();
|
|||
|
//html模板
|
|||
|
const html = {};
|
|||
|
//浏览器
|
|||
|
let browser = "";
|
|||
|
//截图数达到时重启浏览器 避免生成速度越来越慢
|
|||
|
let restartNum = 20;
|
|||
|
//截图次数
|
|||
|
let renderNum = 0;
|
|||
|
//锁住
|
|||
|
let lock = false;
|
|||
|
//截图中
|
|||
|
let shoting = [];
|
|||
|
|
|||
|
/**
|
|||
|
* 渲染生成图片,调试命令 npm run debug,window会直接打开无头浏览器
|
|||
|
*
|
|||
|
* 原始html文件路径:/resources/app/type/type.html,文件夹名要和html名一致
|
|||
|
*
|
|||
|
* 生成html文件路径:/data/html/app/type/save_id.html
|
|||
|
*
|
|||
|
* 模板生成art-template文档 http://aui.github.io/art-template/zh-cn/docs/
|
|||
|
*
|
|||
|
* @param app 应用名称
|
|||
|
* @param type 方法名
|
|||
|
* @param data 前端参数,必传 data.save_id 用来区分模板
|
|||
|
* @param imgType 图片类型 jpeg,png(清晰一点,大小更大)
|
|||
|
*/
|
|||
|
async function render1(app = "", type = "", data = {}, imgType = "jpeg") {
|
|||
|
if (lodash.isUndefined(data._res_path)) {
|
|||
|
data._res_path = `../../../../../plugins/xiaoyao-cvs-plugin/resources/`;
|
|||
|
}
|
|||
|
if (lodash.isUndefined(data._sys_res_path)) {
|
|||
|
data._sys_res_path = `../../../../../plugins/xiaoyao-cvs-plugin/resources/`;
|
|||
|
}
|
|||
|
let tplKey = `${app}.${type}`;
|
|||
|
let saveId = data.save_id || type;
|
|||
|
let tplFile = `${_path}/plugins/xiaoyao-cvs-plugin/resources/${app}/${type}.html`;
|
|||
|
Data.createDir(_path + `/data/`, `html/plugin_xiaoyao-cvs-plugin/${app}/${type}`);
|
|||
|
let savePath = _path + `/data/html/plugin_xiaoyao-cvs-plugin/${app}/${type}/${saveId}.html`;
|
|||
|
|
|||
|
return await doRender(app, type, data, imgType, {
|
|||
|
tplKey,
|
|||
|
tplFile,
|
|||
|
savePath,
|
|||
|
saveId,
|
|||
|
});
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
async function doRender(app, type, data, imgType, renderCfg) {
|
|||
|
|
|||
|
let { tplKey, tplFile, savePath, saveId } = renderCfg;
|
|||
|
|
|||
|
if (global.debugView === "web-debug") {
|
|||
|
// debug下保存当前页面的渲染数据,方便模板编写与调试
|
|||
|
// 由于只用于调试,开发者只关注自己当时开发的文件即可,暂不考虑app及plugin的命名冲突
|
|||
|
let saveDir = _path + "/data/ViewData/";
|
|||
|
if (!fs.existsSync(saveDir)) {
|
|||
|
fs.mkdirSync(saveDir);
|
|||
|
}
|
|||
|
let file = saveDir + type + ".json";
|
|||
|
data._app = app;
|
|||
|
fs.writeFileSync(file, JSON.stringify(data));
|
|||
|
|
|||
|
Bot.logger.mark(`${type}-tplFile:${tplFile}`);
|
|||
|
Bot.logger.mark(`${type}-savePath:${savePath}`);
|
|||
|
}
|
|||
|
|
|||
|
if (!html[tplKey] || global.debugView) {
|
|||
|
html[tplKey] = fs.readFileSync(tplFile, "utf8");
|
|||
|
}
|
|||
|
|
|||
|
//替换模板
|
|||
|
let tmpHtml = template.render(html[tplKey], data);
|
|||
|
//保存模板
|
|||
|
fs.writeFileSync(savePath, tmpHtml);
|
|||
|
|
|||
|
|
|||
|
if (!(await browserInit())) {
|
|||
|
return false;
|
|||
|
}
|
|||
|
|
|||
|
let base64 = "";
|
|||
|
let start = Date.now();
|
|||
|
try {
|
|||
|
shoting.push(saveId);
|
|||
|
//图片渲染
|
|||
|
const page = await browser.newPage();
|
|||
|
await page.goto("file://" + savePath);
|
|||
|
let body = await page.$("#container");
|
|||
|
let randData = {
|
|||
|
type: imgType,
|
|||
|
encoding: "base64",
|
|||
|
}
|
|||
|
if(imgType == "jpeg"){
|
|||
|
randData.quality = 100;
|
|||
|
}
|
|||
|
if(imgType == "png"){
|
|||
|
randData.omitBackground=true;
|
|||
|
}
|
|||
|
base64 = await body.screenshot(randData);
|
|||
|
if (!global.debugView) {
|
|||
|
page.close().catch((err) => Bot.logger.error(err));
|
|||
|
}
|
|||
|
shoting.pop();
|
|||
|
} catch (error) {
|
|||
|
Bot.logger.error(`图片生成失败:${type}:${error}`);
|
|||
|
//重启浏览器
|
|||
|
if (browser) {
|
|||
|
await browser.close().catch((err) => Bot.logger.error(err));
|
|||
|
}
|
|||
|
browser = "";
|
|||
|
base64 = "";
|
|||
|
return false;
|
|||
|
}
|
|||
|
|
|||
|
if (!base64) {
|
|||
|
Bot.logger.error(`图片生成为空:${type}`);
|
|||
|
return false;
|
|||
|
}
|
|||
|
|
|||
|
renderNum++;
|
|||
|
Bot.logger.mark(`图片生成 ${type}:${Date.now() - start}ms 次数:${renderNum}`);
|
|||
|
|
|||
|
if (typeof test != "undefined") {
|
|||
|
return `图片base64:${type}`;
|
|||
|
}
|
|||
|
|
|||
|
//截图超过重启数时,自动关闭重启浏览器,避免生成速度越来越慢
|
|||
|
if (renderNum % restartNum == 0) {
|
|||
|
if (shoting.length <= 0) {
|
|||
|
setTimeout(async function () {
|
|||
|
browser.removeAllListeners("disconnected");
|
|||
|
await browser.close().catch((err) => Bot.logger.error(err));
|
|||
|
browser = "";
|
|||
|
Bot.logger.mark("puppeteer 关闭重启");
|
|||
|
}, 100);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return base64;
|
|||
|
}
|
|||
|
|
|||
|
async function browserInit() {
|
|||
|
if (browser) {
|
|||
|
return browser;
|
|||
|
}
|
|||
|
if (lock) {
|
|||
|
return false;
|
|||
|
}
|
|||
|
lock = true;
|
|||
|
Bot.logger.mark("puppeteer 启动中。。");
|
|||
|
//初始化puppeteer
|
|||
|
browser = await puppeteer
|
|||
|
.launch({
|
|||
|
// executablePath:'',//chromium其他路径
|
|||
|
headless: global.debugView === "debug" ? false : true,
|
|||
|
args: [
|
|||
|
"--disable-gpu",
|
|||
|
"--disable-dev-shm-usage",
|
|||
|
"--disable-setuid-sandbox",
|
|||
|
"--no-first-run",
|
|||
|
"--no-sandbox",
|
|||
|
"--no-zygote",
|
|||
|
"--single-process",
|
|||
|
],
|
|||
|
})
|
|||
|
.catch((err) => {
|
|||
|
Bot.logger.error(err);
|
|||
|
if(String(err).includes("correct Chromium")){
|
|||
|
Bot.logger.error("没有正确安装Chromium,可以尝试执行安装命令:node ./node_modules/puppeteer/install.js");
|
|||
|
}
|
|||
|
});
|
|||
|
|
|||
|
lock = false;
|
|||
|
|
|||
|
if (browser) {
|
|||
|
Bot.logger.mark("puppeteer 启动成功");
|
|||
|
|
|||
|
//监听Chromium实例是否断开
|
|||
|
browser.on("disconnected", function (e) {
|
|||
|
Bot.logger.error("Chromium实例关闭或崩溃!");
|
|||
|
browser = "";
|
|||
|
});
|
|||
|
|
|||
|
return browser;
|
|||
|
} else {
|
|||
|
Bot.logger.error("puppeteer 启动失败");
|
|||
|
return false;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
export { render1, browserInit, renderNum };
|