UNPKG

@remotion/renderer

Version:

Render Remotion videos using Node.js or Bun

238 lines (237 loc) • 8.77 kB
"use strict"; /** * Copyright 2017 Google Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ Object.defineProperty(exports, "__esModule", { value: true }); exports.BrowserContext = exports.HeadlessBrowser = void 0; const assert_1 = require("./assert"); const BrowserRunner_1 = require("./BrowserRunner"); const EventEmitter_1 = require("./EventEmitter"); const Target_1 = require("./Target"); const util_1 = require("./util"); class HeadlessBrowser extends EventEmitter_1.EventEmitter { static async create({ defaultViewport, timeout, userDataDir, args, executablePath, logLevel, indent, }) { const runner = await (0, BrowserRunner_1.makeBrowserRunner)({ executablePath, processArguments: args, userDataDir, indent, logLevel, timeout, }); const browser = new HeadlessBrowser({ connection: runner.connection, defaultViewport, runner, }); await runner.connection.send('Target.setDiscoverTargets', { discover: true }); return browser; } #defaultViewport; connection; #defaultContext; #contexts; #targets; id; runner; get _targets() { return this.#targets; } constructor({ connection, defaultViewport, runner, }) { super(); this.#defaultViewport = defaultViewport; this.connection = connection; this.id = Math.random().toString(36).substring(2, 15); this.#defaultContext = new BrowserContext(this); this.#contexts = new Map(); this.#targets = new Map(); this.connection.on('Target.targetCreated', this.#targetCreated.bind(this)); this.connection.on('Target.targetDestroyed', this.#targetDestroyed.bind(this)); this.connection.on('Target.targetInfoChanged', this.#targetInfoChanged.bind(this)); this.runner = runner; } browserContexts() { return [this.#defaultContext, ...Array.from(this.#contexts.values())]; } async #targetCreated(event) { var _a; const { targetInfo } = event; const { browserContextId } = targetInfo; const context = browserContextId && this.#contexts.has(browserContextId) ? this.#contexts.get(browserContextId) : this.#defaultContext; if (!context) { throw new Error('Missing browser context'); } const target = new Target_1.Target(targetInfo, context, () => { return this.connection.createSession(targetInfo); }, (_a = this.#defaultViewport) !== null && _a !== void 0 ? _a : null); (0, assert_1.assert)(!this.#targets.has(event.targetInfo.targetId), 'Target should not exist before targetCreated'); this.#targets.set(event.targetInfo.targetId, target); if (await target._initializedPromise) { this.emit("targetcreated" /* BrowserEmittedEvents.TargetCreated */, target); } } #targetDestroyed(event) { const target = this.#targets.get(event.targetId); if (!target) { throw new Error(`Missing target in _targetDestroyed (id = ${event.targetId})`); } target._initializedCallback(false); this.#targets.delete(event.targetId); target._closedCallback(); } #targetInfoChanged(event) { const target = this.#targets.get(event.targetInfo.targetId); if (!target) { throw new Error(`Missing target in targetInfoChanged (id = ${event.targetInfo.targetId})`); } const previousURL = target.url(); const wasInitialized = target._isInitialized; target._targetInfoChanged(event.targetInfo); if (wasInitialized && previousURL !== target.url()) { this.emit("targetchanged" /* BrowserEmittedEvents.TargetChanged */, target); } } newPage({ context, logLevel, indent, pageIndex, onBrowserLog, onLog, }) { return this.#defaultContext.newPage({ context, logLevel, indent, pageIndex, onBrowserLog, onLog, }); } async _createPageInContext({ context, logLevel, indent, pageIndex, onBrowserLog, onLog, }) { const { value: { targetId }, } = await this.connection.send('Target.createTarget', { url: 'about:blank', browserContextId: undefined, }); const target = this.#targets.get(targetId); if (!target) { throw new Error(`Missing target for page (id = ${targetId})`); } const initialized = await target._initializedPromise; if (!initialized) { throw new Error(`Failed to create target for page (id = ${targetId})`); } const page = await target.page({ sourceMapGetter: context, logLevel, indent, pageIndex, onBrowserLog, onLog, }); if (!page) { throw new Error(`Failed to create a page for context`); } return page; } targets() { return Array.from(this.#targets.values()).filter((target) => { return target._isInitialized; }); } async waitForTarget(predicate, options = {}) { const { timeout = 30000 } = options; let resolve; let isResolved = false; const targetPromise = new Promise((x) => { resolve = x; }); this.on("targetcreated" /* BrowserEmittedEvents.TargetCreated */, check); this.on("targetchanged" /* BrowserEmittedEvents.TargetChanged */, check); try { if (!timeout) { return await targetPromise; } this.targets().forEach(check); return await (0, util_1.waitWithTimeout)(targetPromise, 'target', timeout, this); } finally { this.off("targetcreated" /* BrowserEmittedEvents.TargetCreated */, check); this.off("targetchanged" /* BrowserEmittedEvents.TargetChanged */, check); } async function check(target) { if ((await predicate(target)) && !isResolved) { isResolved = true; resolve(target); } } } async pages() { const contextPages = await Promise.all(this.browserContexts().map((context) => { return context.pages(); })); // Flatten array. return contextPages.reduce((acc, x) => { return acc.concat(x); }, []); } async close({ silent }) { await this.runner.closeProcess(); (await this.pages()).forEach((page) => { page.emit("disposed" /* PageEmittedEvents.Disposed */); page.closed = true; }); this.disconnect(); this.emit(silent ? "closed-silent" /* BrowserEmittedEvents.ClosedSilent */ : "closed" /* BrowserEmittedEvents.Closed */); } disconnect() { this.connection.dispose(); } } exports.HeadlessBrowser = HeadlessBrowser; class BrowserContext extends EventEmitter_1.EventEmitter { #browser; constructor(browser) { super(); this.#browser = browser; } targets() { return this.#browser.targets().filter((target) => { return target.browserContext() === this; }); } waitForTarget(predicate, options = {}) { return this.#browser.waitForTarget((target) => { return target.browserContext() === this && predicate(target); }, options); } async pages() { const pages = await Promise.all(this.targets() .filter((target) => target.type() === 'page') .map((target) => target.expectPage())); return pages.filter((page) => { return Boolean(page); }); } newPage({ context, logLevel, indent, pageIndex, onBrowserLog, onLog, }) { return this.#browser._createPageInContext({ context, logLevel, indent, pageIndex, onBrowserLog, onLog, }); } browser() { return this.#browser; } } exports.BrowserContext = BrowserContext;