Spaces:
Running
Running
File size: 3,836 Bytes
8a8fe1d f6647c3 8a8fe1d f6647c3 8a8fe1d f6647c3 8a8fe1d f6647c3 8a8fe1d f6647c3 8a8fe1d f6647c3 8a8fe1d f6647c3 8a8fe1d f6647c3 8a8fe1d f6647c3 8a8fe1d f6647c3 8a8fe1d f6647c3 8a8fe1d f6647c3 8a8fe1d f6647c3 8a8fe1d f6647c3 8a8fe1d |
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 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 |
import {Browser, Page, PuppeteerLaunchOptions} from "puppeteer";
import path from "path";
import run from "node:test";
import * as fs from "fs";
import {shuffleArray, sleep} from "../utils";
const puppeteer = require('puppeteer-extra');
const StealthPlugin = require('puppeteer-extra-plugin-stealth');
puppeteer.use(StealthPlugin());
const runPath = path.join(__dirname, 'run');
export interface PageInfo<T> {
id: string;
ready: boolean;
page?: Page;
data?: T;
}
type PrepareFunc<T> = (id: string, browser: Browser) => Promise<[Page | undefined, T]>
export interface BrowserUser<T> {
init: PrepareFunc<T>;
newID: () => string
deleteID: (id: string) => void
}
export class BrowserPool<T> {
private readonly pool: PageInfo<T>[] = [];
private readonly size: number;
private readonly user: BrowserUser<T>
constructor(size: number, user: BrowserUser<T>) {
this.size = size
this.user = user;
this.init();
}
init() {
for (let i = 0; i < this.size; i++) {
const id = this.user.newID();
const info: PageInfo<T> = {
id,
ready: false,
}
this.pool.push(info)
this.initOne(id).then()
}
}
find(id: string): PageInfo<T> | undefined {
for (const info of this.pool) {
if (info.id === id) {
return info;
}
}
}
async initOne(id: string): Promise<void> {
const info = this.find(id);
if (!info) {
console.error('init one failed, not found info');
return;
}
const options: PuppeteerLaunchOptions = {
headless: process.env.DEBUG === "1" ? false : 'new',
args: ['--no-sandbox', '--disable-setuid-sandbox'],
userDataDir: `run/${info.id}`,
};
try {
const browser = await puppeteer.launch(options);
const [page, data] = await this.user.init(info.id, browser);
if (!page) {
const newID = this.user.newID();
console.warn(`init ${info.id} failed, delete! init new ${newID}`);
await browser.close();
if (options.userDataDir) {
fs.rmdirSync(options.userDataDir, {recursive: true});
}
await sleep(5000);
info.id = newID;
return await this.initOne(info.id);
}
info.page = page;
info.data = data
info.ready = true;
} catch (e) {
console.error('init one failed, err:', e);
const newID = this.user.newID();
console.warn(`init ${info.id} failed, delete! init new ${newID}`);
if (options.userDataDir) {
fs.rmdirSync(options.userDataDir, {recursive: true});
}
await sleep(5000);
info.id = newID;
return await this.initOne(info.id);
}
}
//@ts-ignore
get(): [page: Page | undefined, data: T | undefined, done: (data: T) => void, destroy: () => void] {
for (const item of shuffleArray(this.pool)) {
if (item.ready) {
item.ready = false;
return [
item.page,
item.data,
(data: T) => {
item.ready = true
item.data = data;
},
() => {
item.page?.close();
this.user.deleteID(item.id);
item.id = this.user.newID();
this.initOne(item.id).then();
}
]
}
}
return [] as any;
}
}
|