pup-recorder 0.2.7 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,78 +1,45 @@
1
- # pup-recorder(1)
1
+ # PUP-RECORDER(1)
2
2
 
3
3
  ## NAME
4
4
 
5
- pup-recorder - record web pages as video
5
+ pup-recorder record web pages as video
6
6
 
7
7
  ## SYNOPSIS
8
8
 
9
- ```
10
- pup source [-W width] [-H height] [-f fps] [-t duration] [-o file] [-a] [-d] [--use-inner-proxy]
11
- ```
9
+ pup source [-W width] [-H height] [-f fps] [-t duration] [-o file] [-a] [-d]
12
10
 
13
11
  ## OPTIONS
14
12
 
15
- ```
16
- source file://, http(s)://, or data: URI
17
-
18
- -W, --width <number> default: 1920
19
- -H, --height <number> default: 1080
20
- -f, --fps <number> default: 30
21
- -t, --duration <number> default: 5
22
- -o, --out-file <path> default: output.mp4
23
- -a, --with-audio
24
- -d, --deterministic frame-by-frame rendering mode
25
- --use-inner-proxy use Bilibili inner proxy
26
- ```
13
+ source file://, http(s)://, or data: URI
14
+ -W, --width <number> default: 1920
15
+ -H, --height <number> default: 1080
16
+ -f, --fps <number> default: 30
17
+ -t, --duration <number> default: 5
18
+ -o, --out-file <path> default: output.mp4
19
+ -a, --with-audio
20
+ -d, --deterministic frame-by-frame rendering mode
21
+ --use-inner-proxy use Bilibili inner proxy
27
22
 
28
23
  ## ENVIRONMENT
29
24
 
30
- ```
31
- PUP_LOG_LEVEL 0=error 1=warn 2=info 3=debug. default: 2
32
- PUP_USE_INNER_PROXY 1=on
33
- PUP_DISABLE_GPU 1=on
34
- PUP_DETERMINISTIC 1=on
35
- ```
25
+ PUP_LOG_LEVEL 0=error 1=warn 2=info 3=debug (default: 2)
26
+ PUP_USE_INNER_PROXY 1=on
27
+ PUP_DISABLE_GPU 1=on
28
+ PUP_DETERMINISTIC 1=on
29
+ PUP_EXPERIMENTAL_PUPPETEER 1=on (Linux puppeteer mode)
36
30
 
37
31
  ## API
38
32
 
39
- ```typescript
33
+ ```ts
40
34
  import { pup } from "pup-recorder";
41
35
 
42
36
  const result = await pup(source, {
43
- width?: number,
44
- height?: number,
45
- fps?: number,
46
- duration?: number,
47
- outFile?: string,
48
- withAudio?: boolean,
49
- deterministic?: boolean,
50
- useInnerProxy?: boolean,
51
- signal?: AbortSignal,
52
- onProgress?: (progress: number) => void,
37
+ width, height, fps, duration, outFile,
38
+ withAudio, deterministic, useInnerProxy,
39
+ signal, onProgress,
53
40
  });
