diff --git a/.gitignore b/.gitignore index f223f6b..7e222cb 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,4 @@ package-lock.json test.js test.ts +image.png diff --git a/package.json b/package.json index 7351377..5fef1f0 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,16 @@ "type": "git", "url": "git+https://github.com/KarinJS/puppeteer-core.git" }, + "exports": { + ".": { + "import": "./lib/index.js", + "types": "./lib/index.d.ts" + }, + "./puppeteer-core": { + "import": "./lib/puppeteer.js", + "types": "./lib/puppeteer.d.ts" + } + }, "license": "MIT", "author": "shijin", "type": "module", @@ -33,11 +43,13 @@ }, "dependencies": { "decompress": "4.2.1", - "puppeteer-core": "23.6.1" + "progress": "^2.0.3", + "puppeteer-core": "23.10.1" }, "devDependencies": { "@types/decompress": "^4.2.7", "@types/node": "20.14.2", + "@types/progress": "^2.0.7", "eslint": "latest", "neostandard": "latest", "sort-package-json": "2.10.0", diff --git a/src/common/common.ts b/src/common/common.ts index b08a422..a7a7629 100644 --- a/src/common/common.ts +++ b/src/common/common.ts @@ -4,10 +4,11 @@ import path from 'path' import https from 'https' import { promisify } from 'util' import { pipeline } from 'stream' +import ProgressBar from 'progress' import decompress from 'decompress' import { fileURLToPath } from 'url' -import { exec as execCmd, ExecOptions } from 'child_process' import { EventEmitter } from 'events' +import { exec as execCmd, ExecOptions } from 'child_process' const streamPipeline = promisify(pipeline) @@ -113,6 +114,46 @@ export class Common extends EventEmitter { return } + /** 计算下载进度 */ + const calculateProgress = (downloadedSize: number, total: number, startTime: number) => { + /** 耗时 */ + const elapsedTime = (Date.now() - startTime) / 1000 + /** 当前已下载 */ + const data = downloadedSize / (1024 * 1024) + /** 下载速度 */ + const speed = (data / elapsedTime).toFixed(2) + /** 总大小 */ + const size = (total / (1024 * 1024)).toFixed(2) + /** 已过去时间 */ + const time = Math.floor(elapsedTime) + + return { speed, size, time, data: data.toFixed(2) } + } + + /** 文件总大小 */ + const total = Number(res.headers['content-length'] || '0') + let downloadedSize = 0 + const startTime = Date.now() + + /** 进度条 */ + const progressBar = new ProgressBar('🚀 下载进度 [:bar] :percent :data/:size MB | :speed MB/s :times', { + total, + width: 30, + complete: '=', + incomplete: ' ', + }) + + /** 更新下载进度条 */ + res.on('data', (chunk) => { + downloadedSize += chunk.length + const options = calculateProgress(downloadedSize, total, startTime) + progressBar.tick(chunk.length, options) + }) + + res.on('end', () => { + console.log('\n') + }) + const fileStream = fs.createWriteStream(file) streamPipeline(res, fileStream) .then(() => resolve(true)) diff --git a/src/puppeteer.ts b/src/puppeteer.ts new file mode 100644 index 0000000..5ab9b35 --- /dev/null +++ b/src/puppeteer.ts @@ -0,0 +1,2 @@ +export * from 'puppeteer-core' +export { default } from 'puppeteer-core' diff --git a/src/puppeteer/core.ts b/src/puppeteer/core.ts index fd69abe..481a32b 100644 --- a/src/puppeteer/core.ts +++ b/src/puppeteer/core.ts @@ -1,6 +1,6 @@ import { common } from '@Common' import { ChildProcess } from 'child_process' -import puppeteer, { Browser, GoToOptions, HTTPRequest, Page, PuppeteerLaunchOptions, ScreenshotOptions } from 'puppeteer-core' +import puppeteer, { Browser, GoToOptions, HTTPRequest, Page, LaunchOptions, ScreenshotOptions } from 'puppeteer-core' export interface screenshot extends ScreenshotOptions { /** http地址或本地文件路径 */ @@ -91,14 +91,14 @@ export class Render { /** 浏览器id */ id: number /** 浏览器启动配置 */ - config: PuppeteerLaunchOptions + config: LaunchOptions /** 浏览器实例 */ browser!: Browser /** 截图队列 存放每个任务的唯一标识 */ list: Map /** 浏览器进程 */ process!: ChildProcess | null - constructor (id: number, config: PuppeteerLaunchOptions) { + constructor (id: number, config: LaunchOptions) { this.id = id this.config = config this.list = new Map() diff --git a/src/puppeteer/index.ts b/src/puppeteer/index.ts index 1ed579b..846f913 100644 --- a/src/puppeteer/index.ts +++ b/src/puppeteer/index.ts @@ -2,10 +2,10 @@ import crypto from 'crypto' import InitChrome from './init' import { common } from '@Common' import { Render, RenderResult, screenshot } from './core' -import { PuppeteerLaunchOptions } from 'puppeteer-core' -import { PUPPETEER_REVISIONS } from 'puppeteer-core/lib/cjs/puppeteer/revisions.js' +import { LaunchOptions } from 'puppeteer-core' +import { PUPPETEER_REVISIONS } from 'puppeteer-core/internal/revisions.js' -export interface RunConfig extends PuppeteerLaunchOptions { +export interface RunConfig extends LaunchOptions { /** * 启动浏览器数量 * @default 1 @@ -31,6 +31,11 @@ export interface RunConfig extends PuppeteerLaunchOptions { * 通过管道而不是 WebSocket 连接到浏览器。在大多数情况下,这将导致更快的页面加载。 */ pipe?: boolean + /** + * 需要使用的浏览器标识 + * @default 'chrome-headless-shell' + */ + chrome?: 'chrome-headless-shell' | 'chrome' } /** @@ -44,7 +49,7 @@ export class Puppeteer { /** 实例管理器配置 初始化的时候传递 */ config: RunConfig /** 启动浏览器的参数 初始化后才产生 */ - browserOptions: PuppeteerLaunchOptions + browserOptions: LaunchOptions constructor (config?: RunConfig) { this.index = 0 this.list = [] @@ -52,6 +57,7 @@ export class Puppeteer { pipe: true, headless: true, devtools: false, + chrome: 'chrome', args: [ '--enable-gpu', '--no-sandbox', @@ -62,6 +68,7 @@ export class Puppeteer { ], } this.browserOptions = this.config + if (!this.config.chrome) this.config.chrome = 'chrome' } async init () { @@ -70,8 +77,8 @@ export class Puppeteer { /** 浏览器执行路径 */ if (!this.config?.executablePath) { - const version = PUPPETEER_REVISIONS['chrome-headless-shell'] - const init = new InitChrome(version) + const version = PUPPETEER_REVISIONS[this.config.chrome!] + const init = new InitChrome(version, this.config.chrome!) const executablePath = await init.init() this.browserOptions.executablePath = executablePath } diff --git a/src/puppeteer/init.ts b/src/puppeteer/init.ts index 36b0b5a..87edaf3 100644 --- a/src/puppeteer/init.ts +++ b/src/puppeteer/init.ts @@ -31,15 +31,17 @@ export const enum ChromeUrl { } export default class InitChrome { - /** - * @param version - 传入下载的chrome版本 - */ + /** 版本 */ version: string + /** 操作系统标识符 */ platform: Platform + /** chrome信息 */ info: Info - constructor (version: string) { + /** browser标识 暂不支持firefox */ + browser: string + constructor (version: string, headless: 'chrome-headless-shell' | 'chrome') { + this.browser = headless this.version = version - /** 获取系统版本 */ this.platform = common.Platform() this.info = this.GetInfo() } @@ -53,6 +55,8 @@ export default class InitChrome { if (common.exists(this.info.chrome)) { console.info(`[chrome] ${this.info.chrome}`) return this.info.chrome + } else { + console.info('[chrome][init] 未找到chrome,开始下载') } /** 下载chrome */ @@ -193,7 +197,7 @@ export default class InitChrome { async GetDownloadUrl (host: ChromeUrl = ChromeUrl.Cnpm): Promise { try { /** 组合url */ - const url = `${host}/${this.version}/${this.platform}/chrome-headless-shell-${this.platform}.zip` + const url = `${host}/${this.version}/${this.platform}/${this.browser}-${this.platform}.zip` console.info(`[chrome][init] 获取下载地址完成:${url}`) return url } catch (e) { @@ -219,19 +223,19 @@ export default class InitChrome { /** 是否为windows */ const isWin = os.platform() === 'win32' /** 缓存目录 */ - const cache = path.join(os.homedir(), '.cache', 'puppeteer', 'chrome-headless-shell') + const cache = path.join(os.homedir(), '.cache', 'puppeteer', this.browser) /** 版本 */ const version = `${platform}-${this.version}` /** 存放实际 Chrome 可执行文件的目录 */ const dir = path.join(cache, version) /** 下载后压缩包的存放路径 */ - const zip = path.join(dir, `chrome-headless-shell-${platform}.zip`) + const zip = path.join(dir, `${this.browser}-${platform}.zip`) /** 解压路径 */ const chromeDir = dir /** chrome二进制路径 */ - const chrome = path.join(chromeDir, `chrome-headless-shell-${platform}`, `chrome-headless-shell${isWin ? '.exe' : ''}`) + const chrome = path.join(chromeDir, `${this.browser}-${platform}`, `${this.browser}${isWin ? '.exe' : ''}`) - // tips: 压缩包解压后会带一个文件夹: chrome-headless-shell-${platform} + // tips: 压缩包解压后会带一个文件夹: ${this.browser}-${platform} return { isWin, platform, diff --git a/tsconfig.json b/tsconfig.json index 2661b10..2e1f1f1 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -3,7 +3,7 @@ "target": "ES2022", "baseUrl": "./", "outDir": "./lib", - "moduleResolution": "node", + "moduleResolution": "bundler", "module": "ES2022", "paths": { "@Common": [