bytecut 0.2.1 → 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 +18 -4
- package/dist/{audit-EBKL6L65.js → audit-RKJNHNDZ.js} +11 -11
- package/dist/{check-XTTU5SCZ.js → check-MN7R7XOM.js} +10 -10
- package/dist/{chunk-IZ4JEL3Q.js → chunk-4AM2CCML.js} +1 -1
- package/dist/{chunk-4FIB6VNC.js → chunk-7RHHOWQT.js} +1 -1
- package/dist/{chunk-KN5QOB7R.js → chunk-ABHEWNYQ.js} +4 -4
- package/dist/{chunk-TCQFQ354.js → chunk-CSDZGNUN.js} +14 -3
- package/dist/{chunk-WI4ZO7GP.js → chunk-DZLFVN4G.js} +14 -11
- package/dist/{chunk-PQRBLHCC.js → chunk-EM7JMQND.js} +1 -1
- package/dist/{chunk-HLBNRB3S.js → chunk-GNW3OK5U.js} +198 -44
- package/dist/{chunk-RZ2V4PCS.js → chunk-JMMXC5UA.js} +3 -3
- package/dist/{chunk-FY53B4NB.js → chunk-KJK7XGWA.js} +24 -13
- package/dist/{chunk-QOG2F4SF.js → chunk-L5HLKZDW.js} +2 -2
- package/dist/{chunk-MV6J66AP.js → chunk-MUE3L5XT.js} +1 -1
- package/dist/{chunk-SKANYRVT.js → chunk-NOTYXNMY.js} +1 -1
- package/dist/{chunk-3PF6BBFK.js → chunk-PE5NKZCH.js} +18 -8
- package/dist/{chunk-S3FQYUTI.js → chunk-SMR2764T.js} +1 -1
- package/dist/{chunk-7DHE25UE.js → chunk-UZ3Z2T5I.js} +1 -1
- package/dist/cli.js +22 -22
- package/dist/{compare-MPPGTUC5.js → compare-XRMDMCZL.js} +8 -8
- package/dist/{doctor-PWCUEBD5.js → doctor-YYTLIOVG.js} +5 -5
- package/dist/{explain-LKOB7FRT.js → explain-GZWGHYRY.js} +12 -7
- package/dist/{init-CWDWVJ55.js → init-3IQAWWBW.js} +4 -4
- package/dist/{optimize-TLJG7UYF.js → optimize-BDPL4Y2Z.js} +11 -11
- package/dist/{snippet-IIPVF6PS.js → snippet-6P7CMROL.js} +7 -7
- package/dist/{tui-6WSMCHZS.js → tui-ETSPQFXB.js} +29 -17
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -74,15 +74,26 @@ Everything is optional. Config is only needed to make a choice stick. Put it in
|
|
|
74
74
|
"maxSize": "150 KB",
|
|
75
75
|
"floor": 65
|
|
76
76
|
},
|
|
77
|
-
"loop": { "extends": "background-video", "vmaf": 92 }
|
|
77
|
+
"loop": { "extends": "background-video", "vmaf": 92 },
|
|
78
|
+
"promo": {
|
|
79
|
+
"extends": "content-video",
|
|
80
|
+
"ssimulacra2": 70,
|
|
81
|
+
"floor": 55,
|
|
82
|
+
"maxSize": "5 MB"
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
"assets": {
|
|
86
|
+
"public/gallery/**": "gallery",
|
|
87
|
+
"public/loops/*.mp4": "loop",
|
|
88
|
+
"public/promo.mp4": "promo"
|
|
78
89
|
},
|
|
79
|
-
"assets": { "public/gallery/**": "gallery", "public/loops/*.mp4": "loop" },
|
|
80
90
|
"budgets": { "maxFileSize": "250 KB", "maxTotalSize": "5 MB" }
|
|
81
91
|
}
|
|
82
92
|
```
|
|
83
93
|
|
|
84
94
|
- **Presets**: built-in `photo`, `hero`, `icon`, `background-video`, `content-video` and
|
|
85
|
-
`gif-to-video`. Add your own with `extends`; the first matching `assets` glob wins.
|
|
95
|
+
`gif-to-video`. Add your own with `extends`; the first matching `assets` glob wins. In the
|
|
96
|
+
TUI, `p` and `/preset` offer image presets for images and video presets for videos and GIFs.
|
|
86
97
|
- **Size**: `maxEdge` caps the longest side; `widths` lists widths instead (a responsive set).
|
|
87
98
|
- **Formats**: `jpg` (default), `png`, `webp`, `avif`. A `<picture>` lists AVIF, then WebP, and
|
|
88
99
|
the JPEG is the `<img>`.
|
|
@@ -98,7 +109,10 @@ that still meets it:
|
|
|
98
109
|
confirms on the full-size output.
|
|
99
110
|
- `maxSize` with a required `floor` lowers quality, then steps the width down, to fit a size
|
|
100
111
|
cap. A constraint that cannot be met writes nothing and fails the run; it is never silently
|
|
101
|
-
exceeded.
|
|
112
|
+
exceeded. On video the cap is per output file (AV1 and H.264 each): the CRF is raised until
|
|
113
|
+
the predicted size fits, never past the `floor`, and then the width steps down by a fifth at a
|
|
114
|
+
time, never below half. The finished file is measured, not just predicted. A narrower file is
|
|
115
|
+
named for its real width, e.g. `promo-1228.h264.mp4`.
|
|
102
116
|
- `vmaf: 92` (video) uses ffmpeg's `libvmaf`. Without it, the preset's fixed quality is used
|
|
103
117
|
and a warning says so.
|
|
104
118
|
|
|
@@ -4,16 +4,16 @@ import {
|
|
|
4
4
|
} from "./chunk-LTWUPQDT.js";
|
|
5
5
|
import {
|
|
6
6
|
audit
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-DZLFVN4G.js";
|
|
8
8
|
import {
|
|
9
9
|
formatAuditReport
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-GNW3OK5U.js";
|
|
11
11
|
import "./chunk-3DBJKHOD.js";
|
|
12
|
-
import "./chunk-
|
|
12
|
+
import "./chunk-CSDZGNUN.js";
|
|
13
13
|
import {
|
|
14
14
|
sharpImageEngine
|
|
15
15
|
} from "./chunk-CSFBJ3UH.js";
|
|
16
|
-
import "./chunk-
|
|
16
|
+
import "./chunk-JMMXC5UA.js";
|
|
17
17
|
import {
|
|
18
18
|
configArg,
|
|
19
19
|
createVideoEngine,
|
|
@@ -21,15 +21,15 @@ import {
|
|
|
21
21
|
ffmpegInstallText,
|
|
22
22
|
ffmpegStatus,
|
|
23
23
|
noAssetsMessage
|
|
24
|
-
} from "./chunk-
|
|
25
|
-
import "./chunk-
|
|
26
|
-
import "./chunk-
|
|
27
|
-
import "./chunk-
|
|
28
|
-
import "./chunk-
|
|
24
|
+
} from "./chunk-ABHEWNYQ.js";
|
|
25
|
+
import "./chunk-SMR2764T.js";
|
|
26
|
+
import "./chunk-L5HLKZDW.js";
|
|
27
|
+
import "./chunk-PE5NKZCH.js";
|
|
28
|
+
import "./chunk-4AM2CCML.js";
|
|
29
29
|
import {
|
|
30
30
|
guarded
|
|
31
|
-
} from "./chunk-
|
|
32
|
-
import "./chunk-
|
|
31
|
+
} from "./chunk-MUE3L5XT.js";
|
|
32
|
+
import "./chunk-KJK7XGWA.js";
|
|
33
33
|
|
|
34
34
|
// src/cli/commands/audit.ts
|
|
35
35
|
import { defineCommand } from "citty";
|
|
@@ -4,16 +4,16 @@ import {
|
|
|
4
4
|
} from "./chunk-LTWUPQDT.js";
|
|
5
5
|
import {
|
|
6
6
|
audit
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-DZLFVN4G.js";
|
|
8
8
|
import {
|
|
9
9
|
formatAuditReport
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-GNW3OK5U.js";
|
|
11
11
|
import "./chunk-3DBJKHOD.js";
|
|
12
|
-
import "./chunk-
|
|
12
|
+
import "./chunk-CSDZGNUN.js";
|
|
13
13
|
import {
|
|
14
14
|
sharpImageEngine
|
|
15
15
|
} from "./chunk-CSFBJ3UH.js";
|
|
16
|
-
import "./chunk-
|
|
16
|
+
import "./chunk-JMMXC5UA.js";
|
|
17
17
|
import {
|
|
18
18
|
configArg,
|
|
19
19
|
createVideoEngine,
|
|
@@ -21,22 +21,22 @@ import {
|
|
|
21
21
|
ffmpegInstallText,
|
|
22
22
|
ffmpegStatus,
|
|
23
23
|
noAssetsMessage
|
|
24
|
-
} from "./chunk-
|
|
24
|
+
} from "./chunk-ABHEWNYQ.js";
|
|
25
25
|
import {
|
|
26
26
|
run
|
|
27
|
-
} from "./chunk-
|
|
27
|
+
} from "./chunk-SMR2764T.js";
|
|
28
28
|
import {
|
|
29
29
|
toPosix
|
|
30
|
-
} from "./chunk-
|
|
31
|
-
import "./chunk-
|
|
32
|
-
import "./chunk-
|
|
30
|
+
} from "./chunk-L5HLKZDW.js";
|
|
31
|
+
import "./chunk-PE5NKZCH.js";
|
|
32
|
+
import "./chunk-4AM2CCML.js";
|
|
33
33
|
import {
|
|
34
34
|
BytecutError,
|
|
35
35
|
CONFIG_FILES,
|
|
36
36
|
formatBytes,
|
|
37
37
|
percentChange,
|
|
38
38
|
plural
|
|
39
|
-
} from "./chunk-
|
|
39
|
+
} from "./chunk-KJK7XGWA.js";
|
|
40
40
|
|
|
41
41
|
// src/cli/commands/check.ts
|
|
42
42
|
import { defineCommand } from "citty";
|
|
@@ -3,19 +3,19 @@ import {
|
|
|
3
3
|
isFile,
|
|
4
4
|
readOsReleaseId,
|
|
5
5
|
run
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-SMR2764T.js";
|
|
7
7
|
import {
|
|
8
8
|
createFfmpegVideoEngine
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-L5HLKZDW.js";
|
|
10
10
|
import {
|
|
11
11
|
installHint,
|
|
12
12
|
resolveBinary,
|
|
13
13
|
resolveConfig
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-PE5NKZCH.js";
|
|
15
15
|
import {
|
|
16
16
|
BytecutError,
|
|
17
17
|
loadConfig
|
|
18
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-KJK7XGWA.js";
|
|
19
19
|
|
|
20
20
|
// src/cli/shared.ts
|
|
21
21
|
import { existsSync } from "fs";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
BytecutError
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-KJK7XGWA.js";
|
|
5
5
|
|
|
6
6
|
// src/core/plan/schema.ts
|
|
7
7
|
import { z } from "zod";
|
|
@@ -49,7 +49,12 @@ var videoOutputSchema = z.discriminatedUnion("role", [
|
|
|
49
49
|
maxFps: z.number().positive().nullable(),
|
|
50
50
|
audio: z.enum(["strip", "keep"]),
|
|
51
51
|
audioBitrate: z.string().regex(/^\d+k$/),
|
|
52
|
-
search: z.object({
|
|
52
|
+
search: z.object({
|
|
53
|
+
metric: z.enum(["ssimulacra2", "vmaf"]),
|
|
54
|
+
target: z.number().min(1).max(100),
|
|
55
|
+
floor: z.number().min(1).max(100).optional(),
|
|
56
|
+
maxBytes: z.number().int().positive().optional()
|
|
57
|
+
}).strict().optional()
|
|
53
58
|
}).strict(),
|
|
54
59
|
z.object({
|
|
55
60
|
role: z.literal("poster"),
|
|
@@ -182,7 +187,13 @@ var videoOutputSchema2 = z2.object({
|
|
|
182
187
|
codecs: z2.string().optional(),
|
|
183
188
|
encoder: z2.string(),
|
|
184
189
|
status: z2.enum(["written", "already-optimal", "stale"]),
|
|
185
|
-
search: z2.object({
|
|
190
|
+
search: z2.object({
|
|
191
|
+
metric: z2.enum(["ssimulacra2", "vmaf"]),
|
|
192
|
+
target: z2.number(),
|
|
193
|
+
floor: z2.number().optional(),
|
|
194
|
+
maxBytes: z2.number().int().optional(),
|
|
195
|
+
plannedPath: z2.string().optional()
|
|
196
|
+
}).strict().optional(),
|
|
186
197
|
score: z2.number().optional(),
|
|
187
198
|
scorer: z2.enum(["wasm", "native", "libvmaf"]).optional()
|
|
188
199
|
}).strict();
|
|
@@ -6,38 +6,38 @@ import {
|
|
|
6
6
|
sortFindings,
|
|
7
7
|
walkFiles,
|
|
8
8
|
withVideos
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-GNW3OK5U.js";
|
|
10
10
|
import {
|
|
11
11
|
MANIFEST_FILE,
|
|
12
12
|
parseManifest
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-CSDZGNUN.js";
|
|
14
14
|
import {
|
|
15
15
|
ANIMATED_GIF_REASON,
|
|
16
16
|
buildPlan,
|
|
17
17
|
classify,
|
|
18
18
|
classifyVideo,
|
|
19
19
|
sha256
|
|
20
|
-
} from "./chunk-
|
|
20
|
+
} from "./chunk-JMMXC5UA.js";
|
|
21
21
|
import {
|
|
22
22
|
isFastStart,
|
|
23
23
|
toPosix
|
|
24
|
-
} from "./chunk-
|
|
24
|
+
} from "./chunk-L5HLKZDW.js";
|
|
25
25
|
import {
|
|
26
26
|
BUILTIN_POLICY,
|
|
27
27
|
getImagePreset,
|
|
28
28
|
pickAv1Encoder,
|
|
29
29
|
rootPreset
|
|
30
|
-
} from "./chunk-
|
|
30
|
+
} from "./chunk-PE5NKZCH.js";
|
|
31
31
|
import {
|
|
32
32
|
SafeFs
|
|
33
|
-
} from "./chunk-
|
|
33
|
+
} from "./chunk-4AM2CCML.js";
|
|
34
34
|
import {
|
|
35
35
|
ALL_RULES,
|
|
36
36
|
checkCache,
|
|
37
37
|
checkVideoCache,
|
|
38
38
|
matchesGlob,
|
|
39
39
|
savingOf
|
|
40
|
-
} from "./chunk-
|
|
40
|
+
} from "./chunk-KJK7XGWA.js";
|
|
41
41
|
|
|
42
42
|
// src/core/commands/audit.ts
|
|
43
43
|
import { stat as stat2 } from "fs/promises";
|
|
@@ -232,10 +232,10 @@ async function estimateVideo(item, deps, sourcePath) {
|
|
|
232
232
|
].join("|");
|
|
233
233
|
let streamBytes = deps.cache.getEstimate(key)?.bytes;
|
|
234
234
|
if (streamBytes === void 0) {
|
|
235
|
-
let
|
|
235
|
+
let total2 = 0;
|
|
236
236
|
let seconds = 0;
|
|
237
237
|
for (const s of starts) {
|
|
238
|
-
|
|
238
|
+
total2 += await deps.engine.sampleBytes(
|
|
239
239
|
{
|
|
240
240
|
source: sourcePath,
|
|
241
241
|
encoder,
|
|
@@ -252,12 +252,15 @@ async function estimateVideo(item, deps, sourcePath) {
|
|
|
252
252
|
deps.onStep?.({ done: ++stepsDone, total: stepTotal });
|
|
253
253
|
seconds += s.durationSec;
|
|
254
254
|
}
|
|
255
|
-
streamBytes = Math.round(seconds > 0 ?
|
|
255
|
+
streamBytes = Math.round(seconds > 0 ? total2 / seconds * item.durationSec : total2);
|
|
256
256
|
deps.cache.setEstimate(key, { bytes: streamBytes, basis: exact ? "exact" : "extrapolated" });
|
|
257
257
|
}
|
|
258
|
+
const total = streamBytes + audioBytes(out, item);
|
|
258
259
|
perCodec.push({
|
|
259
260
|
codec: out.codec,
|
|
260
|
-
|
|
261
|
+
// A size cap is met by raising the CRF or narrowing the width, or the output is not
|
|
262
|
+
// written at all, so what a run will produce never exceeds it.
|
|
263
|
+
bytes: out.search?.maxBytes !== void 0 ? Math.min(total, out.search.maxBytes) : total,
|
|
261
264
|
basis: exact ? "exact" : "extrapolated"
|
|
262
265
|
});
|
|
263
266
|
}
|