bytecut 0.2.0 → 0.2.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/README.md CHANGED
@@ -108,6 +108,9 @@ SSIMULACRA2 runs as WebAssembly in worker threads, so no native binary is needed
108
108
 
109
109
  ## Video
110
110
 
111
+ Estimating a large video encodes real samples, so the first scan of a 4K file can take a minute or
112
+ more; the screen shows which sample it is on, and the result is cached by content hash.
113
+
111
114
  bytecut uses **your own** `ffmpeg` and `ffprobe` (`--ffmpeg`, `BYTECUT_FFMPEG`, `ffmpegPath`, or
112
115
  `PATH`). It never downloads or bundles one. Without ffmpeg images work fully and videos are
113
116
  listed as "needs ffmpeg" with the install command for your OS. Without an AV1 encoder you still
@@ -4,7 +4,7 @@ import {
4
4
  } from "./chunk-LTWUPQDT.js";
5
5
  import {
6
6
  audit
7
- } from "./chunk-5JEA233Z.js";
7
+ } from "./chunk-WI4ZO7GP.js";
8
8
  import {
9
9
  formatAuditReport
10
10
  } from "./chunk-HLBNRB3S.js";
@@ -4,7 +4,7 @@ import {
4
4
  } from "./chunk-LTWUPQDT.js";
5
5
  import {
6
6
  audit
7
- } from "./chunk-5JEA233Z.js";
7
+ } from "./chunk-WI4ZO7GP.js";
8
8
  import {
9
9
  formatAuditReport
10
10
  } from "./chunk-HLBNRB3S.js";
@@ -3,7 +3,7 @@
3
3
  // package.json
4
4
  var package_default = {
5
5
  name: "bytecut",
6
- version: "0.2.0",
6
+ version: "0.2.1",
7
7
  description: "Audit and optimize images and videos in web projects.",
8
8
  type: "module",
9
9
  license: "MIT",
@@ -212,6 +212,8 @@ async function estimateVideo(item, deps, sourcePath) {
212
212
  const starts = sampleStarts(item.durationSec);
213
213
  const exact = starts.length === 1 && starts[0].durationSec >= item.durationSec;
214
214
  const perCodec = [];
215
+ const stepTotal = item.outputs.filter((o) => o.role === "video" && encoderFor(o.codec, caps)).length * starts.length;
216
+ let stepsDone = 0;
215
217
  for (const out of item.outputs) {
216
218
  if (out.role !== "video") continue;
217
219
  const encoder = encoderFor(out.codec, caps);
@@ -247,6 +249,7 @@ async function estimateVideo(item, deps, sourcePath) {
247
249
  },
248
250
  deps.signal
249
251
  );
252
+ deps.onStep?.({ done: ++stepsDone, total: stepTotal });
250
253
  seconds += s.durationSec;
251
254
  }
252
255
  streamBytes = Math.round(seconds > 0 ? total / seconds * item.durationSec : total);
@@ -444,7 +447,14 @@ async function audit(options) {
444
447
  {
445
448
  engine: options.videoEngine,
446
449
  cache,
447
- ffmpegVersion: videoVersions.ffmpeg ?? "unknown"
450
+ ffmpegVersion: videoVersions.ffmpeg ?? "unknown",
451
+ onStep: (step) => options.onProgress?.({
452
+ phase: "estimate",
453
+ done: probed - 1,
454
+ total,
455
+ source: item.source,
456
+ detail: `encoding sample ${step.done} of ${step.total}`
457
+ })
448
458
  },
449
459
  absolute
450
460
  );
package/dist/cli.js CHANGED
@@ -8,10 +8,10 @@ import {
8
8
  } from "./chunk-LTWUPQDT.js";
9
9
  import {
10
10
  audit
11
- } from "./chunk-5JEA233Z.js";
11
+ } from "./chunk-WI4ZO7GP.js";
12
12
  import {
13
13
  package_default
14
- } from "./chunk-LJWOPIM6.js";
14
+ } from "./chunk-SKANYRVT.js";
15
15
  import {
16
16
  formatAuditReport
17
17
  } from "./chunk-HLBNRB3S.js";
