Files
server-configs/node_modules/playwright-core/lib/server/screencast.js
2026-02-13 22:24:27 +08:00

191 lines
6.7 KiB
JavaScript

"use strict";
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var screencast_exports = {};
__export(screencast_exports, {
Screencast: () => Screencast
});
module.exports = __toCommonJS(screencast_exports);
var import_path = __toESM(require("path"));
var import_utils = require("../utils");
var import_utils2 = require("../utils");
var import_videoRecorder = require("./videoRecorder");
var import_page = require("./page");
var import_registry = require("./registry");
class Screencast {
constructor(page) {
this._videoRecorder = null;
this._videoId = null;
this._screencastClients = /* @__PURE__ */ new Set();
// Aiming at 25 fps by default - each frame is 40ms, but we give some slack with 35ms.
// When throttling for tracing, 200ms between frames, except for 10 frames around the action.
this._frameThrottler = new FrameThrottler(10, 35, 200);
this._frameListener = null;
this._page = page;
}
stopFrameThrottler() {
this._frameThrottler.dispose();
}
setOptions(options) {
this._setOptions(options).catch((e) => import_utils2.debugLogger.log("error", e));
this._frameThrottler.setThrottlingEnabled(!!options);
}
throttleFrameAck(ack) {
this._frameThrottler.ack(ack);
}
temporarilyDisableThrottling() {
this._frameThrottler.recharge();
}
launchVideoRecorder() {
const recordVideo = this._page.browserContext._options.recordVideo;
if (!recordVideo)
return void 0;
(0, import_utils.assert)(!this._videoId);
this._videoId = (0, import_utils.createGuid)();
const outputFile = import_path.default.join(recordVideo.dir, this._videoId + ".webm");
const videoOptions = {
// validateBrowserContextOptions ensures correct video size.
...recordVideo.size,
outputFile
};
const ffmpegPath = import_registry.registry.findExecutable("ffmpeg").executablePathOrDie(this._page.browserContext._browser.sdkLanguage());
this._videoRecorder = new import_videoRecorder.VideoRecorder(ffmpegPath, videoOptions);
this._frameListener = import_utils.eventsHelper.addEventListener(this._page, import_page.Page.Events.ScreencastFrame, (frame) => this._videoRecorder.writeFrame(frame.buffer, frame.frameSwapWallTime / 1e3));
this._page.waitForInitializedOrError().then((p) => {
if (p instanceof Error)
this.stopVideoRecording().catch(() => {
});
});
return videoOptions;
}
async startVideoRecording(options) {
const videoId = this._videoId;
(0, import_utils.assert)(videoId);
this._page.once(import_page.Page.Events.Close, () => this.stopVideoRecording().catch(() => {
}));
const gotFirstFrame = new Promise((f) => this._page.once(import_page.Page.Events.ScreencastFrame, f));
await this._startScreencast(this._videoRecorder, {
quality: 90,
width: options.width,
height: options.height
});
gotFirstFrame.then(() => {
this._page.browserContext._browser._videoStarted(this._page.browserContext, videoId, options.outputFile, this._page.waitForInitializedOrError());
});
}
async stopVideoRecording() {
if (!this._videoId)
return;
if (this._frameListener)
import_utils.eventsHelper.removeEventListeners([this._frameListener]);
this._frameListener = null;
const videoId = this._videoId;
this._videoId = null;
const videoRecorder = this._videoRecorder;
this._videoRecorder = null;
await this._stopScreencast(videoRecorder);
await videoRecorder.stop();
const video = this._page.browserContext._browser._takeVideo(videoId);
video?.reportFinished();
}
async _setOptions(options) {
if (options)
await this._startScreencast(this, options);
else
await this._stopScreencast(this);
}
async _startScreencast(client, options) {
this._screencastClients.add(client);
if (this._screencastClients.size === 1) {
await this._page.delegate.startScreencast({
width: options.width,
height: options.height,
quality: options.quality
});
}
}
async _stopScreencast(client) {
this._screencastClients.delete(client);
if (!this._screencastClients.size)
await this._page.delegate.stopScreencast();
}
}
class FrameThrottler {
constructor(nonThrottledFrames, defaultInterval, throttlingInterval) {
this._acks = [];
this._throttlingEnabled = false;
this._nonThrottledFrames = nonThrottledFrames;
this._budget = nonThrottledFrames;
this._defaultInterval = defaultInterval;
this._throttlingInterval = throttlingInterval;
this._tick();
}
dispose() {
if (this._timeoutId) {
clearTimeout(this._timeoutId);
this._timeoutId = void 0;
}
}
setThrottlingEnabled(enabled) {
this._throttlingEnabled = enabled;
}
recharge() {
for (const ack of this._acks)
ack();
this._acks = [];
this._budget = this._nonThrottledFrames;
if (this._timeoutId) {
clearTimeout(this._timeoutId);
this._tick();
}
}
ack(ack) {
if (!this._timeoutId) {
ack();
return;
}
this._acks.push(ack);
}
_tick() {
const ack = this._acks.shift();
if (ack) {
--this._budget;
ack();
}
if (this._throttlingEnabled && this._budget <= 0) {
this._timeoutId = setTimeout(() => this._tick(), this._throttlingInterval);
} else {
this._timeoutId = setTimeout(() => this._tick(), this._defaultInterval);
}
}
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
Screencast
});