puppeteer-stream
Version:
An Extension for Puppeteer to retrieve audio and/or video streams of a page
257 lines • 21.1 kB
JavaScript
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.getStream = exports.getExtensionPage = exports.launch = exports.wss = void 0;
const puppeteer_core_1 = require("puppeteer-core");
const path = __importStar(require("path"));
const stream_1 = require("stream");
const ws_1 = __importStar(require("ws"));
const extensionId = "jjndjgheafjngoipoacpjgeicjeomjli";
let currentIndex = 0;
let port;
exports.wss = (async () => {
for (let i = 55200; i <= 65535; i++) {
const ws = new ws_1.WebSocketServer({ port: i });
const promise = await Promise.race([
new Promise((resolve) => {
ws.on("error", (e) => {
resolve(!e.message.includes("EADDRINUSE"));
});
}),
new Promise((resolve) => {
ws.on("listening", () => {
resolve(true);
});
}),
]);
if (promise) {
port = i;
return ws;
}
}
})();
async function launch(arg1, opts) {
var _a, _b;
//if puppeteer library is not passed as first argument, then first argument is options
// @ts-ignore
if (typeof arg1.launch != "function")
opts = arg1;
if (!opts)
opts = {};
if (!opts.args)
opts.args = [];
function addToArgs(arg, value) {
if (!value) {
if (opts.args.includes(arg))
return;
return opts.args.push(arg);
}
let found = false;
opts.args = opts.args.map((x) => {
if (x.includes(arg)) {
found = true;
return x + "," + value;
}
return x;
});
if (!found)
opts.args.push(arg + value);
}
if (!opts.extensionPath) {
opts.extensionPath = path.join(__dirname, "..", "extension");
}
addToArgs("--load-extension=", opts.extensionPath);
addToArgs("--disable-extensions-except=", opts.extensionPath);
addToArgs("--allowlisted-extension-id=", extensionId);
addToArgs("--autoplay-policy=no-user-gesture-required");
addToArgs("--auto-accept-this-tab-capture");
if (((_a = opts.defaultViewport) === null || _a === void 0 ? void 0 : _a.width) && ((_b = opts.defaultViewport) === null || _b === void 0 ? void 0 : _b.height)) {
opts.args.push(`--window-size=${opts.defaultViewport.width},${opts.defaultViewport.height}`);
opts.args.push(`--ozone-override-screen-size=${opts.defaultViewport.width},${opts.defaultViewport.height}`);
}
// @ts-ignore
opts.headless = opts.headless === "new" ? "new" : false;
if (opts.headless) {
if (!opts.ignoreDefaultArgs)
opts.ignoreDefaultArgs = [];
if (Array.isArray(opts.ignoreDefaultArgs) && !opts.ignoreDefaultArgs.includes("--mute-audio"))
opts.ignoreDefaultArgs.push("--mute-audio");
if (!opts.args.includes("--headless=new"))
opts.args.push("--headless=new");
}
let browser;
// @ts-ignore
if (typeof arg1.launch == "function") {
// @ts-ignore
browser = await arg1.launch(opts);
}
else {
browser = await (0, puppeteer_core_1.launch)(opts);
}
if (opts.allowIncognito) {
const settings = await browser.newPage();
await settings.goto(`chrome://extensions/?id=${extensionId}`);
await settings.evaluate(() => {
document
.querySelector("extensions-manager")
.shadowRoot.querySelector("#viewManager > extensions-detail-view.active")
.shadowRoot.querySelector("div#container.page-container > div.page-content > div#options-section extensions-toggle-row#allow-incognito")
.shadowRoot.querySelector("label#label input")
.click();
});
await settings.close();
}
(await browser.newPage()).goto(`chrome-extension://${extensionId}/options.html#${port}`);
const old_browser_close = browser.close;
browser.close = async () => {
for (const page of await browser.pages()) {
if (!page.url().startsWith(`chrome-extension://${extensionId}/options.html`)) {
await page.close();
}
}
const extension = await getExtensionPage(browser);
await extension.evaluate(async () => {
return chrome.tabs.query({});
});
if (opts.closeDelay) {
await new Promise((r) => setTimeout(r, opts.closeDelay));
}
await old_browser_close.call(browser);
};
return browser;
}
exports.launch = launch;
async function getExtensionPage(browser) {
const extensionTarget = await browser.waitForTarget((target) => {
return target.type() === "page" && target.url().startsWith(`chrome-extension://${extensionId}/options.html`);
});
if (!extensionTarget)
throw new Error("cannot load extension");
const videoCaptureExtension = await extensionTarget.page();
if (!videoCaptureExtension)
throw new Error("cannot get page of extension");
return videoCaptureExtension;
}
exports.getExtensionPage = getExtensionPage;
let mutex = false;
let queue = [];
function lock() {
return new Promise((res) => {
if (!mutex) {
mutex = true;
return res(null);
}
queue.push(res);
});
}
function unlock() {
if (queue.length)
queue.shift()();
else
mutex = false;
}
async function getStream(page, opts) {
var _a;
if (!opts.audio && !opts.video)
throw new Error("At least audio or video must be true");
if (!opts.mimeType) {
if (opts.video)
opts.mimeType = "video/webm";
else if (opts.audio)
opts.mimeType = "audio/webm";
}
if (!opts.frameSize)
opts.frameSize = 20;
const retryPolicy = Object.assign({}, { each: 20, times: 3 }, opts.retry);
const extension = await getExtensionPage(page.browser());
const highWaterMarkMB = ((_a = opts.streamConfig) === null || _a === void 0 ? void 0 : _a.highWaterMarkMB) || 8;
const index = currentIndex++;
await lock();
await page.bringToFront();
const [tab] = await extension.evaluate(async (x) => {
// @ts-ignore
return chrome.tabs.query(x);
}, opts.tabQuery || {
active: true,
});
unlock();
if (!tab)
throw new Error("Cannot find tab, try providing your own tabQuery to getStream options");
const stream = new stream_1.Transform({
highWaterMark: 1024 * 1024 * highWaterMarkMB,
transform(chunk, encoding, callback) {
callback(null, chunk);
},
});
function onConnection(ws, req) {
const url = new URL(`http://localhost:${port}${req.url}`);
if (url.searchParams.get("index") != index.toString())
return;
async function close() {
var _a, _b;
if (!stream.readableEnded && !stream.writableEnded)
stream.end();
if (!extension.isClosed() && extension.browser().isConnected()) {
// @ts-ignore
extension.evaluate((index) => STOP_RECORDING(index), index);
}
if (ws.readyState != ws_1.default.CLOSED) {
setTimeout(() => {
// await pending messages to be sent and then close the socket
if (ws.readyState != ws_1.default.CLOSED)
ws.close();
}, (_b = (_a = opts.streamConfig) === null || _a === void 0 ? void 0 : _a.closeTimeout) !== null && _b !== void 0 ? _b : 5000);
}
(await exports.wss).off("connection", onConnection);
}
ws.on("message", (data) => {
stream.write(data);
});
ws.on("close", close);
page.on("close", close);
stream.on("close", close);
}
(await exports.wss).on("connection", onConnection);
await lock();
await page.bringToFront();
await assertExtensionLoaded(extension, retryPolicy);
await extension.evaluate(
// @ts-ignore
(settings) => START_RECORDING(settings), Object.assign(Object.assign({}, opts), { index, tabId: tab.id }));
unlock();
return stream;
}
exports.getStream = getStream;
async function assertExtensionLoaded(ext, opt) {
const wait = (ms) => new Promise((res) => setTimeout(res, ms));
for (let currentTick = 0; currentTick < opt.times; currentTick++) {
// @ts-ignore
if (await ext.evaluate(() => typeof START_RECORDING === "function"))
return;
await wait(Math.pow(opt.each, currentTick));
}
throw new Error("Could not find START_RECORDING function in the browser context");
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiUHVwcGV0ZWVyU3RyZWFtLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc3JjL1B1cHBldGVlclN0cmVhbS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQUFBLG1EQU93QjtBQUN4QiwyQ0FBNkI7QUFDN0IsbUNBQW1DO0FBQ25DLHlDQUFnRDtBQUdoRCxNQUFNLFdBQVcsR0FBRyxrQ0FBa0MsQ0FBQztBQUN2RCxJQUFJLFlBQVksR0FBRyxDQUFDLENBQUM7QUFVckIsSUFBSSxJQUFZLENBQUM7QUFFSixRQUFBLEdBQUcsR0FBRyxDQUFDLEtBQUssSUFBSSxFQUFFO0lBQzlCLEtBQUssSUFBSSxDQUFDLEdBQUcsS0FBSyxFQUFFLENBQUMsSUFBSSxLQUFLLEVBQUUsQ0FBQyxFQUFFLEVBQUU7UUFDcEMsTUFBTSxFQUFFLEdBQUcsSUFBSSxvQkFBZSxDQUFDLEVBQUUsSUFBSSxFQUFFLENBQUMsRUFBRSxDQUFDLENBQUM7UUFDNUMsTUFBTSxPQUFPLEdBQUcsTUFBTSxPQUFPLENBQUMsSUFBSSxDQUFDO1lBQ2xDLElBQUksT0FBTyxDQUFDLENBQUMsT0FBTyxFQUFFLEVBQUU7Z0JBQ3ZCLEVBQUUsQ0FBQyxFQUFFLENBQUMsT0FBTyxFQUFFLENBQUMsQ0FBTSxFQUFFLEVBQUU7b0JBQ3pCLE9BQU8sQ0FBQyxDQUFDLENBQUMsQ0FBQyxPQUFPLENBQUMsUUFBUSxDQUFDLFlBQVksQ0FBQyxDQUFDLENBQUM7Z0JBQzVDLENBQUMsQ0FBQyxDQUFDO1lBQ0osQ0FBQyxDQUFDO1lBQ0YsSUFBSSxPQUFPLENBQUMsQ0FBQyxPQUFPLEVBQUUsRUFBRTtnQkFDdkIsRUFBRSxDQUFDLEVBQUUsQ0FBQyxXQUFXLEVBQUUsR0FBRyxFQUFFO29CQUN2QixPQUFPLENBQUMsSUFBSSxDQUFDLENBQUM7Z0JBQ2YsQ0FBQyxDQUFDLENBQUM7WUFDSixDQUFDLENBQUM7U0FDRixDQUFDLENBQUM7UUFDSCxJQUFJLE9B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
;