@@ -57,7 +57,7 @@ async function runDefault() {
57
57
  return;
58
58
  }
59
59
  if (isInteractive()) {
60
- const { runTui } = await import("./tui-ZAEXBY4L.js");
60
+ const { runTui } = await import("./tui-6WSMCHZS.js");
61
61
  await runTui({
62
62
  projectRoot: found.projectRoot,
63
63
  project: found.project,
@@ -125,13 +125,13 @@ var main = defineCommand({
125
125
  await runDefault();
126
126
  }),
127
127
  subCommands: {
128
- audit: () => import("./audit-LJOGSJS6.js").then((m) => m.default),
129
- optimize: () => import("./optimize-SLLQGDDX.js").then((m) => m.default),
128
+ audit: () => import("./audit-EBKL6L65.js").then((m) => m.default),
129
+ optimize: () => import("./optimize-TLJG7UYF.js").then((m) => m.default),
130
130
  snippet: () => import("./snippet-IIPVF6PS.js").then((m) => m.default),
131
131
  compare: () => import("./compare-MPPGTUC5.js").then((m) => m.default),
132
132
  explain: () => import("./explain-LKOB7FRT.js").then((m) => m.default),
133
133
  init: () => import("./init-CWDWVJ55.js").then((m) => m.default),
134
- check: () => import("./check-MEBXJBNG.js").then((m) => m.default),
134
+ check: () => import("./check-XTTU5SCZ.js").then((m) => m.default),
135
135
  doctor: () => import("./doctor-PWCUEBD5.js").then((m) => m.default)
136
136
  }
137
137
  });
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  package_default
4
- } from "./chunk-LJWOPIM6.js";
4
+ } from "./chunk-SKANYRVT.js";
5
5
  import {
6
6
  formatFindings,
7
7
  formatPlan,
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  audit
4
- } from "./chunk-5JEA233Z.js";
4
+ } from "./chunk-WI4ZO7GP.js";
5
5
  import {
6
6
  formatSummary,
7
7
  optimize
@@ -454,7 +454,16 @@ function effectivePreset(state, row) {
454
454
  function reducer(state, action) {
455
455
  switch (action.type) {
456
456
  case "scan-progress":
457
- return { ...state, scan: { phase: action.phase, done: action.done, total: action.total } };
457
+ return {
458
+ ...state,
459
+ scan: {
460
+ phase: action.phase,
461
+ done: action.done,
462
+ total: action.total,
463
+ ...action.source ? { source: action.source } : {},
464
+ ...action.detail ? { detail: action.detail } : {}
465
+ }
466
+ };
458
467
  case "scan-done":
459
468
  return {
460
469
  ...state,
@@ -1579,10 +1588,18 @@ function App(props) {
1579
1588
  let body;
1580
1589
  if (state.view === "scanning") {
1581
1590
  const sc = state.scan;
1582
- body = /* @__PURE__ */ jsxs5(Text5, { children: [
1583
- "Scanning",
1584
- sc && sc.total > 0 ? ` ${Math.min(sc.done, sc.total)}/${sc.total} images (${sc.phase})` : "",
1585
- "\u2026"
1591
+ body = /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", children: [
1592
+ /* @__PURE__ */ jsxs5(Text5, { children: [
1593
+ "Scanning",
1594
+ sc && sc.total > 0 ? ` ${Math.min(sc.done, sc.total)}/${sc.total} files (${sc.phase})` : "",
1595
+ "\u2026"
1596
+ ] }),
1597
+ sc?.source && sc.detail ? /* @__PURE__ */ jsxs5(Text5, { dimColor: true, children: [
1598
+ sc.source,
1599
+ ": ",
1600
+ sc.detail,
1601
+ ". Large videos take a while; the result is cached."
1602
+ ] }) : null
1586
1603
  ] });
1587
1604
  } else if (state.view === "error") {
1588
1605
  body = /* @__PURE__ */ jsx5(Text5, { color: "red", children: state.error });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bytecut",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "Audit and optimize images and videos in web projects.",
5
5
  "type": "module",
6
6
  "license": "MIT",