54
- // result: { options, written, jank, outFile, audio? }
55
- ```
56
-
57
- ## FILES
58
-
59
- ```
60
- dist/cli.cjs
61
- dist/index.js
62
- ```
63
-
64
- ## EXAMPLES
65
-
66
- ```sh
67
- pup https://example.com -t 5
68
- pup file:///path/to/page.html -d -t 10
69
- pup https://example.com -a -W 1280 -H 720 -f 60 -t 10
70
41
  ```
71
42
 
72
43
  ## SEE ALSO
73
44
 
74
- pup-recorder-mcp(1), pup-server(1), pup(7)
75
-
76
- ## AUTHOR
77
-
78
- qq1909698494@gmail.com
45
+ pup-server(1), pup-recorder-mcp(1), pup(7)
package/build.ts CHANGED
@@ -3,15 +3,12 @@ import { rm } from "fs/promises";
3
3
  import { createRequire } from "module";
4
4
  import { join } from "path";
5
5
  import { build, type Options } from "tsup";
6
- import { buildRust } from "./build_rust";
7
6
  import { dependencies } from "./package.json";
8
7
 
9
8
  const require = createRequire(import.meta.url);
10
9
  const tsPath = require.resolve("@typescript/native-preview/package.json");
11
10
  const tsgo = join(tsPath, "..", "bin", "tsgo.js");
12
11
 
13
- await buildRust();
14
-
15
12
  await $`${tsgo}`;
16
13
  await rm("dist", { recursive: true, force: true });
17
14
 
@@ -10,15 +10,20 @@ import { FFAudioEncoder } from 'node-av/constants';
10
10
  import { FFVideoEncoder } from 'node-av/constants';
11
11
  import { FormatContext } from 'node-av';
12
12
  import { Frame } from 'node-av';
13
+ import { HardwareContext } from 'node-av/api';
13
14
  import type { NativeImage } from 'electron';
14
15
  import { Packet } from 'node-av';
15
16
  import { Size } from 'electron';
16
17
  import { Socket } from 'net';
18
+ import { SoftwareScaleContext } from 'node-av';
17
19
  import { SpawnOptions } from 'child_process';
20
+ import { Stream } from 'node-av';
18
21
  import z from 'zod';
19
22
 
20
23
  export declare function advanceVirtualTime(cdp: Debugger, budget: number): Promise<void>;
21
24
 
25
+ export declare const ANNEX_B_START_CODE: Buffer<ArrayBuffer>;
26
+
22
27
  export declare interface AudioCapture {
23
28
  teardown(): Promise<void>;
24
29
  }
@@ -62,39 +67,77 @@ export declare interface AudioEncoderOptions {
62
67
  muxer: FormatMuxer;
63
68
  }
64
69
 
70
+ /**
71
+ * Build alpha_channel_info SEI message (payloadType=165).
72
+ * Layout from x265 SEIAlphaChannelInfo::writeSEI.
73
+ */
74
+ export declare function buildAlphaChannelInfoSEI(): Buffer;
75
+
65
76
  export declare function buildRust(): Promise<void>;
66
77
 
67
78
  export declare function buildStegoHTML(targetURL: string, size: Size): string;
68
79
 
69
80
  /**
70
- * Builds the JS injector that hooks all time-related globals in the target iframe.
71
- * Guards against running in the wrapper (top-level) frame so the stego canvas is unaffected.
81
+ * Builds the JS injector that hooks all time-related globals in the target frame.
82
+ * In Electron/stego mode (default), guards against running in the top-level frame.
83
+ * In Puppeteer mode (skipFrameGuard: true), injects directly into the main document.
72
84
  * Must be injected via Page.addScriptToEvaluateOnNewDocument AND directly into
73
- * already-loaded sub-frames.
85
+ * already-loaded frames.
74
86
  */
75
- export declare function buildTickInjector(): string;
87
+ export declare function buildTickInjector(opts?: TickInjectorOptions): string;
88
+
89
+ /**
90
+ * Build unified extradata: base VPS (patched for alpha) + interleaved SPS/PPS.
91
+ * Alpha SPS/PPS follow their base counterparts so the HVCC serializer
92
+ * groups them into the same NAL array (required for multi-layer).
93
+ */
94
+ export declare function buildUnifiedExtradata(baseExtradata: Buffer, alphaExtradata: Buffer): Buffer;
76
95
 
77
96
  export declare const canIUseGPU: Promise<boolean>;
78
97
 
79
98
  export declare function checkHTML(source: string): void;
80
99
 
100
+ export declare function chromiumOptions(disableGpu: boolean): Promise<string[]>;
101
+
81
102
  export declare interface CLIOptions {
82
103
  name: string;
83
104
  defaults: RenderOptions;
84
105
  run: (source: string, options: RenderOptions) => Promise<unknown>;
85
106
  }
86
107
 
108
+ declare class CodecState_2 implements Disposable {
109
+ readonly src: Frame;
110
+ readonly dst: Frame;
111
+ readonly pkt: Packet;
112
+ private _sws?;
113
+ private _png?;
114
+ static create(width: number, height: number): Promise<CodecState_2>;
115
+ private constructor();
116
+ /**
117
+ * Create a fresh PNG decoder context.
118
+ * The FFmpeg PNG decoder accumulates APNG blending state
119
+ * across frames, so a shared instance corrupts output when decoding standalone PNGs.
120
+ */
121
+ png(): Promise<CodecContext>;
122
+ decodePNG(pngData: Buffer): Promise<Frame>;
123
+ get sws(): SoftwareScaleContext;
124
+ [Symbol.dispose](): void;
125
+ }
126
+ export { CodecState_2 as CodecState }
127
+
87
128
  declare class ConcurrencyLimiter {
88
129
  readonly maxConcurrency: number;
89
130
  private _active;
90
131
  private _queue;
91
- private _ended;
132
+ private _signals;
133
+ private _resolve?;
92
134
  constructor(maxConcurrency: number);
93
135
  get active(): number;
94
136
  get pending(): number;
95
137
  get stats(): string;
96
- schedule<T>(fn: () => Promise<T>): Promise<T>;
97
- end(): Promise<void>;
138
+ schedule<T>(fn: () => Promise<T>, signal?: AbortSignal): Promise<T>;
139
+ drain(): Promise<void>;
140
+ private flush;
98
141
  private next;
99
142
  }
100
143
  export { ConcurrencyLimiter }
@@ -106,6 +149,10 @@ export declare function createIpcServer(socketPath: string): Promise<IpcServer>;
106
149
 
107
150
  export declare function createStegoURL(src: string, size: Size): string;
108
151
 
152
+ export declare function createVideoEncoder(opts: VideoFactoryOptions, muxer: FormatMuxer): Promise<VideoSetup>;
153
+
154
+ export declare function debounce<T extends (...args: unknown[]) => void>(fn: T, delay?: number): T;
155
+
109
156
  export declare function decodeStego(bitmap: Buffer, size: Size): number | undefined;
110
157
 
111
158
  declare const DEFAULT_DURATION = 5;
@@ -120,7 +167,7 @@ declare const DEFAULT_HEIGHT = 1080;
120
167
  export { DEFAULT_HEIGHT }
121
168
  export { DEFAULT_HEIGHT as DEFAULT_HEIGHT_alias_1 }
122
169
 
123
- declare const DEFAULT_OUT_FILE = "output.mov";
170
+ declare const DEFAULT_OUT_FILE = "output.mp4";
124
171
  export { DEFAULT_OUT_FILE }
125
172
  export { DEFAULT_OUT_FILE as DEFAULT_OUT_FILE_alias_1 }
126
173
 
@@ -136,26 +183,27 @@ export declare function doEject(): string;
136
183
 
137
184
  export declare function doProcess(timestampMs: number): string;
138
185
 
139
- export declare function electronOpts(): Promise<string[]>;
186
+ export declare function doPuppeteer(source: string, options: RenderOptions, onProgress?: (p: number) => void): Promise<IpcDonePayload>;
187
+
188
+ export declare function drainPackets(ctx: CodecContext, pkt: Packet, stream: Stream, muxer: FormatMuxer): Promise<void>;
189
+
190
+ export declare function electronOpts(disableGpu: boolean): Promise<string[]>;
191
+
192
+ export declare function encodeNalHeader(type: number, layerId: number, temporalId: number): [number, number];
140
193
 
141
194
  declare class EncoderPipeline {
142
- private _video;
143
- private _audio;
144
- private _muxer;
145
- private _limiter;
146
- private _outFile;
147
- private _sws;
148
- private _srcFrame;
149
- private _dstFrame;
195
+ private _s;
150
196
  private _disposed;
151
197
  private constructor();
152
198
  static create(opts: EncoderPipelineOptions): Promise<EncoderPipeline>;
153
199
  setupAudio(sampleRate: number): void;
154
- encodeFrame(input: Buffer): Promise<void>;
200
+ encodeBGRA(input: Buffer): Promise<void>;
201
+ encodePNG(pngData: Buffer): Promise<void>;
155
202
  encodeAudio(pcm: Buffer): Promise<void>;
156
203
  finish(): Promise<string>;
157
204
  [Symbol.asyncDispose](): Promise<void>;
158
205
  private free;
206
+ private bgraFrame;
159
207
  }
160
208
  export { EncoderPipeline }
161
209
  export { EncoderPipeline as EncoderPipeline_alias_1 }
@@ -166,6 +214,7 @@ declare interface EncoderPipelineOptions {
166
214
  fps: number;
167
215
  outFile: string;
168
216
  withAudio?: boolean;
217
+ disableGpu?: boolean;
169
218
  }
170
219
  export { EncoderPipelineOptions }
171
220
  export { EncoderPipelineOptions as EncoderPipelineOptions_alias_1 }
@@ -234,6 +283,20 @@ export declare class FrameDropStats {
234
283
  finalize(): FrameDropScore;
235
284
  }
236
285
 
286
+ export declare type HwEncoder = VideoToolboxEncoder | NvencDualLayerEncoder;
287
+
288
+ export declare interface HwVideoEncoderOptions {
289
+ width: number;
290
+ height: number;
291
+ fps: number;
292
+ hw: HardwareContext;
293
+ bitrate: number;
294
+ muxer: FormatMuxer;
295
+ }
296
+
297
+ /** Interleave base and alpha NALs for a single frame. */
298
+ export declare function interleaveAccessUnits(baseNals: NalUnit[], alphaNals: NalUnit[]): Buffer;
299
+
237
300
  export declare interface IpcDonePayload {
238
301
  written: number;
239
302
  jank: number;
@@ -326,6 +389,18 @@ export { LoggerLike as LoggerLike_alias_1 }
326
389
 
327
390
  export declare function makeCLI(options: CLIOptions): Promise<void>;
328
391
 
392
+ export declare function makeFrame(width: number, height: number, pixFmt: AVPixelFormat): Frame;
393
+
394
+ export declare function makePacket(): Packet;
395
+
396
+ export declare const NAL_HEADER_SIZE = 2;
397
+
398
+ export declare interface NalUnit {
399
+ type: number;
400
+ layerId: number;
401
+ data: Buffer;
402
+ }
403
+
329
404
  export declare interface NetworkOptions {
330
405
  source: string;
331
406
  window: BrowserWindow;
@@ -336,6 +411,23 @@ declare function noerr<Fn extends (...args: any[]) => any, D>(fn: Fn, defaultVal
336
411
  export { noerr }
337
412
  export { noerr as noerr_alias_1 }
338
413
 
414
+ export declare class NvencDualLayerEncoder implements Disposable {
415
+ private _s;
416
+ private _seiBuffer;
417
+ private _pts;
418
+ private _seiInjected;
419
+ private _alphaBuf?;
420
+ private _alphaFrame?;
421
+ private constructor();
422
+ static create(opts: HwVideoEncoderOptions): Promise<NvencDualLayerEncoder>;
423
+ encode(bgraFrame: Frame, muxer: FormatMuxer): Promise<void>;
424
+ flush(muxer: FormatMuxer): Promise<void>;
425
+ [Symbol.dispose](): void;
426
+ private drainInterleaved;
427
+ }
428
+
429
+ export declare function packBits(bits: number[]): Buffer;
430
+
339
431
  declare function pargs(): string[];
340
432
  export { pargs }
341
433
  export { pargs as pargs_alias_1 }
@@ -389,6 +481,10 @@ declare const pupDisableGPU: boolean;
389
481
  export { pupDisableGPU }
390
482
  export { pupDisableGPU as pupDisableGPU_alias_1 }
391
483
 
484
+ declare const pupExperimentalPuppeteer: boolean;
485
+ export { pupExperimentalPuppeteer }
486
+ export { pupExperimentalPuppeteer as pupExperimentalPuppeteer_alias_1 }
487
+
392
488
  declare const pupIpcSocket: string | undefined;
393
489
  export { pupIpcSocket }
394
490
  export { pupIpcSocket as pupIpcSocket_alias_1 }
@@ -445,6 +541,7 @@ declare const RenderSchema: z.ZodObject<{
445
541
  outFile: z.ZodString;
446
542
  useInnerProxy: z.ZodBoolean;
447
543
  deterministic: z.ZodBoolean;
544
+ disableGpu: z.ZodBoolean;
448
545
  }, z.core.$strip>;
449
546
  export { RenderSchema }
450
547
  export { RenderSchema as RenderSchema_alias_1 }
@@ -457,6 +554,9 @@ declare interface RetryOptions<Args extends any[], Ret> {
457
554
  export { RetryOptions }
458
555
  export { RetryOptions as RetryOptions_alias_1 }
459
556
 
557
+ /** Rewrite nuh_layer_id in a NAL unit (returns copy). */
558
+ export declare function rewriteNalLayerId(nal: Buffer, layerId: number): Buffer;
559
+
460
560
  export declare function runElectronApp(size: Size, args: unknown[], ipcSocketPath: string): Promise<ProcessHandle>;
461
561
 
462
562
  export declare function setInterceptor({ source, window, useInnerProxy }: NetworkOptions): void;
@@ -471,12 +571,24 @@ declare function sleep(ms: number): Promise<void>;
471
571
  export { sleep }
472
572
  export { sleep as sleep_alias_1 }
473
573
 
574
+ /** Split Annex B bitstream into NAL units. */
575
+ export declare function splitNalUnits(bitstream: Buffer): NalUnit[];
576
+
474
577
  export declare function startStego(cdp: Debugger): Promise<any>;
475
578
 
476
579
  export declare function stopStego(cdp: Debugger): Promise<any>;
477
580
 
478
581
  export declare const TICK_SYMBOL = "__pup_tick__";
479
582
 
583
+ export declare interface TickInjectorOptions {
584
+ /**
585
+ * When true, skips the top-frame guard so the injector runs in the main document.
586
+ * Required for Puppeteer mode where the page is loaded directly (no stego iframe wrapper).
587
+ * Default: false (Electron/stego mode — only inject in iframes).
588
+ */
589
+ skipFrameGuard?: boolean;
590
+ }
591
+
480
592
  export declare function unsetInterceptor(window: BrowserWindow): void;
481
593
 
482
594
  declare function useRetry<Args extends any[], Ret>({ fn, maxAttempts, timeout }: RetryOptions<Args, Ret>): (...args: Args) => Promise<Ret>;
@@ -507,10 +619,37 @@ export declare interface VideoEncoderOptions {
507
619
  codecOpts: Record<string, string>;
508
620
  bitrate: number;
509
621
  pixelFormat: AVPixelFormat;
510
- threadCount?: number;
511
622
  muxer: FormatMuxer;
512
623
  }
513
624
 
625
+ export declare interface VideoFactoryOptions {
626
+ width: number;
627
+ height: number;
628
+ fps: number;
629
+ bitrate?: number;
630
+ disableGpu?: boolean;
631
+ }
632
+
633
+ export declare interface VideoSetup {
634
+ video?: VideoEncoder_2;
635
+ hwVideo?: HwEncoder;
636
+ codec?: CodecState_2;
637
+ hw?: HardwareContext;
638
+ }
639
+
640
+ export declare class VideoToolboxEncoder implements Disposable {
641
+ private _ctx;
642
+ private _pkt;
643
+ private _stream;
644
+ private _pts;
645
+ private constructor();
646
+ static create(opts: HwVideoEncoderOptions): Promise<VideoToolboxEncoder>;
647
+ encode(bgraFrame: Frame, muxer: FormatMuxer): Promise<void>;
648
+ flush(muxer: FormatMuxer): Promise<void>;
649
+ [Symbol.dispose](): void;
650
+ private drain;
651
+ }
652
+
514
653
  export declare class WaitableEvent {
515
654
  private _promise?;
516
655
  private _resolve?;