rechrome 1.48.0 → 1.48.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rechrome",
3
- "version": "1.48.0",
3
+ "version": "1.48.1",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/snomiao/rechrome.git"
package/record.js CHANGED
@@ -34,6 +34,12 @@ function recordHoldMs(args) {
34
34
  return 0;
35
35
  }
36
36
  }
37
+ function withFormatExt(name, format) {
38
+ const m = /^(.+)\.(mp4|webm|mjpeg|mjpg|mkv|mov|avi)$/i.exec(name);
39
+ if (m && m[2].toLowerCase() === format)
40
+ return name;
41
+ return `${m ? m[1] : name}.${format}`;
42
+ }
37
43
  function freeName(name, taken) {
38
44
  if (!taken(name))
39
45
  return name;
@@ -277,5 +283,6 @@ export {
277
283
  recordHoldMs,
278
284
  recordSink,
279
285
  resolveFfmpeg,
280
- resolveFormat
286
+ resolveFormat,
287
+ withFormatExt
281
288
  };
package/record.ts CHANGED
@@ -59,6 +59,13 @@ export function recordHoldMs(args: string[]): number {
59
59
  } catch { return 0; }
60
60
  }
61
61
 
62
+ /** `name` with the extension of what was actually written: `clip.mp4` saved as mjpeg is `clip.mjpeg`. */
63
+ export function withFormatExt(name: string, format: RecordFormat): string {
64
+ const m = /^(.+)\.(mp4|webm|mjpeg|mjpg|mkv|mov|avi)$/i.exec(name);
65
+ if (m && m[2]!.toLowerCase() === format) return name;
66
+ return `${m ? m[1] : name}.${format}`;
67
+ }
68
+
62
69
  /** `name`, or `name-2`, `name-3`… before the extension: the first that is not taken. */
