pup-recorder 0.0.8
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/Cargo.lock +230 -0
- package/Cargo.toml +23 -0
- package/LICENSE +26 -0
- package/README.md +96 -0
- package/build.rs +5 -0
- package/build.ts +55 -0
- package/build_rust.ts +51 -0
- package/dist/cjs/app.cjs +633 -0
- package/dist/cjs/app.cjs.map +7 -0
- package/dist/cjs/cli.cjs +691 -0
- package/dist/cjs/cli.cjs.map +7 -0
- package/dist/cjs/index.cjs +781 -0
- package/dist/cjs/index.cjs.map +7 -0
- package/dist/cli.js +667 -0
- package/dist/cli.js.map +7 -0
- package/dist/index.d.ts +93 -0
- package/dist/index.js +728 -0
- package/dist/index.js.map +7 -0
- package/package.json +37 -0
- package/rust/darwin-arm64.node +0 -0
- package/rust/darwin-x64.node +0 -0
- package/rust/linux-arm64.node +0 -0
- package/rust/linux-x64.node +0 -0
- package/src/app.ts +19 -0
- package/src/base/abort.ts +75 -0
- package/src/base/constants.ts +18 -0
- package/src/base/electron.ts +51 -0
- package/src/base/encoder.ts +35 -0
- package/src/base/env.ts +21 -0
- package/src/base/ffmpeg.ts +188 -0
- package/src/base/frame_sync.ts +139 -0
- package/src/base/image.ts +9 -0
- package/src/base/lazy.ts +20 -0
- package/src/base/limiter.ts +58 -0
- package/src/base/logging.ts +123 -0
- package/src/base/noerr.ts +18 -0
- package/src/base/parser.ts +12 -0
- package/src/base/process.ts +35 -0
- package/src/base/proxy.ts +33 -0
- package/src/base/record.ts +228 -0
- package/src/base/retry.ts +40 -0
- package/src/base/stream.ts +74 -0
- package/src/base/timing.ts +23 -0
- package/src/base/types.ts +19 -0
- package/src/cli.ts +6 -0
- package/src/common.ts +53 -0
- package/src/index.ts +14 -0
- package/src/pup.ts +142 -0
- package/src/rust/lib.rs +105 -0
- package/src/rust/lib.ts +28 -0
- package/tsconfig.json +25 -0
- package/x265/darwin-arm64 +0 -0
- package/x265/darwin-x64 +0 -0
- package/x265/linux-arm64 +0 -0
- package/x265/linux-x64 +0 -0
|
@@ -0,0 +1,781 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/index.ts
|
|
31
|
+
var index_exports = {};
|
|
32
|
+
__export(index_exports, {
|
|
33
|
+
ConcurrencyLimiter: () => ConcurrencyLimiter,
|
|
34
|
+
Lazy: () => Lazy,
|
|
35
|
+
PUP_ARGS_ENV_KEY: () => PUP_ARGS_ENV_KEY,
|
|
36
|
+
exec: () => exec,
|
|
37
|
+
logger: () => logger,
|
|
38
|
+
noerr: () => noerr,
|
|
39
|
+
pargs: () => pargs,
|
|
40
|
+
parseNumber: () => parseNumber,
|
|
41
|
+
penv: () => penv,
|
|
42
|
+
periodical: () => periodical,
|
|
43
|
+
pup: () => pup,
|
|
44
|
+
pupAppPath: () => pupAppPath,
|
|
45
|
+
pupFFmpegPath: () => pupFFmpegPath,
|
|
46
|
+
pupLogLevel: () => pupLogLevel,
|
|
47
|
+
pupUseInnerProxy: () => pupUseInnerProxy,
|
|
48
|
+
sleep: () => sleep,
|
|
49
|
+
useRetry: () => useRetry
|
|
50
|
+
});
|
|
51
|
+
module.exports = __toCommonJS(index_exports);
|
|
52
|
+
|
|
53
|
+
// src/base/constants.ts
|
|
54
|
+
var import_fs = require("fs");
|
|
55
|
+
var import_path = require("path");
|
|
56
|
+
|
|
57
|
+
// src/base/env.ts
|
|
58
|
+
function penv(name, parser, defaultValue) {
|
|
59
|
+
try {
|
|
60
|
+
return parser(process.env[name]);
|
|
61
|
+
} catch {
|
|
62
|
+
return defaultValue;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// src/base/parser.ts
|
|
67
|
+
function parseNumber(value) {
|
|
68
|
+
if (typeof value === "number") {
|
|
69
|
+
return value;
|
|
70
|
+
}
|
|
71
|
+
const num = Number(value);
|
|
72
|
+
if (Number.isNaN(num)) {
|
|
73
|
+
throw new Error(`Value ${value} is not a valid number`);
|
|
74
|
+
}
|
|
75
|
+
return num;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// src/base/constants.ts
|
|
79
|
+
var pupAppSearchPaths = [
|
|
80
|
+
(0, import_path.resolve)(__dirname, "cjs/app.cjs"),
|
|
81
|
+
// process from dist
|
|
82
|
+
(0, import_path.resolve)(__dirname, "app.cjs"),
|
|
83
|
+
// process from dist/cjs
|
|
84
|
+
(0, import_path.resolve)(__dirname, "../../cjs/app.cjs")
|
|
85
|
+
// process from src
|
|
86
|
+
];
|
|
87
|
+
var pupAppPath = pupAppSearchPaths.find(import_fs.existsSync);
|
|
88
|
+
var env = process.env;
|
|
89
|
+
var pupLogLevel = penv("PUP_LOG_LEVEL", parseNumber, 2);
|
|
90
|
+
var pupUseInnerProxy = env["PUP_USE_INNER_PROXY"] === "1";
|
|
91
|
+
var pupFFmpegPath = env["FFMPEG_BIN"] ?? `ffmpeg`;
|
|
92
|
+
|
|
93
|
+
// src/base/lazy.ts
|
|
94
|
+
var Lazy = class {
|
|
95
|
+
constructor(makeValue) {
|
|
96
|
+
this.makeValue = makeValue;
|
|
97
|
+
}
|
|
98
|
+
get value() {
|
|
99
|
+
if (!this._initialized) {
|
|
100
|
+
this._value = this.makeValue();
|
|
101
|
+
this._initialized = true;
|
|
102
|
+
}
|
|
103
|
+
return this._value;
|
|
104
|
+
}
|
|
105
|
+
get initialized() {
|
|
106
|
+
return this._initialized;
|
|
107
|
+
}
|
|
108
|
+
_initialized = false;
|
|
109
|
+
_value;
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
// src/base/limiter.ts
|
|
113
|
+
var ConcurrencyLimiter = class {
|
|
114
|
+
constructor(maxConcurrency) {
|
|
115
|
+
this.maxConcurrency = maxConcurrency;
|
|
116
|
+
}
|
|
117
|
+
_active = 0;
|
|
118
|
+
_queue = [];
|
|
119
|
+
_pending = 0;
|
|
120
|
+
_ended = false;
|
|
121
|
+
get active() {
|
|
122
|
+
return this._active;
|
|
123
|
+
}
|
|
124
|
+
get pending() {
|
|
125
|
+
return this._pending;
|
|
126
|
+
}
|
|
127
|
+
async schedule(fn) {
|
|
128
|
+
if (this._ended) {
|
|
129
|
+
throw new Error("ended");
|
|
130
|
+
}
|
|
131
|
+
return new Promise((resolve3, reject) => {
|
|
132
|
+
const run = () => {
|
|
133
|
+
this._active++;
|
|
134
|
+
this._pending--;
|
|
135
|
+
fn().then(resolve3).catch(reject).finally(() => {
|
|
136
|
+
this._active--;
|
|
137
|
+
this.next();
|
|
138
|
+
});
|
|
139
|
+
};
|
|
140
|
+
this._pending++;
|
|
141
|
+
if (this._active < this.maxConcurrency) {
|
|
142
|
+
run();
|
|
143
|
+
} else {
|
|
144
|
+
this._queue.push(run);
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
async end() {
|
|
149
|
+
if (!this._ended) {
|
|
150
|
+
this._ended = true;
|
|
151
|
+
while (this._active > 0 || this._pending > 0) {
|
|
152
|
+
await new Promise((resolve3) => setTimeout(resolve3, 50));
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
next() {
|
|
157
|
+
if (this._active < this.maxConcurrency && this._queue.length > 0) {
|
|
158
|
+
this._queue.shift()?.();
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
// src/base/logging.ts
|
|
164
|
+
var DEBUG = "<pup@debug>";
|
|
165
|
+
var INFO = "<pup@info>";
|
|
166
|
+
var WARN = "<pup@warn>";
|
|
167
|
+
var ERROR = "<pup@error>";
|
|
168
|
+
var FATAL = "<pup@fatal>";
|
|
169
|
+
var Logger = class {
|
|
170
|
+
_impl;
|
|
171
|
+
get impl() {
|
|
172
|
+
return this._impl;
|
|
173
|
+
}
|
|
174
|
+
set impl(value) {
|
|
175
|
+
const debug = value.debug ?? console.debug;
|
|
176
|
+
const info = value.info ?? console.info;
|
|
177
|
+
const warn = value.warn ?? console.warn;
|
|
178
|
+
const error = value.error ?? console.error;
|
|
179
|
+
this._impl = {
|
|
180
|
+
debug: pupLogLevel >= 3 ? debug : void 0,
|
|
181
|
+
info: pupLogLevel >= 2 ? info : void 0,
|
|
182
|
+
warn: pupLogLevel >= 1 ? warn : void 0,
|
|
183
|
+
error: pupLogLevel >= 0 ? error : void 0
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
constructor() {
|
|
187
|
+
this.impl = console;
|
|
188
|
+
}
|
|
189
|
+
debug(...messages) {
|
|
190
|
+
this.impl?.debug?.(DEBUG, ...messages);
|
|
191
|
+
}
|
|
192
|
+
info(...messages) {
|
|
193
|
+
this.impl?.info?.(INFO, ...messages);
|
|
194
|
+
}
|
|
195
|
+
warn(...messages) {
|
|
196
|
+
this.impl?.warn?.(WARN, ...messages);
|
|
197
|
+
}
|
|
198
|
+
error(...messages) {
|
|
199
|
+
this.impl?.error?.(ERROR, ...messages);
|
|
200
|
+
}
|
|
201
|
+
fatal(...messages) {
|
|
202
|
+
this.impl?.error?.(FATAL, ...messages);
|
|
203
|
+
process.exit(1);
|
|
204
|
+
}
|
|
205
|
+
dispatch(message) {
|
|
206
|
+
if (message.startsWith(DEBUG)) {
|
|
207
|
+
this.debug(message.slice(DEBUG.length + 1));
|
|
208
|
+
} else if (message.startsWith(INFO)) {
|
|
209
|
+
this.info(message.slice(INFO.length + 1));
|
|
210
|
+
} else if (message.startsWith(WARN)) {
|
|
211
|
+
this.warn(message.slice(WARN.length + 1));
|
|
212
|
+
} else if (message.startsWith(ERROR)) {
|
|
213
|
+
this.error(message.slice(ERROR.length + 1));
|
|
214
|
+
} else {
|
|
215
|
+
this.info(message);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
attach(proc, name) {
|
|
219
|
+
return new Promise((resolve3, reject) => {
|
|
220
|
+
this.debug(`${name}.attach`);
|
|
221
|
+
let fatal = "";
|
|
222
|
+
const dispatch = (data) => {
|
|
223
|
+
const message = data.toString();
|
|
224
|
+
if (message.startsWith(FATAL)) {
|
|
225
|
+
fatal += message.slice(FATAL.length + 1);
|
|
226
|
+
} else {
|
|
227
|
+
this.dispatch(message);
|
|
228
|
+
}
|
|
229
|
+
};
|
|
230
|
+
proc.stderr?.on("data", dispatch);
|
|
231
|
+
proc.stdout?.on("data", dispatch);
|
|
232
|
+
proc.on("message", dispatch).on("error", (err) => {
|
|
233
|
+
fatal += err.message;
|
|
234
|
+
proc.kill();
|
|
235
|
+
}).once("close", (code, signal) => {
|
|
236
|
+
if (code || signal || fatal) {
|
|
237
|
+
fatal ||= `command failed: ${proc.spawnargs.join(" ")}`;
|
|
238
|
+
this.error(`${name}.close`, { code, signal, fatal });
|
|
239
|
+
reject(new Error(fatal));
|
|
240
|
+
} else {
|
|
241
|
+
this.debug(`${name}.close`);
|
|
242
|
+
resolve3();
|
|
243
|
+
}
|
|
244
|
+
}).on("unhandledRejection", (reason) => {
|
|
245
|
+
this.error(`${name}.unhandled`, reason);
|
|
246
|
+
}).on("uncaughtExceptionMonitor", (err) => {
|
|
247
|
+
this.error(`${name}.unhandled`, err);
|
|
248
|
+
});
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
};
|
|
252
|
+
var logger = new Logger();
|
|
253
|
+
|
|
254
|
+
// src/base/noerr.ts
|
|
255
|
+
function noerr(fn, defaultValue) {
|
|
256
|
+
return (...args) => {
|
|
257
|
+
try {
|
|
258
|
+
const ret = fn(...args);
|
|
259
|
+
if (ret instanceof Promise) {
|
|
260
|
+
return ret.catch(() => defaultValue);
|
|
261
|
+
}
|
|
262
|
+
return ret;
|
|
263
|
+
} catch {
|
|
264
|
+
return defaultValue;
|
|
265
|
+
}
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// src/base/process.ts
|
|
270
|
+
var import_child_process = require("child_process");
|
|
271
|
+
var PUP_ARGS_ENV_KEY = "__PUP_ARGS__";
|
|
272
|
+
function pargs() {
|
|
273
|
+
const pupArgs = process.env[PUP_ARGS_ENV_KEY];
|
|
274
|
+
if (pupArgs) {
|
|
275
|
+
const args = ["exec", ...process.argv.slice(-1)];
|
|
276
|
+
args.push(...JSON.parse(pupArgs));
|
|
277
|
+
logger.debug("pupargs", args);
|
|
278
|
+
return args;
|
|
279
|
+
}
|
|
280
|
+
logger.debug("procargv", process.argv);
|
|
281
|
+
return process.argv;
|
|
282
|
+
}
|
|
283
|
+
function exec(cmd, options) {
|
|
284
|
+
const parts = cmd.split(" ").filter((s) => s.length);
|
|
285
|
+
const [command, ...args] = parts;
|
|
286
|
+
if (!command) throw new Error("empty command");
|
|
287
|
+
const proc = (0, import_child_process.spawn)(command, args, {
|
|
288
|
+
stdio: "inherit",
|
|
289
|
+
...options
|
|
290
|
+
});
|
|
291
|
+
return { process: proc, wait: logger.attach(proc, command) };
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
// src/base/retry.ts
|
|
295
|
+
var import_promises = require("timers/promises");
|
|
296
|
+
|
|
297
|
+
// src/base/timing.ts
|
|
298
|
+
function sleep(ms) {
|
|
299
|
+
return new Promise((resolve3) => setTimeout(resolve3, ms));
|
|
300
|
+
}
|
|
301
|
+
function periodical(callback, ms) {
|
|
302
|
+
let token;
|
|
303
|
+
let closed = false;
|
|
304
|
+
async function tick(count) {
|
|
305
|
+
await callback(count);
|
|
306
|
+
if (closed) return;
|
|
307
|
+
token = setTimeout(() => tick(count + 1), ms);
|
|
308
|
+
}
|
|
309
|
+
token = setTimeout(() => tick(0), ms);
|
|
310
|
+
return () => {
|
|
311
|
+
closed = true;
|
|
312
|
+
clearTimeout(token);
|
|
313
|
+
};
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
// src/base/retry.ts
|
|
317
|
+
function useRetry({
|
|
318
|
+
fn,
|
|
319
|
+
maxAttempts = 3,
|
|
320
|
+
timeout
|
|
321
|
+
}) {
|
|
322
|
+
const timeoutError = new Error(`timeout over ${timeout}ms`);
|
|
323
|
+
return async function(...args) {
|
|
324
|
+
let attempt = 0;
|
|
325
|
+
while (true) {
|
|
326
|
+
try {
|
|
327
|
+
const promises = [fn(...args)];
|
|
328
|
+
if (timeout) {
|
|
329
|
+
promises.push(
|
|
330
|
+
(0, import_promises.setTimeout)(timeout).then(() => {
|
|
331
|
+
throw timeoutError;
|
|
332
|
+
})
|
|
333
|
+
);
|
|
334
|
+
}
|
|
335
|
+
return await Promise.race(promises);
|
|
336
|
+
} catch (e) {
|
|
337
|
+
attempt++;
|
|
338
|
+
if (attempt >= maxAttempts) {
|
|
339
|
+
throw e;
|
|
340
|
+
}
|
|
341
|
+
await sleep(Math.pow(2, attempt) * 100 + Math.random() * 100);
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
};
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
// src/pup.ts
|
|
348
|
+
var import_child_process2 = require("child_process");
|
|
349
|
+
var import_promises2 = require("fs/promises");
|
|
350
|
+
var import_path3 = require("path");
|
|
351
|
+
|
|
352
|
+
// src/base/abort.ts
|
|
353
|
+
var AbortLink = class _AbortLink {
|
|
354
|
+
constructor(query, interval = 1e3) {
|
|
355
|
+
this.query = query;
|
|
356
|
+
this.interval = interval;
|
|
357
|
+
if (query) {
|
|
358
|
+
this.tick();
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
_callback;
|
|
362
|
+
_aborted;
|
|
363
|
+
_stopped = false;
|
|
364
|
+
static start(query, interval) {
|
|
365
|
+
return new _AbortLink(query, interval);
|
|
366
|
+
}
|
|
367
|
+
get aborted() {
|
|
368
|
+
return !this._stopped && this._aborted;
|
|
369
|
+
}
|
|
370
|
+
get stopped() {
|
|
371
|
+
return this._stopped;
|
|
372
|
+
}
|
|
373
|
+
async onAbort(callback) {
|
|
374
|
+
if (this._aborted) {
|
|
375
|
+
await callback();
|
|
376
|
+
} else {
|
|
377
|
+
this._callback = callback;
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
wait(...handles) {
|
|
381
|
+
const abort = new Promise((_, reject) => {
|
|
382
|
+
this.onAbort(async () => {
|
|
383
|
+
handles.forEach((h) => h.process.kill());
|
|
384
|
+
reject(new Error("aborted"));
|
|
385
|
+
});
|
|
386
|
+
});
|
|
387
|
+
return Promise.race([
|
|
388
|
+
abort,
|
|
389
|
+
Promise.all(handles.map((h) => h.wait))
|
|
390
|
+
//
|
|
391
|
+
]);
|
|
392
|
+
}
|
|
393
|
+
stop() {
|
|
394
|
+
this._stopped = true;
|
|
395
|
+
}
|
|
396
|
+
tick() {
|
|
397
|
+
setTimeout(async () => {
|
|
398
|
+
if (this._stopped) {
|
|
399
|
+
return;
|
|
400
|
+
}
|
|
401
|
+
this._aborted = await this.query?.();
|
|
402
|
+
if (this._stopped) {
|
|
403
|
+
return;
|
|
404
|
+
}
|
|
405
|
+
if (this._aborted) {
|
|
406
|
+
await this._callback?.();
|
|
407
|
+
} else {
|
|
408
|
+
this.tick();
|
|
409
|
+
}
|
|
410
|
+
}, this.interval);
|
|
411
|
+
}
|
|
412
|
+
};
|
|
413
|
+
|
|
414
|
+
// src/base/electron.ts
|
|
415
|
+
var import_electron = __toESM(require("electron"), 1);
|
|
416
|
+
var ELECTRON_OPTS = [
|
|
417
|
+
"no-sandbox",
|
|
418
|
+
"disable-setuid-sandbox",
|
|
419
|
+
"disable-gpu",
|
|
420
|
+
"disable-dev-shm-usage",
|
|
421
|
+
"disable-software-rasterizer",
|
|
422
|
+
"disable-web-security",
|
|
423
|
+
"disable-site-isolation-trials",
|
|
424
|
+
"disable-features=IsolateOrigins,site-per-process",
|
|
425
|
+
"allow-insecure-localhost",
|
|
426
|
+
"ignore-certificate-errors",
|
|
427
|
+
"disable-blink-features=AutomationControlled",
|
|
428
|
+
"mute-audio",
|
|
429
|
+
"disable-extensions",
|
|
430
|
+
"disable-background-networking",
|
|
431
|
+
"address-family=ipv4",
|
|
432
|
+
"disable-async-dns",
|
|
433
|
+
"force-device-scale-factor=1",
|
|
434
|
+
"trace-warnings",
|
|
435
|
+
"force-color-profile=srgb",
|
|
436
|
+
"disable-color-correct-rendering",
|
|
437
|
+
"log-level=3"
|
|
438
|
+
];
|
|
439
|
+
function runElectronApp(size, app, args) {
|
|
440
|
+
const electronArgs = ELECTRON_OPTS.map((a) => `--${a}`);
|
|
441
|
+
const cmdParts = [];
|
|
442
|
+
if (process.platform === "linux") {
|
|
443
|
+
cmdParts.push(
|
|
444
|
+
"xvfb-run",
|
|
445
|
+
"--auto-servernum",
|
|
446
|
+
`--server-args='-screen 0 ${size.width}x${size.height}x24'`
|
|
447
|
+
);
|
|
448
|
+
}
|
|
449
|
+
cmdParts.push(import_electron.default, ...electronArgs, app);
|
|
450
|
+
return exec(cmdParts.join(" "), {
|
|
451
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
452
|
+
shell: true,
|
|
453
|
+
env: {
|
|
454
|
+
...process.env,
|
|
455
|
+
ELECTRON_DISABLE_DBUS: "1",
|
|
456
|
+
RUST_BACKTRACE: "full",
|
|
457
|
+
__PUP_ARGS__: JSON.stringify(args)
|
|
458
|
+
}
|
|
459
|
+
});
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
// src/base/ffmpeg.ts
|
|
463
|
+
var import_fs2 = require("fs");
|
|
464
|
+
var import_path2 = require("path");
|
|
465
|
+
var import_process2 = require("process");
|
|
466
|
+
var quiet = ["-hide_banner", "-loglevel", "error", "-nostats"];
|
|
467
|
+
function resolveX265() {
|
|
468
|
+
const path = `x265/${import_process2.platform}-${import_process2.arch}`;
|
|
469
|
+
const dirs = [
|
|
470
|
+
(0, import_path2.resolve)(__dirname, `../../${path}`),
|
|
471
|
+
// process from src
|
|
472
|
+
(0, import_path2.resolve)(__dirname, `../${path}`)
|
|
473
|
+
// process from dist
|
|
474
|
+
];
|
|
475
|
+
const found = dirs.find(import_fs2.existsSync);
|
|
476
|
+
if (!found) {
|
|
477
|
+
throw new Error("x265 not found");
|
|
478
|
+
}
|
|
479
|
+
return found;
|
|
480
|
+
}
|
|
481
|
+
function createBgraFileCommand(bgraPath, spec, files) {
|
|
482
|
+
const { fps, frames } = spec;
|
|
483
|
+
const args = [
|
|
484
|
+
"-y",
|
|
485
|
+
...quiet,
|
|
486
|
+
"-f",
|
|
487
|
+
"rawvideo",
|
|
488
|
+
"-pix_fmt",
|
|
489
|
+
"bgra",
|
|
490
|
+
"-s",
|
|
491
|
+
`${spec.size.width}x${spec.size.height}`,
|
|
492
|
+
"-r",
|
|
493
|
+
`${fps}`,
|
|
494
|
+
"-i",
|
|
495
|
+
bgraPath,
|
|
496
|
+
"-frames:v",
|
|
497
|
+
`${Math.floor(frames)}`
|
|
498
|
+
];
|
|
499
|
+
if (files.mp4) {
|
|
500
|
+
args.push(
|
|
501
|
+
"-colorspace",
|
|
502
|
+
"bt709",
|
|
503
|
+
"-color_primaries",
|
|
504
|
+
"bt709",
|
|
505
|
+
"-color_trc",
|
|
506
|
+
"bt709",
|
|
507
|
+
"-c:v",
|
|
508
|
+
"libx264",
|
|
509
|
+
"-preset",
|
|
510
|
+
"fast",
|
|
511
|
+
"-threads",
|
|
512
|
+
"0",
|
|
513
|
+
"-pix_fmt",
|
|
514
|
+
"yuv420p",
|
|
515
|
+
"-movflags",
|
|
516
|
+
"+faststart",
|
|
517
|
+
files.mp4
|
|
518
|
+
);
|
|
519
|
+
}
|
|
520
|
+
if (files.webm) {
|
|
521
|
+
args.push(
|
|
522
|
+
"-c:v",
|
|
523
|
+
"libvpx-vp9",
|
|
524
|
+
"-row-mt",
|
|
525
|
+
"1",
|
|
526
|
+
"-cpu-used",
|
|
527
|
+
"1",
|
|
528
|
+
"-threads",
|
|
529
|
+
"0",
|
|
530
|
+
"-pix_fmt",
|
|
531
|
+
"yuva420p",
|
|
532
|
+
"-auto-alt-ref",
|
|
533
|
+
"0",
|
|
534
|
+
files.webm
|
|
535
|
+
);
|
|
536
|
+
}
|
|
537
|
+
return { command: pupFFmpegPath, args };
|
|
538
|
+
}
|
|
539
|
+
function createBgraToMovPipeline(bgraPath, spec, mov) {
|
|
540
|
+
const { fps, size } = spec;
|
|
541
|
+
return {
|
|
542
|
+
raw: {
|
|
543
|
+
command: pupFFmpegPath,
|
|
544
|
+
args: [
|
|
545
|
+
"-y",
|
|
546
|
+
...quiet,
|
|
547
|
+
"-f",
|
|
548
|
+
"rawvideo",
|
|
549
|
+
"-pix_fmt",
|
|
550
|
+
"bgra",
|
|
551
|
+
"-s",
|
|
552
|
+
`${size.width}x${size.height}`,
|
|
553
|
+
"-r",
|
|
554
|
+
`${fps}`,
|
|
555
|
+
"-i",
|
|
556
|
+
bgraPath,
|
|
557
|
+
"-f",
|
|
558
|
+
"rawvideo",
|
|
559
|
+
"-pix_fmt",
|
|
560
|
+
"yuva420p10le",
|
|
561
|
+
"pipe:1"
|
|
562
|
+
]
|
|
563
|
+
},
|
|
564
|
+
x265: {
|
|
565
|
+
command: resolveX265(),
|
|
566
|
+
args: [
|
|
567
|
+
"--no-progress",
|
|
568
|
+
"--log-level",
|
|
569
|
+
"error",
|
|
570
|
+
"--input",
|
|
571
|
+
"-",
|
|
572
|
+
"--input-res",
|
|
573
|
+
`${size.width}x${size.height}`,
|
|
574
|
+
"--input-csp",
|
|
575
|
+
"i420",
|
|
576
|
+
"--input-depth",
|
|
577
|
+
"10",
|
|
578
|
+
"--output-depth",
|
|
579
|
+
"10",
|
|
580
|
+
"--fps",
|
|
581
|
+
`${fps}`,
|
|
582
|
+
"--alpha",
|
|
583
|
+
"--bframes",
|
|
584
|
+
"0",
|
|
585
|
+
"--colorprim",
|
|
586
|
+
"bt709",
|
|
587
|
+
"--transfer",
|
|
588
|
+
"bt709",
|
|
589
|
+
"--colormatrix",
|
|
590
|
+
"bt709",
|
|
591
|
+
"--crf",
|
|
592
|
+
"18",
|
|
593
|
+
"--output",
|
|
594
|
+
"-"
|
|
595
|
+
]
|
|
596
|
+
},
|
|
597
|
+
mux: {
|
|
598
|
+
command: pupFFmpegPath,
|
|
599
|
+
args: [
|
|
600
|
+
"-y",
|
|
601
|
+
...quiet,
|
|
602
|
+
"-f",
|
|
603
|
+
"hevc",
|
|
604
|
+
"-r",
|
|
605
|
+
`${fps}`,
|
|
606
|
+
"-i",
|
|
607
|
+
"pipe:0",
|
|
608
|
+
"-c:v",
|
|
609
|
+
"copy",
|
|
610
|
+
"-tag:v",
|
|
611
|
+
"hvc1",
|
|
612
|
+
"-movflags",
|
|
613
|
+
"+faststart",
|
|
614
|
+
mov
|
|
615
|
+
]
|
|
616
|
+
}
|
|
617
|
+
};
|
|
618
|
+
}
|
|
619
|
+
function createCoverCommand(src, dst) {
|
|
620
|
+
return {
|
|
621
|
+
command: pupFFmpegPath,
|
|
622
|
+
args: ["-y", ...quiet, "-i", src, "-frames:v", "1", "-q:v", "2", dst]
|
|
623
|
+
};
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
// src/base/encoder.ts
|
|
627
|
+
function encodeBgraFile(bgraPath, outputPath, spec, format) {
|
|
628
|
+
const files = format === "mp4" ? { mp4: outputPath } : { webm: outputPath };
|
|
629
|
+
const cmd = createBgraFileCommand(bgraPath, spec, files);
|
|
630
|
+
return exec(`${cmd.command} ${cmd.args.join(" ")}`, {
|
|
631
|
+
stdio: ["ignore", "inherit", "inherit"]
|
|
632
|
+
});
|
|
633
|
+
}
|
|
634
|
+
function encodeBgraToMov(bgraPath, movPath, spec) {
|
|
635
|
+
const x265 = createBgraToMovPipeline(bgraPath, spec, movPath);
|
|
636
|
+
const shellCmd = [
|
|
637
|
+
`${x265.raw.command} ${x265.raw.args.join(" ")}`,
|
|
638
|
+
`${x265.x265.command} ${x265.x265.args.join(" ")}`,
|
|
639
|
+
`${x265.mux.command} ${x265.mux.args.join(" ")}`
|
|
640
|
+
].join(" | ");
|
|
641
|
+
return exec(shellCmd, {
|
|
642
|
+
stdio: ["ignore", "inherit", "inherit"],
|
|
643
|
+
shell: true
|
|
644
|
+
});
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
// src/base/stream.ts
|
|
648
|
+
function waitAll(...procs) {
|
|
649
|
+
return Promise.all(
|
|
650
|
+
procs.map(
|
|
651
|
+
(proc) => new Promise((resolve3, reject) => {
|
|
652
|
+
proc.on("error", reject);
|
|
653
|
+
proc.on(
|
|
654
|
+
"close",
|
|
655
|
+
(code) => code === 0 ? resolve3() : reject(new Error(`exit ${code ?? "null"}`))
|
|
656
|
+
);
|
|
657
|
+
})
|
|
658
|
+
)
|
|
659
|
+
);
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
// src/common.ts
|
|
663
|
+
var import_commander = require("commander");
|
|
664
|
+
var DEFAULT_WIDTH = 1920;
|
|
665
|
+
var DEFAULT_HEIGHT = 1080;
|
|
666
|
+
|
|
667
|
+
// src/pup.ts
|
|
668
|
+
var TAG = "[pup]";
|
|
669
|
+
var PROGRESS_TAG = " progress: ";
|
|
670
|
+
function runPupApp(source, options) {
|
|
671
|
+
logger.debug(TAG, `runPupApp`, source, options);
|
|
672
|
+
const args = [source];
|
|
673
|
+
if (options.width) args.push("--width", `${options.width}`);
|
|
674
|
+
if (options.height) args.push("--height", `${options.height}`);
|
|
675
|
+
if (options.fps) args.push("--fps", `${options.fps}`);
|
|
676
|
+
if (options.duration) args.push("--duration", `${options.duration}`);
|
|
677
|
+
if (options.outDir) args.push("--out-dir", options.outDir);
|
|
678
|
+
if (options.withAlphaChannel) args.push("--with-alpha-channel");
|
|
679
|
+
const w = options.width ?? DEFAULT_WIDTH;
|
|
680
|
+
const h = options.height ?? DEFAULT_HEIGHT;
|
|
681
|
+
const handle = runElectronApp({ width: w, height: h }, pupAppPath, args);
|
|
682
|
+
const counter = new ConcurrencyLimiter(1);
|
|
683
|
+
handle.process.stdout?.on("data", (data) => {
|
|
684
|
+
let message = data.toString().trim();
|
|
685
|
+
let start = message.indexOf(PROGRESS_TAG);
|
|
686
|
+
if (start < 0) {
|
|
687
|
+
return;
|
|
688
|
+
}
|
|
689
|
+
message = message.slice(start + PROGRESS_TAG.length);
|
|
690
|
+
const end = message.indexOf("%");
|
|
691
|
+
if (end < 0) {
|
|
692
|
+
return;
|
|
693
|
+
}
|
|
694
|
+
const progressStr = message.slice(0, end);
|
|
695
|
+
const progress = parseNumber(progressStr);
|
|
696
|
+
counter.schedule(async () => {
|
|
697
|
+
await options.onProgress?.(progress);
|
|
698
|
+
});
|
|
699
|
+
});
|
|
700
|
+
return { handle, counter };
|
|
701
|
+
}
|
|
702
|
+
async function pup(source, options) {
|
|
703
|
+
logger.debug(TAG, `pup`, source, options);
|
|
704
|
+
const link = AbortLink.start(options.cancelQuery);
|
|
705
|
+
const outDir = options.outDir ?? "out";
|
|
706
|
+
const t0 = performance.now();
|
|
707
|
+
const { handle, counter } = runPupApp(source, { ...options, outDir });
|
|
708
|
+
await link.wait(handle);
|
|
709
|
+
await counter.end();
|
|
710
|
+
logger.info(TAG, `capture cost ${Math.round(performance.now() - t0)}ms`);
|
|
711
|
+
const metaPath = (0, import_path3.join)(outDir, "record.json");
|
|
712
|
+
const meta = JSON.parse(await (0, import_promises2.readFile)(metaPath, "utf-8"));
|
|
713
|
+
const { bgraPath, written, options: recordOptions } = meta;
|
|
714
|
+
const { fps, width, height, withAlphaChannel } = recordOptions;
|
|
715
|
+
const size = { width, height };
|
|
716
|
+
const outputs = {
|
|
717
|
+
mp4: withAlphaChannel ? void 0 : (0, import_path3.join)(outDir, "output.mp4"),
|
|
718
|
+
webm: withAlphaChannel ? (0, import_path3.join)(outDir, "output.webm") : void 0,
|
|
719
|
+
mov: withAlphaChannel ? (0, import_path3.join)(outDir, "output.mov") : void 0,
|
|
720
|
+
cover: (0, import_path3.join)(outDir, "cover.png")
|
|
721
|
+
};
|
|
722
|
+
try {
|
|
723
|
+
const t1 = performance.now();
|
|
724
|
+
const spec = { fps, frames: written, size };
|
|
725
|
+
const handles = [];
|
|
726
|
+
if (outputs.mp4) {
|
|
727
|
+
handles.push(encodeBgraFile(bgraPath, outputs.mp4, spec, "mp4"));
|
|
728
|
+
}
|
|
729
|
+
if (outputs.webm) {
|
|
730
|
+
handles.push(encodeBgraFile(bgraPath, outputs.webm, spec, "webm"));
|
|
731
|
+
}
|
|
732
|
+
if (outputs.mov) {
|
|
733
|
+
handles.push(encodeBgraToMov(bgraPath, outputs.mov, spec));
|
|
734
|
+
}
|
|
735
|
+
await link.wait(...handles);
|
|
736
|
+
const coverSrc = outputs.mov ?? outputs.webm ?? outputs.mp4;
|
|
737
|
+
if (coverSrc) {
|
|
738
|
+
const coverCmd = createCoverCommand(coverSrc, outputs.cover);
|
|
739
|
+
await waitAll(
|
|
740
|
+
(0, import_child_process2.spawn)(coverCmd.command, coverCmd.args, { stdio: "inherit" })
|
|
741
|
+
);
|
|
742
|
+
}
|
|
743
|
+
link.stop();
|
|
744
|
+
logger.info(TAG, `encoding cost ${Math.round(performance.now() - t1)}ms`);
|
|
745
|
+
await Promise.all([
|
|
746
|
+
(0, import_promises2.rm)(bgraPath, { force: true }),
|
|
747
|
+
(0, import_promises2.rm)(metaPath, { force: true })
|
|
748
|
+
]);
|
|
749
|
+
return {
|
|
750
|
+
...outputs,
|
|
751
|
+
width,
|
|
752
|
+
height,
|
|
753
|
+
fps,
|
|
754
|
+
duration: Math.ceil(written / fps)
|
|
755
|
+
};
|
|
756
|
+
} catch (error) {
|
|
757
|
+
await (0, import_promises2.rm)(outDir, { recursive: true, force: true });
|
|
758
|
+
throw error;
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
762
|
+
0 && (module.exports = {
|
|
763
|
+
ConcurrencyLimiter,
|
|
764
|
+
Lazy,
|
|
765
|
+
PUP_ARGS_ENV_KEY,
|
|
766
|
+
exec,
|
|
767
|
+
logger,
|
|
768
|
+
noerr,
|
|
769
|
+
pargs,
|
|
770
|
+
parseNumber,
|
|
771
|
+
penv,
|
|
772
|
+
periodical,
|
|
773
|
+
pup,
|
|
774
|
+
pupAppPath,
|
|
775
|
+
pupFFmpegPath,
|
|
776
|
+
pupLogLevel,
|
|
777
|
+
pupUseInnerProxy,
|
|
778
|
+
sleep,
|
|
779
|
+
useRetry
|
|
780
|
+
});
|
|
781
|
+
//# sourceMappingURL=index.cjs.map
|