@webreel/core 0.1.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 +188 -0
- package/assets/click-1.mp3 +0 -0
- package/assets/click-2.mp3 +0 -0
- package/assets/click-3.mp3 +0 -0
- package/assets/click-4.mp3 +0 -0
- package/assets/key-1.mp3 +0 -0
- package/assets/key-2.mp3 +0 -0
- package/assets/key-3.mp3 +0 -0
- package/assets/key-4.mp3 +0 -0
- package/dist/__tests__/actions.test.d.ts +2 -0
- package/dist/__tests__/actions.test.d.ts.map +1 -0
- package/dist/__tests__/actions.test.js +252 -0
- package/dist/__tests__/actions.test.js.map +1 -0
- package/dist/__tests__/chrome.test.d.ts +2 -0
- package/dist/__tests__/chrome.test.d.ts.map +1 -0
- package/dist/__tests__/chrome.test.js +29 -0
- package/dist/__tests__/chrome.test.js.map +1 -0
- package/dist/__tests__/cursor-motion.test.d.ts +2 -0
- package/dist/__tests__/cursor-motion.test.d.ts.map +1 -0
- package/dist/__tests__/cursor-motion.test.js +39 -0
- package/dist/__tests__/cursor-motion.test.js.map +1 -0
- package/dist/__tests__/ffmpeg.test.d.ts +2 -0
- package/dist/__tests__/ffmpeg.test.d.ts.map +1 -0
- package/dist/__tests__/ffmpeg.test.js +90 -0
- package/dist/__tests__/ffmpeg.test.js.map +1 -0
- package/dist/__tests__/media.test.d.ts +2 -0
- package/dist/__tests__/media.test.d.ts.map +1 -0
- package/dist/__tests__/media.test.js +98 -0
- package/dist/__tests__/media.test.js.map +1 -0
- package/dist/__tests__/overlays.test.d.ts +2 -0
- package/dist/__tests__/overlays.test.d.ts.map +1 -0
- package/dist/__tests__/overlays.test.js +109 -0
- package/dist/__tests__/overlays.test.js.map +1 -0
- package/dist/__tests__/recording-context.test.d.ts +2 -0
- package/dist/__tests__/recording-context.test.d.ts.map +1 -0
- package/dist/__tests__/recording-context.test.js +46 -0
- package/dist/__tests__/recording-context.test.js.map +1 -0
- package/dist/__tests__/timeline.test.d.ts +2 -0
- package/dist/__tests__/timeline.test.d.ts.map +1 -0
- package/dist/__tests__/timeline.test.js +88 -0
- package/dist/__tests__/timeline.test.js.map +1 -0
- package/dist/actions.d.ts +65 -0
- package/dist/actions.d.ts.map +1 -0
- package/dist/actions.js +729 -0
- package/dist/actions.js.map +1 -0
- package/dist/cdp.d.ts +3 -0
- package/dist/cdp.d.ts.map +1 -0
- package/dist/cdp.js +5 -0
- package/dist/cdp.js.map +1 -0
- package/dist/chrome.d.ts +12 -0
- package/dist/chrome.d.ts.map +1 -0
- package/dist/chrome.js +241 -0
- package/dist/chrome.js.map +1 -0
- package/dist/compositor.d.ts +8 -0
- package/dist/compositor.d.ts.map +1 -0
- package/dist/compositor.js +224 -0
- package/dist/compositor.js.map +1 -0
- package/dist/cursor-motion.d.ts +17 -0
- package/dist/cursor-motion.d.ts.map +1 -0
- package/dist/cursor-motion.js +138 -0
- package/dist/cursor-motion.js.map +1 -0
- package/dist/download.d.ts +6 -0
- package/dist/download.d.ts.map +1 -0
- package/dist/download.js +78 -0
- package/dist/download.js.map +1 -0
- package/dist/ffmpeg.d.ts +5 -0
- package/dist/ffmpeg.d.ts.map +1 -0
- package/dist/ffmpeg.js +106 -0
- package/dist/ffmpeg.js.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -0
- package/dist/media.d.ts +23 -0
- package/dist/media.d.ts.map +1 -0
- package/dist/media.js +155 -0
- package/dist/media.js.map +1 -0
- package/dist/overlays.d.ts +21 -0
- package/dist/overlays.d.ts.map +1 -0
- package/dist/overlays.js +97 -0
- package/dist/overlays.js.map +1 -0
- package/dist/recorder.d.ts +41 -0
- package/dist/recorder.d.ts.map +1 -0
- package/dist/recorder.js +223 -0
- package/dist/recorder.js.map +1 -0
- package/dist/timeline.d.ts +82 -0
- package/dist/timeline.d.ts.map +1 -0
- package/dist/timeline.js +140 -0
- package/dist/timeline.js.map +1 -0
- package/dist/types.d.ts +90 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +16 -0
- package/dist/types.js.map +1 -0
- package/package.json +51 -0
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { FRAME_MS, CAPTURE_CYCLE_MS } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Fitts's law inspired duration: scales with sqrt of distance.
|
|
4
|
+
* Returns milliseconds. Tuned so short moves feel quick but not
|
|
5
|
+
* instant, and long cross-screen moves have enough frames to
|
|
6
|
+
* appear smooth at the target capture rate.
|
|
7
|
+
*/
|
|
8
|
+
function moveDuration(distance) {
|
|
9
|
+
return 180 + 16 * Math.sqrt(distance) + (Math.random() - 0.5) * 30;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Asymmetric ease-in-out: reaches 50% progress at 40% of elapsed time.
|
|
13
|
+
* Acceleration is quadratic; deceleration is cubic (gentler settle-in).
|
|
14
|
+
* C1-continuous at the inflection point.
|
|
15
|
+
*/
|
|
16
|
+
function humanEase(t) {
|
|
17
|
+
const mid = 0.4;
|
|
18
|
+
if (t <= mid) {
|
|
19
|
+
const s = t / mid;
|
|
20
|
+
return 0.5 * s * s;
|
|
21
|
+
}
|
|
22
|
+
const s = (t - mid) / (1 - mid);
|
|
23
|
+
return 0.5 + 0.5 * (1 - (1 - s) * (1 - s) * (1 - s));
|
|
24
|
+
}
|
|
25
|
+
function bezierControl(x0, y0, x1, y1, dist) {
|
|
26
|
+
const mx = (x0 + x1) / 2;
|
|
27
|
+
const my = (y0 + y1) / 2;
|
|
28
|
+
if (dist < 80)
|
|
29
|
+
return { x: mx, y: my };
|
|
30
|
+
const px = -(y1 - y0) / dist;
|
|
31
|
+
const py = (x1 - x0) / dist;
|
|
32
|
+
const offset = dist * (0.03 + Math.random() * 0.07) * (Math.random() < 0.5 ? -1 : 1);
|
|
33
|
+
return { x: mx + px * offset, y: my + py * offset };
|
|
34
|
+
}
|
|
35
|
+
function evalBezier(t, p0, p1, p2) {
|
|
36
|
+
const m = 1 - t;
|
|
37
|
+
return {
|
|
38
|
+
x: m * m * p0.x + 2 * m * t * p1.x + t * t * p2.x,
|
|
39
|
+
y: m * m * p0.y + 2 * m * t * p1.y + t * t * p2.y,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Animate cursor from one position to another. Pre-computes the full
|
|
44
|
+
* bezier path with easing and jitter, then registers a frame-based
|
|
45
|
+
* tick function (window.__tickCursor) that the recorder calls before
|
|
46
|
+
* every captureScreenshot. Each tick advances exactly one step through
|
|
47
|
+
* the path, so every captured frame shows a smooth intermediate
|
|
48
|
+
* position regardless of actual capture latency.
|
|
49
|
+
*/
|
|
50
|
+
export async function animateMoveTo(ctx, client, fromX, fromY, toX, toY) {
|
|
51
|
+
const dx = toX - fromX;
|
|
52
|
+
const dy = toY - fromY;
|
|
53
|
+
const dist = Math.sqrt(dx * dx + dy * dy);
|
|
54
|
+
if (dist < 1)
|
|
55
|
+
return;
|
|
56
|
+
const duration = moveDuration(dist);
|
|
57
|
+
const ctrl = bezierControl(fromX, fromY, toX, toY, dist);
|
|
58
|
+
const p0 = { x: fromX, y: fromY };
|
|
59
|
+
const p2 = { x: toX, y: toY };
|
|
60
|
+
const NUM_STEPS = Math.max(6, Math.round(duration / FRAME_MS));
|
|
61
|
+
const positions = [{ x: fromX, y: fromY }];
|
|
62
|
+
for (let i = 1; i <= NUM_STEPS; i++) {
|
|
63
|
+
const rawT = i / NUM_STEPS;
|
|
64
|
+
const t = humanEase(rawT);
|
|
65
|
+
const pos = evalBezier(t, p0, ctrl, p2);
|
|
66
|
+
const jitter = dist > 60 ? microJitter(rawT, dist) : { x: 0, y: 0 };
|
|
67
|
+
positions.push({
|
|
68
|
+
x: Math.round((pos.x + jitter.x) * 10) / 10,
|
|
69
|
+
y: Math.round((pos.y + jitter.y) * 10) / 10,
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
positions[positions.length - 1] = { x: toX, y: toY };
|
|
73
|
+
if (ctx.isRecording && ctx.timeline) {
|
|
74
|
+
ctx.timeline.setCursorPath(positions);
|
|
75
|
+
await new Promise((r) => setTimeout(r, NUM_STEPS * CAPTURE_CYCLE_MS));
|
|
76
|
+
await client.Input.dispatchMouseEvent({
|
|
77
|
+
type: "mouseMoved",
|
|
78
|
+
x: toX,
|
|
79
|
+
y: toY,
|
|
80
|
+
});
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
await client.Runtime.evaluate({
|
|
84
|
+
expression: `(() => {
|
|
85
|
+
const el = document.getElementById("__demo-cursor");
|
|
86
|
+
if (!el) return;
|
|
87
|
+
const pts = ${JSON.stringify(positions)};
|
|
88
|
+
let idx = 0;
|
|
89
|
+
window.__tickCursor = function() {
|
|
90
|
+
if (idx >= pts.length) { window.__tickCursor = null; return; }
|
|
91
|
+
const p = pts[idx++];
|
|
92
|
+
el.style.transform = "translate(" + p.x + "px," + p.y + "px)";
|
|
93
|
+
el.dataset.cx = String(p.x);
|
|
94
|
+
el.dataset.cy = String(p.y);
|
|
95
|
+
if (idx >= pts.length) window.__tickCursor = null;
|
|
96
|
+
};
|
|
97
|
+
window.__tickCursor();
|
|
98
|
+
})()`,
|
|
99
|
+
});
|
|
100
|
+
await new Promise((r) => setTimeout(r, NUM_STEPS * CAPTURE_CYCLE_MS));
|
|
101
|
+
await client.Input.dispatchMouseEvent({
|
|
102
|
+
type: "mouseMoved",
|
|
103
|
+
x: toX,
|
|
104
|
+
y: toY,
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
function microJitter(t, dist) {
|
|
108
|
+
const bell = Math.exp(-8 * (t - 0.5) * (t - 0.5));
|
|
109
|
+
const mag = Math.min(0.4, dist * 0.0004) * bell;
|
|
110
|
+
return {
|
|
111
|
+
x: (Math.random() - 0.5) * 2 * mag,
|
|
112
|
+
y: (Math.random() - 0.5) * 2 * mag,
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
export function computeEasedPath(fromX, fromY, toX, toY, steps) {
|
|
116
|
+
const dx = toX - fromX;
|
|
117
|
+
const dy = toY - fromY;
|
|
118
|
+
const dist = Math.sqrt(dx * dx + dy * dy);
|
|
119
|
+
if (dist < 1)
|
|
120
|
+
return [{ x: toX, y: toY }];
|
|
121
|
+
const ctrl = bezierControl(fromX, fromY, toX, toY, dist);
|
|
122
|
+
const p0 = { x: fromX, y: fromY };
|
|
123
|
+
const p2 = { x: toX, y: toY };
|
|
124
|
+
const pts = [];
|
|
125
|
+
for (let i = 1; i <= steps; i++) {
|
|
126
|
+
const rawT = i / steps;
|
|
127
|
+
const t = humanEase(rawT);
|
|
128
|
+
pts.push(evalBezier(t, p0, ctrl, p2));
|
|
129
|
+
}
|
|
130
|
+
pts[pts.length - 1] = { x: toX, y: toY };
|
|
131
|
+
return pts;
|
|
132
|
+
}
|
|
133
|
+
export function computeDragTiming(distance) {
|
|
134
|
+
const duration = 300 + 20 * Math.sqrt(distance) + (Math.random() - 0.5) * 40;
|
|
135
|
+
const steps = Math.max(12, Math.round(duration / 30));
|
|
136
|
+
return { steps, delayMs: duration / steps };
|
|
137
|
+
}
|
|
138
|
+
//# sourceMappingURL=cursor-motion.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cursor-motion.js","sourceRoot":"","sources":["../src/cursor-motion.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export declare function fetchJson(url: string): Promise<unknown>;
|
|
2
|
+
export declare function downloadFile(url: string, destPath: string, label: string): Promise<void>;
|
|
3
|
+
export declare function extractArchive(archivePath: string, destDir: string): void;
|
|
4
|
+
export declare function downloadAndExtract(url: string, destDir: string, label: string): Promise<void>;
|
|
5
|
+
export declare function makeExecutable(path: string): void;
|
|
6
|
+
//# sourceMappingURL=download.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"download.d.ts","sourceRoot":"","sources":["../src/download.ts"],"names":[],"mappings":"AA6BA,wBAAsB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAO7D;AAED,wBAAsB,YAAY,CAChC,GAAG,EAAE,MAAM,EACX,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,IAAI,CAAC,CAgBf;AAED,wBAAgB,cAAc,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAmBzE;AAED,wBAAsB,kBAAkB,CACtC,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,IAAI,CAAC,CASf;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAIjD"}
|
package/dist/download.js
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { createWriteStream, mkdirSync, unlinkSync, chmodSync } from "node:fs";
|
|
2
|
+
import { resolve } from "node:path";
|
|
3
|
+
import { execFileSync } from "node:child_process";
|
|
4
|
+
import { Readable } from "node:stream";
|
|
5
|
+
import { pipeline } from "node:stream/promises";
|
|
6
|
+
const MAX_RETRIES = 3;
|
|
7
|
+
const RETRY_DELAY_MS = 1000;
|
|
8
|
+
async function withRetry(fn, label) {
|
|
9
|
+
let lastError = null;
|
|
10
|
+
for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) {
|
|
11
|
+
try {
|
|
12
|
+
return await fn();
|
|
13
|
+
}
|
|
14
|
+
catch (err) {
|
|
15
|
+
lastError = err;
|
|
16
|
+
if (attempt < MAX_RETRIES) {
|
|
17
|
+
await new Promise((r) => setTimeout(r, RETRY_DELAY_MS * attempt));
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
throw new Error(`${label} failed after ${MAX_RETRIES} attempts: ${lastError?.message ?? "unknown error"}`, {
|
|
22
|
+
cause: lastError,
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
export async function fetchJson(url) {
|
|
26
|
+
const res = await withRetry(async () => {
|
|
27
|
+
const r = await fetch(url);
|
|
28
|
+
if (!r.ok)
|
|
29
|
+
throw new Error(`HTTP ${r.status} fetching ${url}`);
|
|
30
|
+
return r;
|
|
31
|
+
}, `Fetch ${url}`);
|
|
32
|
+
return res.json();
|
|
33
|
+
}
|
|
34
|
+
export async function downloadFile(url, destPath, label) {
|
|
35
|
+
console.log(`Downloading ${label}... (one-time setup)`);
|
|
36
|
+
mkdirSync(resolve(destPath, ".."), { recursive: true });
|
|
37
|
+
await withRetry(async () => {
|
|
38
|
+
const res = await fetch(url);
|
|
39
|
+
if (!res.ok)
|
|
40
|
+
throw new Error(`HTTP ${res.status} fetching ${url}`);
|
|
41
|
+
if (!res.body)
|
|
42
|
+
throw new Error(`Empty response body from ${url}`);
|
|
43
|
+
const ws = createWriteStream(destPath);
|
|
44
|
+
await pipeline(Readable.fromWeb(res.body), ws);
|
|
45
|
+
}, `Download ${label}`);
|
|
46
|
+
}
|
|
47
|
+
export function extractArchive(archivePath, destDir) {
|
|
48
|
+
mkdirSync(destDir, { recursive: true });
|
|
49
|
+
if (archivePath.endsWith(".tar.xz")) {
|
|
50
|
+
execFileSync("tar", ["-xf", archivePath, "-C", destDir], { stdio: "pipe" });
|
|
51
|
+
}
|
|
52
|
+
else if (process.platform === "win32") {
|
|
53
|
+
execFileSync("powershell", [
|
|
54
|
+
"-Command",
|
|
55
|
+
`Expand-Archive -Force -Path '${archivePath.replace(/'/g, "''")}' -DestinationPath '${destDir.replace(/'/g, "''")}'`,
|
|
56
|
+
], { stdio: "pipe" });
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
execFileSync("unzip", ["-o", "-q", archivePath, "-d", destDir], {
|
|
60
|
+
stdio: "pipe",
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
export async function downloadAndExtract(url, destDir, label) {
|
|
65
|
+
mkdirSync(destDir, { recursive: true });
|
|
66
|
+
const ext = url.endsWith(".tar.xz") ? ".tar.xz" : ".zip";
|
|
67
|
+
const archivePath = resolve(destDir, `_download${ext}`);
|
|
68
|
+
await downloadFile(url, archivePath, label);
|
|
69
|
+
extractArchive(archivePath, destDir);
|
|
70
|
+
unlinkSync(archivePath);
|
|
71
|
+
console.log(`${label} ready.`);
|
|
72
|
+
}
|
|
73
|
+
export function makeExecutable(path) {
|
|
74
|
+
if (process.platform !== "win32") {
|
|
75
|
+
chmodSync(path, 0o755);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=download.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"download.js","sourceRoot":"","sources":["../src/download.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,SAAS,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAC9E,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAEhD,MAAM,WAAW,GAAG,CAAC,CAAC;AACtB,MAAM,cAAc,GAAG,IAAI,CAAC;AAE5B,KAAK,UAAU,SAAS,CAAI,EAAoB,EAAE,KAAa;IAC7D,IAAI,SAAS,GAAiB,IAAI,CAAC;IACnC,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,IAAI,WAAW,EAAE,OAAO,EAAE,EAAE,CAAC;QACxD,IAAI,CAAC;YACH,OAAO,MAAM,EAAE,EAAE,CAAC;QACpB,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,SAAS,GAAG,GAAY,CAAC;YACzB,IAAI,OAAO,GAAG,WAAW,EAAE,CAAC;gBAC1B,MAAM,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,CAAC,CAAC;YACpE,CAAC;QACH,CAAC;IACH,CAAC;IACD,MAAM,IAAI,KAAK,CACb,GAAG,KAAK,iBAAiB,WAAW,cAAc,SAAS,EAAE,OAAO,IAAI,eAAe,EAAE,EACzF;QACE,KAAK,EAAE,SAAS;KACjB,CACF,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,GAAW;IACzC,MAAM,GAAG,GAAG,MAAM,SAAS,CAAC,KAAK,IAAI,EAAE;QACrC,MAAM,CAAC,GAAG,MAAM,KAAK,CAAC,GAAG,CAAC,CAAC;QAC3B,IAAI,CAAC,CAAC,CAAC,EAAE;YAAE,MAAM,IAAI,KAAK,CAAC,QAAQ,CAAC,CAAC,MAAM,aAAa,GAAG,EAAE,CAAC,CAAC;QAC/D,OAAO,CAAC,CAAC;IACX,CAAC,EAAE,SAAS,GAAG,EAAE,CAAC,CAAC;IACnB,OAAO,GAAG,CAAC,IAAI,EAAE,CAAC;AACpB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,GAAW,EACX,QAAgB,EAChB,KAAa;IAEb,OAAO,CAAC,GAAG,CAAC,eAAe,KAAK,sBAAsB,CAAC,CAAC;IAExD,SAAS,CAAC,OAAO,CAAC,QAAQ,EAAE,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAExD,MAAM,SAAS,CAAC,KAAK,IAAI,EAAE;QACzB,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,CAAC,CAAC;QAC7B,IAAI,CAAC,GAAG,CAAC,EAAE;YAAE,MAAM,IAAI,KAAK,CAAC,QAAQ,GAAG,CAAC,MAAM,aAAa,GAAG,EAAE,CAAC,CAAC;QACnE,IAAI,CAAC,GAAG,CAAC,IAAI;YAAE,MAAM,IAAI,KAAK,CAAC,4BAA4B,GAAG,EAAE,CAAC,CAAC;QAElE,MAAM,EAAE,GAAG,iBAAiB,CAAC,QAAQ,CAAC,CAAC;QACvC,MAAM,QAAQ,CACZ,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,IAAgD,CAAC,EACtE,EAAE,CACH,CAAC;IACJ,CAAC,EAAE,YAAY,KAAK,EAAE,CAAC,CAAC;AAC1B,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,WAAmB,EAAE,OAAe;IACjE,SAAS,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAExC,IAAI,WAAW,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC;QACpC,YAAY,CAAC,KAAK,EAAE,CAAC,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE,OAAO,CAAC,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC;IAC9E,CAAC;SAAM,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO,EAAE,CAAC;QACxC,YAAY,CACV,YAAY,EACZ;YACE,UAAU;YACV,gCAAgC,WAAW,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,uBAAuB,OAAO,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG;SACrH,EACD,EAAE,KAAK,EAAE,MAAM,EAAE,CAClB,CAAC;IACJ,CAAC;SAAM,CAAC;QACN,YAAY,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,OAAO,CAAC,EAAE;YAC9D,KAAK,EAAE,MAAM;SACd,CAAC,CAAC;IACL,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,GAAW,EACX,OAAe,EACf,KAAa;IAEb,SAAS,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACxC,MAAM,GAAG,GAAG,GAAG,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC;IACzD,MAAM,WAAW,GAAG,OAAO,CAAC,OAAO,EAAE,YAAY,GAAG,EAAE,CAAC,CAAC;IAExD,MAAM,YAAY,CAAC,GAAG,EAAE,WAAW,EAAE,KAAK,CAAC,CAAC;IAC5C,cAAc,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;IACrC,UAAU,CAAC,WAAW,CAAC,CAAC;IACxB,OAAO,CAAC,GAAG,CAAC,GAAG,KAAK,SAAS,CAAC,CAAC;AACjC,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,IAAY;IACzC,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO,EAAE,CAAC;QACjC,SAAS,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IACzB,CAAC;AACH,CAAC"}
|
package/dist/ffmpeg.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export declare function btbnAssetName(): string | null;
|
|
2
|
+
export declare function binaryName(): string;
|
|
3
|
+
export declare function findBinaryInDir(dir: string, name: string): string | null;
|
|
4
|
+
export declare function ensureFfmpeg(): Promise<string>;
|
|
5
|
+
//# sourceMappingURL=ffmpeg.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ffmpeg.d.ts","sourceRoot":"","sources":["../src/ffmpeg.ts"],"names":[],"mappings":"AAgBA,wBAAgB,aAAa,IAAI,MAAM,GAAG,IAAI,CAO7C;AAMD,wBAAgB,UAAU,IAAI,MAAM,CAEnC;AAWD,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAkBxE;AAqCD,wBAAsB,YAAY,IAAI,OAAO,CAAC,MAAM,CAAC,CAoBpD"}
|
package/dist/ffmpeg.js
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { existsSync, readdirSync, unlinkSync } from "node:fs";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import { resolve } from "node:path";
|
|
4
|
+
import { execSync } from "node:child_process";
|
|
5
|
+
import { fetchJson, downloadAndExtract, downloadFile, extractArchive, makeExecutable, } from "./download.js";
|
|
6
|
+
// BtbN/FFmpeg-Builds: linked from ffmpeg.org, built via GitHub Actions.
|
|
7
|
+
// Covers Linux (x64, arm64) and Windows (x64).
|
|
8
|
+
const BTBN_BASE = "https://github.com/BtbN/FFmpeg-Builds/releases/download/latest";
|
|
9
|
+
export function btbnAssetName() {
|
|
10
|
+
const { platform, arch } = process;
|
|
11
|
+
if (platform === "linux" && arch === "arm64")
|
|
12
|
+
return "ffmpeg-n7.1-latest-linuxarm64-gpl-7.1.tar.xz";
|
|
13
|
+
if (platform === "linux")
|
|
14
|
+
return "ffmpeg-n7.1-latest-linux64-gpl-7.1.tar.xz";
|
|
15
|
+
if (platform === "win32")
|
|
16
|
+
return "ffmpeg-n7.1-latest-win64-gpl-7.1.zip";
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
// evermeet.cx: linked from ffmpeg.org, macOS x64 static builds.
|
|
20
|
+
// Runs on ARM64 Macs via Rosetta 2.
|
|
21
|
+
const EVERMEET_API = "https://evermeet.cx/ffmpeg/info/ffmpeg/release";
|
|
22
|
+
export function binaryName() {
|
|
23
|
+
return process.platform === "win32" ? "ffmpeg.exe" : "ffmpeg";
|
|
24
|
+
}
|
|
25
|
+
function systemFfmpeg() {
|
|
26
|
+
try {
|
|
27
|
+
execSync("ffmpeg -version", { stdio: "pipe" });
|
|
28
|
+
return "ffmpeg";
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
export function findBinaryInDir(dir, name) {
|
|
35
|
+
if (!existsSync(dir))
|
|
36
|
+
return null;
|
|
37
|
+
const direct = resolve(dir, name);
|
|
38
|
+
if (existsSync(direct))
|
|
39
|
+
return direct;
|
|
40
|
+
try {
|
|
41
|
+
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
42
|
+
if (entry.isDirectory()) {
|
|
43
|
+
const binPath = resolve(dir, entry.name, "bin", name);
|
|
44
|
+
if (existsSync(binPath))
|
|
45
|
+
return binPath;
|
|
46
|
+
const flat = resolve(dir, entry.name, name);
|
|
47
|
+
if (existsSync(flat))
|
|
48
|
+
return flat;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
catch (err) {
|
|
53
|
+
console.warn(`Failed to scan directory ${dir} for ${name}:`, err);
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
async function downloadBtbn(cacheDir) {
|
|
59
|
+
const asset = btbnAssetName();
|
|
60
|
+
if (!asset)
|
|
61
|
+
throw new Error("No BtbN build for this platform");
|
|
62
|
+
const url = `${BTBN_BASE}/${asset}`;
|
|
63
|
+
await downloadAndExtract(url, cacheDir, "ffmpeg");
|
|
64
|
+
const bin = binaryName();
|
|
65
|
+
const found = findBinaryInDir(cacheDir, bin);
|
|
66
|
+
if (found) {
|
|
67
|
+
makeExecutable(found);
|
|
68
|
+
return found;
|
|
69
|
+
}
|
|
70
|
+
throw new Error("Downloaded ffmpeg but could not locate binary");
|
|
71
|
+
}
|
|
72
|
+
async function downloadEvermeet(cacheDir) {
|
|
73
|
+
const info = (await fetchJson(EVERMEET_API));
|
|
74
|
+
const url = info.download.zip.url;
|
|
75
|
+
const archivePath = resolve(cacheDir, "_download.zip");
|
|
76
|
+
await downloadFile(url, archivePath, "ffmpeg");
|
|
77
|
+
extractArchive(archivePath, cacheDir);
|
|
78
|
+
unlinkSync(archivePath);
|
|
79
|
+
const bin = resolve(cacheDir, "ffmpeg");
|
|
80
|
+
if (existsSync(bin)) {
|
|
81
|
+
makeExecutable(bin);
|
|
82
|
+
return bin;
|
|
83
|
+
}
|
|
84
|
+
throw new Error("Downloaded ffmpeg but could not locate binary");
|
|
85
|
+
}
|
|
86
|
+
export async function ensureFfmpeg() {
|
|
87
|
+
if (process.env.FFMPEG_PATH)
|
|
88
|
+
return process.env.FFMPEG_PATH;
|
|
89
|
+
const cacheDir = resolve(homedir(), ".webreel", "bin", "ffmpeg");
|
|
90
|
+
const bin = findBinaryInDir(cacheDir, binaryName());
|
|
91
|
+
if (bin)
|
|
92
|
+
return bin;
|
|
93
|
+
try {
|
|
94
|
+
if (process.platform === "darwin") {
|
|
95
|
+
return await downloadEvermeet(cacheDir);
|
|
96
|
+
}
|
|
97
|
+
return await downloadBtbn(cacheDir);
|
|
98
|
+
}
|
|
99
|
+
catch (err) {
|
|
100
|
+
const sys = systemFfmpeg();
|
|
101
|
+
if (sys)
|
|
102
|
+
return sys;
|
|
103
|
+
throw new Error(`Failed to download ffmpeg and no system ffmpeg found: ${err instanceof Error ? err.message : String(err)}`, { cause: err });
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
//# sourceMappingURL=ffmpeg.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ffmpeg.js","sourceRoot":"","sources":["../src/ffmpeg.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export type { CDPClient, BoundingBox, Point, SoundEvent } from "./types.js";
|
|
2
|
+
export { TARGET_FPS, FRAME_MS, DEFAULT_VIEWPORT_SIZE, OFFSCREEN_MARGIN, CAPTURE_CYCLE_MS, DEFAULT_CURSOR_SVG, DEFAULT_CURSOR_SIZE, DEFAULT_HUD_THEME, } from "./types.js";
|
|
3
|
+
export { connectCDP } from "./cdp.js";
|
|
4
|
+
export { launchChrome, type ChromeInstance, type LaunchChromeOptions } from "./chrome.js";
|
|
5
|
+
export { injectOverlays, showKeys, hideKeys, type OverlayTheme } from "./overlays.js";
|
|
6
|
+
export { RecordingContext, modKey, pause, navigate, waitForSelector, waitForText, findElementByText, findElementBySelector, moveCursorTo, clickAt, pressKey, typeText, dragFromTo, captureScreenshot, } from "./actions.js";
|
|
7
|
+
export { Recorder } from "./recorder.js";
|
|
8
|
+
export { InteractionTimeline, type TimelineData } from "./timeline.js";
|
|
9
|
+
export { compose, type ComposeOptions } from "./compositor.js";
|
|
10
|
+
export { ensureFfmpeg } from "./ffmpeg.js";
|
|
11
|
+
export { extractThumbnail, type SfxConfig } from "./media.js";
|
|
12
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,SAAS,EAAE,WAAW,EAAE,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAC5E,OAAO,EACL,UAAU,EACV,QAAQ,EACR,qBAAqB,EACrB,gBAAgB,EAChB,gBAAgB,EAChB,kBAAkB,EAClB,mBAAmB,EACnB,iBAAiB,GAClB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AACtC,OAAO,EAAE,YAAY,EAAE,KAAK,cAAc,EAAE,KAAK,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAC1F,OAAO,EAAE,cAAc,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAK,YAAY,EAAE,MAAM,eAAe,CAAC;AACtF,OAAO,EACL,gBAAgB,EAChB,MAAM,EACN,KAAK,EACL,QAAQ,EACR,eAAe,EACf,WAAW,EACX,iBAAiB,EACjB,qBAAqB,EACrB,YAAY,EACZ,OAAO,EACP,QAAQ,EACR,QAAQ,EACR,UAAU,EACV,iBAAiB,GAClB,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,mBAAmB,EAAE,KAAK,YAAY,EAAE,MAAM,eAAe,CAAC;AACvE,OAAO,EAAE,OAAO,EAAE,KAAK,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAC/D,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,gBAAgB,EAAE,KAAK,SAAS,EAAE,MAAM,YAAY,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export { TARGET_FPS, FRAME_MS, DEFAULT_VIEWPORT_SIZE, OFFSCREEN_MARGIN, CAPTURE_CYCLE_MS, DEFAULT_CURSOR_SVG, DEFAULT_CURSOR_SIZE, DEFAULT_HUD_THEME, } from "./types.js";
|
|
2
|
+
export { connectCDP } from "./cdp.js";
|
|
3
|
+
export { launchChrome } from "./chrome.js";
|
|
4
|
+
export { injectOverlays, showKeys, hideKeys } from "./overlays.js";
|
|
5
|
+
export { RecordingContext, modKey, pause, navigate, waitForSelector, waitForText, findElementByText, findElementBySelector, moveCursorTo, clickAt, pressKey, typeText, dragFromTo, captureScreenshot, } from "./actions.js";
|
|
6
|
+
export { Recorder } from "./recorder.js";
|
|
7
|
+
export { InteractionTimeline } from "./timeline.js";
|
|
8
|
+
export { compose } from "./compositor.js";
|
|
9
|
+
export { ensureFfmpeg } from "./ffmpeg.js";
|
|
10
|
+
export { extractThumbnail } from "./media.js";
|
|
11
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EACL,UAAU,EACV,QAAQ,EACR,qBAAqB,EACrB,gBAAgB,EAChB,gBAAgB,EAChB,kBAAkB,EAClB,mBAAmB,EACnB,iBAAiB,GAClB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AACtC,OAAO,EAAE,YAAY,EAAiD,MAAM,aAAa,CAAC;AAC1F,OAAO,EAAE,cAAc,EAAE,QAAQ,EAAE,QAAQ,EAAqB,MAAM,eAAe,CAAC;AACtF,OAAO,EACL,gBAAgB,EAChB,MAAM,EACN,KAAK,EACL,QAAQ,EACR,eAAe,EACf,WAAW,EACX,iBAAiB,EACjB,qBAAqB,EACrB,YAAY,EACZ,OAAO,EACP,QAAQ,EACR,QAAQ,EACR,UAAU,EACV,iBAAiB,GAClB,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,mBAAmB,EAAqB,MAAM,eAAe,CAAC;AACvE,OAAO,EAAE,OAAO,EAAuB,MAAM,iBAAiB,CAAC;AAC/D,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,gBAAgB,EAAkB,MAAM,YAAY,CAAC"}
|
package/dist/media.d.ts
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { SoundEvent } from "./types.js";
|
|
2
|
+
export interface SfxConfig {
|
|
3
|
+
click?: 1 | 2 | 3 | 4 | string;
|
|
4
|
+
key?: 1 | 2 | 3 | 4 | string;
|
|
5
|
+
}
|
|
6
|
+
export declare function resolveSfxPath(value: 1 | 2 | 3 | 4 | string | undefined, prefix: "click" | "key"): string;
|
|
7
|
+
export declare function ensureSoundAssets(sfx?: SfxConfig): {
|
|
8
|
+
clickPath: string;
|
|
9
|
+
keyPath: string;
|
|
10
|
+
};
|
|
11
|
+
export declare function buildAudioMixArgs(videoInput: string, events: SoundEvent[], durationSec: number, sfx?: SfxConfig): {
|
|
12
|
+
inputArgs: string[];
|
|
13
|
+
filterComplex: string;
|
|
14
|
+
};
|
|
15
|
+
export interface FinalizeMp4Options {
|
|
16
|
+
remux?: boolean;
|
|
17
|
+
sfx?: SfxConfig;
|
|
18
|
+
}
|
|
19
|
+
export declare function finalizeMp4(ffmpegPath: string, tempVideo: string, outputPath: string, events: SoundEvent[], durationSec: number, options?: FinalizeMp4Options): void;
|
|
20
|
+
export declare function finalizeWebm(ffmpegPath: string, tempVideo: string, outputPath: string, events: SoundEvent[], durationSec: number, sfx?: SfxConfig): void;
|
|
21
|
+
export declare function extractThumbnail(ffmpegPath: string, videoPath: string, outputPath: string, timeSec: number): void;
|
|
22
|
+
export declare function finalizeGif(ffmpegPath: string, tempVideo: string, outputPath: string, width: number): void;
|
|
23
|
+
//# sourceMappingURL=media.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"media.d.ts","sourceRoot":"","sources":["../src/media.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAmB7C,MAAM,WAAW,SAAS;IACxB,KAAK,CAAC,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC;IAC/B,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC;CAC9B;AAED,wBAAgB,cAAc,CAC5B,KAAK,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,MAAM,GAAG,SAAS,EACzC,MAAM,EAAE,OAAO,GAAG,KAAK,GACtB,MAAM,CAIR;AAED,wBAAgB,iBAAiB,CAAC,GAAG,CAAC,EAAE,SAAS,GAAG;IAClD,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;CACjB,CAKA;AAED,wBAAgB,iBAAiB,CAC/B,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,UAAU,EAAE,EACpB,WAAW,EAAE,MAAM,EACnB,GAAG,CAAC,EAAE,SAAS,GACd;IAAE,SAAS,EAAE,MAAM,EAAE,CAAC;IAAC,aAAa,EAAE,MAAM,CAAA;CAAE,CAgChD;AAED,MAAM,WAAW,kBAAkB;IACjC,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,GAAG,CAAC,EAAE,SAAS,CAAC;CACjB;AAED,wBAAgB,WAAW,CACzB,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,UAAU,EAAE,EACpB,WAAW,EAAE,MAAM,EACnB,OAAO,CAAC,EAAE,kBAAkB,GAC3B,IAAI,CAmCN;AAED,wBAAgB,YAAY,CAC1B,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,UAAU,EAAE,EACpB,WAAW,EAAE,MAAM,EACnB,GAAG,CAAC,EAAE,SAAS,GACd,IAAI,CAoDN;AAED,wBAAgB,gBAAgB,CAC9B,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,MAAM,GACd,IAAI,CAWN;AAED,wBAAgB,WAAW,CACzB,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,EAClB,KAAK,EAAE,MAAM,GACZ,IAAI,CASN"}
|
package/dist/media.js
ADDED
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import { spawnSync } from "node:child_process";
|
|
2
|
+
import { rmSync, renameSync } from "node:fs";
|
|
3
|
+
import { resolve, dirname } from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
6
|
+
function runFfmpeg(ffmpegPath, args) {
|
|
7
|
+
const result = spawnSync(ffmpegPath, args, {
|
|
8
|
+
stdio: "pipe",
|
|
9
|
+
maxBuffer: 50 * 1024 * 1024,
|
|
10
|
+
});
|
|
11
|
+
if (result.status !== 0) {
|
|
12
|
+
const stderr = result.stderr?.toString().slice(-2000) ?? "";
|
|
13
|
+
throw new Error(`ffmpeg exited with code ${result.status}${stderr ? `:\n${stderr}` : ""}`);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
const ASSETS_DIR = resolve(__dirname, "..", "assets");
|
|
17
|
+
export function resolveSfxPath(value, prefix) {
|
|
18
|
+
if (value === undefined)
|
|
19
|
+
return resolve(ASSETS_DIR, `${prefix}-1.mp3`);
|
|
20
|
+
if (typeof value === "string")
|
|
21
|
+
return value;
|
|
22
|
+
return resolve(ASSETS_DIR, `${prefix}-${value}.mp3`);
|
|
23
|
+
}
|
|
24
|
+
export function ensureSoundAssets(sfx) {
|
|
25
|
+
return {
|
|
26
|
+
clickPath: resolveSfxPath(sfx?.click, "click"),
|
|
27
|
+
keyPath: resolveSfxPath(sfx?.key, "key"),
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
export function buildAudioMixArgs(videoInput, events, durationSec, sfx) {
|
|
31
|
+
const { clickPath, keyPath } = ensureSoundAssets(sfx);
|
|
32
|
+
const inputArgs = [
|
|
33
|
+
"-i",
|
|
34
|
+
videoInput,
|
|
35
|
+
"-f",
|
|
36
|
+
"lavfi",
|
|
37
|
+
"-i",
|
|
38
|
+
`anullsrc=r=44100:cl=mono`,
|
|
39
|
+
"-t",
|
|
40
|
+
durationSec.toFixed(3),
|
|
41
|
+
];
|
|
42
|
+
const filterParts = [];
|
|
43
|
+
const durationMs = Math.round(durationSec * 1000);
|
|
44
|
+
for (let i = 0; i < events.length; i++) {
|
|
45
|
+
const ev = events[i];
|
|
46
|
+
const soundFile = ev.type === "click" ? clickPath : keyPath;
|
|
47
|
+
const delayMs = Math.min(ev.timeMs, durationMs);
|
|
48
|
+
inputArgs.push("-i", soundFile);
|
|
49
|
+
const baseVol = ev.type === "click" ? 0.25 : 0.15;
|
|
50
|
+
const vol = baseVol + Math.random() * baseVol * 0.6;
|
|
51
|
+
const rate = 44100 * (0.93 + Math.random() * 0.14);
|
|
52
|
+
filterParts.push(`[${i + 2}]asetrate=${Math.round(rate)},aresample=44100,adelay=${delayMs}|${delayMs},volume=${vol.toFixed(3)}[s${i}]`);
|
|
53
|
+
}
|
|
54
|
+
const mixInputs = "[1]" + events.map((_, i) => `[s${i}]`).join("");
|
|
55
|
+
filterParts.push(`${mixInputs}amix=inputs=${events.length + 1}:normalize=0[aout]`);
|
|
56
|
+
return { inputArgs, filterComplex: filterParts.join(";") };
|
|
57
|
+
}
|
|
58
|
+
export function finalizeMp4(ffmpegPath, tempVideo, outputPath, events, durationSec, options) {
|
|
59
|
+
if (events.length === 0 || !options?.sfx) {
|
|
60
|
+
if (options?.remux) {
|
|
61
|
+
runFfmpeg(ffmpegPath, ["-y", "-i", tempVideo, "-c", "copy", outputPath]);
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
renameSync(tempVideo, outputPath);
|
|
65
|
+
}
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
const { inputArgs, filterComplex } = buildAudioMixArgs(tempVideo, events, durationSec, options.sfx);
|
|
69
|
+
runFfmpeg(ffmpegPath, [
|
|
70
|
+
"-y",
|
|
71
|
+
...inputArgs,
|
|
72
|
+
"-filter_complex",
|
|
73
|
+
filterComplex,
|
|
74
|
+
"-map",
|
|
75
|
+
"0:v",
|
|
76
|
+
"-map",
|
|
77
|
+
"[aout]",
|
|
78
|
+
"-c:v",
|
|
79
|
+
"copy",
|
|
80
|
+
"-c:a",
|
|
81
|
+
"aac",
|
|
82
|
+
"-b:a",
|
|
83
|
+
"128k",
|
|
84
|
+
"-shortest",
|
|
85
|
+
outputPath,
|
|
86
|
+
]);
|
|
87
|
+
}
|
|
88
|
+
export function finalizeWebm(ffmpegPath, tempVideo, outputPath, events, durationSec, sfx) {
|
|
89
|
+
const silentWebm = tempVideo + "_silent.webm";
|
|
90
|
+
runFfmpeg(ffmpegPath, [
|
|
91
|
+
"-y",
|
|
92
|
+
"-i",
|
|
93
|
+
tempVideo,
|
|
94
|
+
"-c:v",
|
|
95
|
+
"libvpx-vp9",
|
|
96
|
+
"-crf",
|
|
97
|
+
"30",
|
|
98
|
+
"-b:v",
|
|
99
|
+
"0",
|
|
100
|
+
"-pix_fmt",
|
|
101
|
+
"yuv420p",
|
|
102
|
+
silentWebm,
|
|
103
|
+
]);
|
|
104
|
+
if (events.length === 0 || !sfx) {
|
|
105
|
+
renameSync(silentWebm, outputPath);
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
try {
|
|
109
|
+
const { inputArgs, filterComplex } = buildAudioMixArgs(silentWebm, events, durationSec, sfx);
|
|
110
|
+
runFfmpeg(ffmpegPath, [
|
|
111
|
+
"-y",
|
|
112
|
+
...inputArgs,
|
|
113
|
+
"-filter_complex",
|
|
114
|
+
filterComplex,
|
|
115
|
+
"-map",
|
|
116
|
+
"0:v",
|
|
117
|
+
"-map",
|
|
118
|
+
"[aout]",
|
|
119
|
+
"-c:v",
|
|
120
|
+
"copy",
|
|
121
|
+
"-c:a",
|
|
122
|
+
"libopus",
|
|
123
|
+
"-b:a",
|
|
124
|
+
"128k",
|
|
125
|
+
"-shortest",
|
|
126
|
+
outputPath,
|
|
127
|
+
]);
|
|
128
|
+
}
|
|
129
|
+
finally {
|
|
130
|
+
rmSync(silentWebm, { force: true });
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
export function extractThumbnail(ffmpegPath, videoPath, outputPath, timeSec) {
|
|
134
|
+
runFfmpeg(ffmpegPath, [
|
|
135
|
+
"-y",
|
|
136
|
+
"-ss",
|
|
137
|
+
String(timeSec),
|
|
138
|
+
"-i",
|
|
139
|
+
videoPath,
|
|
140
|
+
"-frames:v",
|
|
141
|
+
"1",
|
|
142
|
+
outputPath,
|
|
143
|
+
]);
|
|
144
|
+
}
|
|
145
|
+
export function finalizeGif(ffmpegPath, tempVideo, outputPath, width) {
|
|
146
|
+
runFfmpeg(ffmpegPath, [
|
|
147
|
+
"-y",
|
|
148
|
+
"-i",
|
|
149
|
+
tempVideo,
|
|
150
|
+
"-vf",
|
|
151
|
+
`fps=15,scale=${width}:-1:flags=lanczos,split[s0][s1];[s0]palettegen[p];[s1][p]paletteuse`,
|
|
152
|
+
outputPath,
|
|
153
|
+
]);
|
|
154
|
+
}
|
|
155
|
+
//# sourceMappingURL=media.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"media.js","sourceRoot":"","sources":["../src/media.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { CDPClient } from "./types.js";
|
|
2
|
+
export interface OverlayTheme {
|
|
3
|
+
cursorSvg?: string;
|
|
4
|
+
cursorSize?: number;
|
|
5
|
+
cursorHotspot?: "top-left" | "center";
|
|
6
|
+
hud?: {
|
|
7
|
+
background?: string;
|
|
8
|
+
color?: string;
|
|
9
|
+
fontSize?: number;
|
|
10
|
+
fontFamily?: string;
|
|
11
|
+
borderRadius?: number;
|
|
12
|
+
position?: "top" | "bottom";
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
export declare function injectOverlays(client: CDPClient, theme?: OverlayTheme, initialPosition?: {
|
|
16
|
+
x: number;
|
|
17
|
+
y: number;
|
|
18
|
+
}): Promise<void>;
|
|
19
|
+
export declare function showKeys(client: CDPClient, labels: string[]): Promise<void>;
|
|
20
|
+
export declare function hideKeys(client: CDPClient): Promise<void>;
|
|
21
|
+
//# sourceMappingURL=overlays.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"overlays.d.ts","sourceRoot":"","sources":["../src/overlays.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAQ5C,MAAM,WAAW,YAAY;IAC3B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,UAAU,GAAG,QAAQ,CAAC;IACtC,GAAG,CAAC,EAAE;QACJ,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,QAAQ,CAAC,EAAE,KAAK,GAAG,QAAQ,CAAC;KAC7B,CAAC;CACH;AAED,wBAAsB,cAAc,CAClC,MAAM,EAAE,SAAS,EACjB,KAAK,CAAC,EAAE,YAAY,EACpB,eAAe,CAAC,EAAE;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,GACzC,OAAO,CAAC,IAAI,CAAC,CAwEf;AAED,wBAAsB,QAAQ,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAcjF;AAED,wBAAsB,QAAQ,CAAC,MAAM,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAO/D"}
|