63
70
  export function freeName(name: string, taken: (name: string) => boolean): string {
64
71
  if (!taken(name)) return name;
package/serve.js CHANGED
@@ -25,7 +25,7 @@ import {
25
25
  chromeDataAccessHint,
26
26
  } from "./rechrome.js";
27
27
  import { SecretMasker, runCodeOnSession, parseReadCookieWire, readCookieOnSession, parseReadStorageWire, readStorageOnSession, parseReadSecretWire, readSecretOnSession, parseReadHeadersWire, readHeadersOnSession, readHeadersEvidence, parseFillSecretWire, fillSecretOnSession, passwordValuesOnSession, hasSnapshot, maskPasswordLines, withholdSnapshots } from "./fill-secret.js";
28
- import { parseRecordArgs, buildCaptureChunkCode, resolveFfmpeg, resolveFormat, recordSink, Recording, freeName, RECORD_CHUNK_MS, RECORD_MAX_SECONDS, type CaptureChunk, type RecordingSummary } from "./record.js";
28
+ import { parseRecordArgs, buildCaptureChunkCode, resolveFfmpeg, resolveFormat, recordSink, Recording, freeName, withFormatExt, RECORD_CHUNK_MS, RECORD_MAX_SECONDS, type CaptureChunk, type RecordingSummary } from "./record.js";
29
29
 
30
30
  const TAILSCALE_BIN = process.env.TAILSCALE_BIN || "/Applications/Tailscale.app/Contents/MacOS/Tailscale";
31
31
  const CERT_RENEW_THRESHOLD_DAYS = 7;
@@ -123,7 +123,9 @@ function deliverRecording(entry: RecordingEntry, s: RecordingSummary, filename:
123
123
  }
124
124
  if (s.encodeError) { try { unlinkSync(s.out); } catch {} throw new Error(s.encodeError); }
125
125
  // Never over an existing file: `--filename clip.mp4` twice keeps both (clip.mp4, clip-2.mp4).
126
- const out = filename ? join(entry.dir, freeName(filename, n => existsSync(join(entry.dir, n)))) : s.out.replace(/\/\.(recording-[^/]+)\.part$/, "/$1");
126
+ // The name says what the file is: no ffmpeg on the host means mjpeg, whatever --filename asked for.
127
+ const wanted = filename ? withFormatExt(filename, s.format) : undefined;
128
+ const out = wanted ? join(entry.dir, freeName(wanted, n => existsSync(join(entry.dir, n)))) : s.out.replace(/\/\.(recording-[^/]+)\.part$/, "/$1");
127
129
  if (out !== s.out) renameSync(s.out, out);
128
130
  log(`record: ${JSON.stringify({ listener: entry.listener, name: relative(entry.dir, out), frames: s.frames, captured: s.captured, fps: s.fps, format: s.format, size: statSync(out).size, kept: entry.keep, ...(s.error ? { ended: s.error.split("\n")[0] } : {}) })}`);
129
131
  if (!entry.keep) {
@@ -131,7 +133,8 @@ function deliverRecording(entry: RecordingEntry, s: RecordingSummary, filename:
131
133
  clearTimeout(pendingDownloads.get(real));
132
134
  pendingDownloads.set(real, setTimeout(() => { pendingDownloads.delete(real); try { unlinkSync(real); } catch {} }, DOWNLOAD_PICKUP_MS));
133
135
  }
134
- const note = s.error ? ` — capture ended early: ${s.error.split("\n")[0]}` : "";
136
+ const note = (filename && wanted !== filename ? ` — saved as ${s.format}, not ${filename}${s.format === "mjpeg" ? " (no ffmpeg on the host)" : ""}` : "")
137
+ + (s.error ? ` — capture ended early: ${s.error.split("\n")[0]}` : "");
135
138
  return { stdout: `record: saved ${relative(entry.dir, out)} (${s.format}, ${s.seconds.toFixed(1)} s at ${s.fps} fps, ${s.captured} screenshots)${note}\n`, files: [(entry.outputPrefix + relative(entry.dir, out)).replaceAll("\\", "/")] };
136
139
  }
137
140
  // playwright-cli's line for a finished download (tools/backend/response.js).
package/serve.ts CHANGED
@@ -25,7 +25,7 @@ import {
25
25
  chromeDataAccessHint,
26
26
  } from "./rechrome.ts";
27
27
  import { SecretMasker, runCodeOnSession, parseReadCookieWire, readCookieOnSession, parseReadStorageWire, readStorageOnSession, parseReadSecretWire, readSecretOnSession, parseReadHeadersWire, readHeadersOnSession, readHeadersEvidence, parseFillSecretWire, fillSecretOnSession, passwordValuesOnSession, hasSnapshot, maskPasswordLines, withholdSnapshots } from "./fill-secret.ts";
28
- import { parseRecordArgs, buildCaptureChunkCode, resolveFfmpeg, resolveFormat, recordSink, Recording, freeName, RECORD_CHUNK_MS, RECORD_MAX_SECONDS, type CaptureChunk, type RecordingSummary } from "./record.ts";
28
+ import { parseRecordArgs, buildCaptureChunkCode, resolveFfmpeg, resolveFormat, recordSink, Recording, freeName, withFormatExt, RECORD_CHUNK_MS, RECORD_MAX_SECONDS, type CaptureChunk, type RecordingSummary } from "./record.ts";
29
29
 
30
30
  const TAILSCALE_BIN = process.env.TAILSCALE_BIN || "/Applications/Tailscale.app/Contents/MacOS/Tailscale";
31
31
  const CERT_RENEW_THRESHOLD_DAYS = 7;
@@ -123,7 +123,9 @@ function deliverRecording(entry: RecordingEntry, s: RecordingSummary, filename:
123
123
  }
124
124
  if (s.encodeError) { try { unlinkSync(s.out); } catch {} throw new Error(s.encodeError); }
125
125
  // Never over an existing file: `--filename clip.mp4` twice keeps both (clip.mp4, clip-2.mp4).
126
- const out = filename ? join(entry.dir, freeName(filename, n => existsSync(join(entry.dir, n)))) : s.out.replace(/\/\.(recording-[^/]+)\.part$/, "/$1");
126
+ // The name says what the file is: no ffmpeg on the host means mjpeg, whatever --filename asked for.
127
+ const wanted = filename ? withFormatExt(filename, s.format) : undefined;
128
+ const out = wanted ? join(entry.dir, freeName(wanted, n => existsSync(join(entry.dir, n)))) : s.out.replace(/\/\.(recording-[^/]+)\.part$/, "/$1");
127
129
  if (out !== s.out) renameSync(s.out, out);
128
130
  log(`record: ${JSON.stringify({ listener: entry.listener, name: relative(entry.dir, out), frames: s.frames, captured: s.captured, fps: s.fps, format: s.format, size: statSync(out).size, kept: entry.keep, ...(s.error ? { ended: s.error.split("\n")[0] } : {}) })}`);
129
131
  if (!entry.keep) {
@@ -131,7 +133,8 @@ function deliverRecording(entry: RecordingEntry, s: RecordingSummary, filename:
131
133
  clearTimeout(pendingDownloads.get(real));
132
134
  pendingDownloads.set(real, setTimeout(() => { pendingDownloads.delete(real); try { unlinkSync(real); } catch {} }, DOWNLOAD_PICKUP_MS));
133
135
  }
134
- const note = s.error ? ` — capture ended early: ${s.error.split("\n")[0]}` : "";
136
+ const note = (filename && wanted !== filename ? ` — saved as ${s.format}, not ${filename}${s.format === "mjpeg" ? " (no ffmpeg on the host)" : ""}` : "")
137
+ + (s.error ? ` — capture ended early: ${s.error.split("\n")[0]}` : "");
135
138
  return { stdout: `record: saved ${relative(entry.dir, out)} (${s.format}, ${s.seconds.toFixed(1)} s at ${s.fps} fps, ${s.captured} screenshots)${note}\n`, files: [(entry.outputPrefix + relative(entry.dir, out)).replaceAll("\\", "/")] };
136
139
  }
137
140
  // playwright-cli's line for a finished download (tools/backend/response.ts).