Spaces:
Running
Running
File size: 2,780 Bytes
08ff55b f357c45 08ff55b 31a5391 08ff55b 2811019 08ff55b 2811019 08ff55b 2811019 08ff55b 2811019 08ff55b a7536b6 08ff55b 31a5391 f357c45 df38a67 f357c45 15268b5 f357c45 08ff55b f357c45 08ff55b f357c45 08ff55b | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 | import { chromium } from 'playwright';
import { attachRecorder } from 'playwright-recorder-plus';
import fs from 'fs';
import path from 'path';
export class HtmlRender {
constructor() {
this.browser = null;
this.page = null;
}
async init() {
if (!this.browser) {
this.browser = await chromium.launch();
this.page = await this.browser.newPage();
}
}
async renderFrame(html, htmlFile, outFilePath, options = {}) {
console.log('Rendering frame from', html?.substring(0, 100) || htmlFile, '->', outFilePath)
await this.init();
const vw = (options.viewport && options.viewport.width) || 1080;
const vh = (options.viewport && options.viewport.height) || 1920;
const context = await this.browser.newContext({
viewport: { width: vw, height: vh }
});
const page = await context.newPage();
if (html) {
await page.setContent(html, { waitUntil: 'networkidle' });
} else if (htmlFile) {
await page.goto(`file://${htmlFile}`, { waitUntil: 'networkidle' });
} else {
throw new Error('Either html or htmlFile must be provided.');
}
await page.screenshot({ path: outFilePath, ...options });
}
async renderVideo(html, htmlFile, outFilePath, durationSec, options = {}) {
console.log('Rendering video from', html?.substring(0, 100) || htmlFile, '->', outFilePath, "duration", durationSec)
if (!this.browser) {
this.browser = await chromium.launch();
}
const vw = (options.viewport && options.viewport.width) || 1080;
const vh = (options.viewport && options.viewport.height) || 1920;
const context = await this.browser.newContext({
viewport: { width: vw, height: vh }
});
const page = await context.newPage();
if (html) {
await page.setContent(html, { waitUntil: 'networkidle' });
} else if (htmlFile) {
const absPath = path.isAbsolute(htmlFile) ? htmlFile : path.join(process.cwd(), htmlFile);
await page.goto(`file://${absPath}`, { waitUntil: 'networkidle' });
}
// Force a layout/paint
await page.evaluate(() => document.body.offsetHeight);
// Attach high-quality recorder
const recorder = await attachRecorder(page, {
path: outFilePath,
fps: options.fps || 30,
ffmpegOptions: {
// Ensuring high quality for the render farm
crf: 18,
preset: 'veryfast'
}
});
// Wait for the requested duration
await page.waitForTimeout(durationSec * 1000);
// Stop and finalize
await recorder.stop();
await recorder.finalized;
await page.close();
await context.close();
}
async close() {
if (this.browser) {
await this.browser.close();
this.browser = null;
this.page = null;
}
}
}
|