diff --git a/apps/render.js b/apps/render.js new file mode 100644 index 0000000..dc9dc66 --- /dev/null +++ b/apps/render.js @@ -0,0 +1,202 @@ +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 }; diff --git a/components/Common.js b/components/Common.js index 54d5a9d..683c8ea 100644 --- a/components/Common.js +++ b/components/Common.js @@ -1,12 +1,12 @@ import { Cfg } from "./index.js"; import { segment } from "oicq"; import { currentVersion, yunzaiVersion } from "./Changelog.js"; - +import {render1} from "../apps/render.js"; export const render = async function (path, params, cfg) { let paths = path.split("/"); let { render, e } = cfg; let _layout_path = process.cwd() + "/plugins/xiaoyao-cvs-plugin/resources/"; - let base64 = await render(paths[0], paths[1], { + let base64 = await render1(paths[0], paths[1], { ...params, _layout_path, defaultLayout: _layout_path + "default.html", diff --git a/components/cfg.json b/components/cfg.json index 877284b..036e2a3 100644 --- a/components/cfg.json +++ b/components/cfg.json @@ -1,6 +1,6 @@ { "sys": { "help": true, - "Note": false + "Note": true } } \ No newline at end of file