coze-plugin-utils
Version:
Comprehensive utility library for Coze plugins with multimedia processing, browser automation, cloud storage integration, and AI-powered video/audio generation capabilities
122 lines • 5.33 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.htmlToVideo = htmlToVideo;
exports.htmlToScreenshot = htmlToScreenshot;
const node_path_1 = __importDefault(require("node:path"));
const node_fs_1 = __importDefault(require("node:fs"));
const promises_1 = require("timers/promises");
const fluent_ffmpeg_1 = __importDefault(require("fluent-ffmpeg"));
const ffmpeg_static_1 = __importDefault(require("ffmpeg-static"));
const puppeteer_core_1 = __importDefault(require("puppeteer-core"));
const core_1 = require("../core");
async function htmlToVideo({ code, duration = 2, width, height, deviceScaleFactor = 1, sample_ratio = 1, }) {
sample_ratio = sample_ratio || 1;
const apiKey = (0, core_1.getGlobalConfig)('browser')?.apiKey;
if (!apiKey) {
throw new Error('请先配置 browser apiKey');
}
// 写入 /tmp 目录
const tmpDir = (0, core_1.createTempDir)();
const browser = await puppeteer_core_1.default.connect({
browserWSEndpoint: `wss://production-sfo.browserless.io/?token=${apiKey}`,
});
const page = await browser.newPage();
// await page.setViewport({ width, height });
// 包裹 SVG 成 HTML 页面
const html = code;
await page.setContent(html);
// 获取页面实际尺寸
const dimensions = { width, height };
if (!dimensions.width || !dimensions.height) {
const autoDimensions = await page.evaluate(() => ({
width: document.documentElement.scrollWidth,
height: document.documentElement.scrollHeight,
}));
dimensions.width = dimensions.width || autoDimensions.width;
dimensions.height = dimensions.height || autoDimensions.height;
}
await page.setViewport({ ...dimensions, deviceScaleFactor });
// 启动 DevTools 会话
const client = await page.target().createCDPSession();
// 0.1 = 动画统一放慢 sample_ratio 倍,因为要考虑 browserless 延迟,直接原速度截图生成动画会卡
await client.send('Animation.setPlaybackRate', { playbackRate: 1 / sample_ratio });
const frames = [];
await client.send('Page.startScreencast', {
format: 'png',
everyNthFrame: 1,
});
client.on('Page.screencastFrame', async (res) => {
const buffer = Buffer.from(res.data, 'base64');
frames.push(buffer);
console.log('capturing frame', frames.length, '...');
await client.send('Page.screencastFrameAck', { sessionId: res.sessionId });
});
await (0, promises_1.setTimeout)(sample_ratio * duration * 1000);
await client.send('Page.stopScreencast');
await browser.close();
const framePrefix = node_path_1.default.join(tmpDir, 'frame_');
for (let i = 0; i < frames.length; i++) {
const filename = `${framePrefix}${String(i).padStart(4, '0')}.png`;
console.log('writing frame', filename, '...');
node_fs_1.default.writeFileSync(filename, frames[i]);
}
const outputPath = node_path_1.default.join(tmpDir, 'output.mp4');
await new Promise((resolve, reject) => {
(0, fluent_ffmpeg_1.default)()
.setFfmpegPath(ffmpeg_static_1.default)
.input(`${framePrefix}%04d.png`)
.inputFPS(frames.length / duration)
.outputOptions([
'-pix_fmt yuv420p',
`-t ${duration}`,
])
.output(outputPath)
.on('end', resolve)
.on('error', reject)
.run();
});
return outputPath;
}
/**
* 截取HTML页面的屏幕截图
* @param options 截图选项
* @returns 返回图片的临时文件路径
*/
async function htmlToScreenshot({ code, width, height, deviceScaleFactor = 1, delay = 500, }) {
const apiKey = (0, core_1.getGlobalConfig)('browser')?.apiKey;
if (!apiKey) {
throw new Error('请先配置 browser apiKey');
}
// 创建临时目录
const tmpDir = (0, core_1.createTempDir)();
const browser = await puppeteer_core_1.default.connect({
browserWSEndpoint: `wss://production-sfo.browserless.io/?token=${apiKey}`,
});
const page = await browser.newPage();
// 设置HTML内容
await page.setContent(code);
// 等待页面加载完成 - 等待DOM加载完成
await page.waitForFunction(() => document.readyState === 'complete');
// 获取页面实际尺寸
const dimensions = { width, height };
if (!dimensions.width || !dimensions.height) {
const autoDimensions = await page.evaluate(() => ({
width: document.documentElement.scrollWidth,
height: document.documentElement.scrollHeight,
}));
dimensions.width = dimensions.width || autoDimensions.width;
dimensions.height = dimensions.height || autoDimensions.height;
}
await page.setViewport({ ...dimensions, deviceScaleFactor });
// 延迟指定毫秒数
await (0, promises_1.setTimeout)(delay);
// 截图
const screenshotPath = node_path_1.default.join(tmpDir, 'screenshot.png');
await page.screenshot({ path: screenshotPath, fullPage: true });
await browser.close();
return screenshotPath;
}
//# sourceMappingURL=browser.js.map