halfsize 0.0.0-stage → 0.1.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/LICENSE +21 -0
- package/README.md +256 -2
- package/README.zh-CN.md +242 -0
- package/bin/halfsize.mjs +8 -0
- package/package.json +45 -4
- package/src/cli.mjs +298 -0
- package/src/encode.mjs +142 -0
- package/src/ffmpeg.mjs +70 -0
- package/src/human.mjs +53 -0
- package/src/plan.mjs +152 -0
- package/src/probe.mjs +72 -0
- package/src/quality.mjs +75 -0
- package/src/server.mjs +288 -0
- package/web/index.html +557 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 YKLovePython
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,257 @@
|
|
|
1
|
-
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="https://raw.githubusercontent.com/YKLovePython/halfsize/main/docs/card.webp" width="760" alt="halfsize: 23.2 MB to 1.99 MB, 12x smaller, VMAF 84.4">
|
|
3
|
+
</p>
|
|
2
4
|
|
|
3
|
-
|
|
5
|
+
<h1 align="center">halfsize</h1>
|
|
6
|
+
|
|
7
|
+
<p align="center">
|
|
8
|
+
<b>Fit any video into any size limit. Locally. And prove the quality survived.</b>
|
|
9
|
+
</p>
|
|
10
|
+
|
|
11
|
+
<p align="center">
|
|
12
|
+
<a href="https://www.npmjs.com/package/halfsize"><img alt="npm" src="https://img.shields.io/npm/v/halfsize?color=b6f36a"></a>
|
|
13
|
+
<a href="https://www.npmjs.com/package/halfsize"><img alt="downloads" src="https://img.shields.io/npm/dm/halfsize?color=b6f36a"></a>
|
|
14
|
+
<a href="LICENSE"><img alt="license" src="https://img.shields.io/badge/license-MIT-b6f36a"></a>
|
|
15
|
+
<img alt="node" src="https://img.shields.io/badge/node-%3E%3D18-b6f36a">
|
|
16
|
+
<img alt="no upload" src="https://img.shields.io/badge/upload-none-b6f36a">
|
|
17
|
+
</p>
|
|
18
|
+
|
|
19
|
+
<p align="center">
|
|
20
|
+
<a href="README.zh-CN.md">中文说明</a> ·
|
|
21
|
+
<a href="#quick-start">Quick start</a> ·
|
|
22
|
+
<a href="#how-it-works">How it works</a> ·
|
|
23
|
+
<a href="#faq">FAQ</a>
|
|
24
|
+
</p>
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
WeChat refuses anything over **25 MB**. Discord gives you **10 MB**. Email chokes around **20 MB**.
|
|
29
|
+
So you open a video editor, guess a bitrate, export, check the size, export again, and still
|
|
30
|
+
can't tell whether the picture got worse.
|
|
31
|
+
|
|
32
|
+
`halfsize` turns that into one command:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
npx halfsize wedding.mp4 --to 25mb
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
It computes the bitrate, picks the resolution that actually fits, encodes twice, then measures
|
|
39
|
+
the result with **SSIM** and **VMAF** so you know whether it still looks the same.
|
|
40
|
+
|
|
41
|
+
```
|
|
42
|
+
halfsize 0.1.0
|
|
43
|
+
|
|
44
|
+
source.mp4
|
|
45
|
+
23.2 MB 8s 1920x1080 30.00 fps h264
|
|
46
|
+
|
|
47
|
+
target 2.00 MB
|
|
48
|
+
plan H.264 960x540 1.81 Mbps audio 128 kbps 2-pass
|
|
49
|
+
scale 1920x1080 -> 960x540
|
|
50
|
+
estimate 1.94 MB (12x smaller)
|
|
51
|
+
output source.2mb.mp4
|
|
52
|
+
|
|
53
|
+
measuring quality done in 2s
|
|
54
|
+
|
|
55
|
+
23.2 MB -> 1.99 MB 12x smaller 0.3% under target
|
|
56
|
+
VMAF 84.4 SSIM 0.968 slight softening
|
|
57
|
+
|
|
58
|
+
-> C:\Videos\source.2mb.mp4
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Why this exists
|
|
62
|
+
|
|
63
|
+
"Make it smaller" is a solved problem. **"Make it fit in exactly 25 MB, and tell me if it
|
|
64
|
+
still looks right"** is the part every tool makes you guess at.
|
|
65
|
+
|
|
66
|
+
- HandBrake is excellent, but it asks you to think in codecs, RF values, and presets.
|
|
67
|
+
- Online compressors work until you remember you just uploaded a private video to a stranger.
|
|
68
|
+
- `ffmpeg` can do all of this. Nobody remembers the 11 flags, and almost nobody checks VMAF afterwards.
|
|
69
|
+
|
|
70
|
+
`halfsize` is the missing 5%: a target size you type, and a quality score you can read.
|
|
71
|
+
|
|
72
|
+
| | halfsize | HandBrake | Online compressors |
|
|
73
|
+
|---|---|---|---|
|
|
74
|
+
| Fit an exact size | ✅ one flag | manual bitrate math | usually re-encodes blindly |
|
|
75
|
+
| Tells you the quality cost | ✅ VMAF + SSIM | ❌ | ❌ |
|
|
76
|
+
| Your file stays on your machine | ✅ | ✅ | ❌ |
|
|
77
|
+
| Scriptable / CI friendly | ✅ CLI + JSON | ❌ GUI | ❌ |
|
|
78
|
+
| Learns a new UI | no | yes | yes |
|
|
79
|
+
|
|
80
|
+
## Quick start
|
|
81
|
+
|
|
82
|
+
Requires [Node.js 18+](https://nodejs.org). FFmpeg is bundled on first install; if it cannot
|
|
83
|
+
be downloaded, `halfsize` uses your system install instead.
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
# compress to a size
|
|
87
|
+
npx halfsize video.mp4 --to 25mb
|
|
88
|
+
|
|
89
|
+
# smaller file, same quality budget
|
|
90
|
+
npx halfsize video.mp4 --to 10mb --codec h265
|
|
91
|
+
|
|
92
|
+
# quality-first mode when size does not matter
|
|
93
|
+
npx halfsize video.mp4 --crf 23
|
|
94
|
+
|
|
95
|
+
# preview the plan without encoding
|
|
96
|
+
npx halfsize video.mp4 --to 25mb --dry-run
|
|
97
|
+
|
|
98
|
+
# machine-readable, for scripts
|
|
99
|
+
npx halfsize video.mp4 --to 25mb --json
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
> While the npm release is being finalized, the same commands work straight from this repo:
|
|
103
|
+
> `npx github:YKLovePython/halfsize video.mp4 --to 25mb`
|
|
104
|
+
|
|
105
|
+
Install it globally if you will use it more than once:
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
npm install -g halfsize
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### The local web app
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
npx halfsize --web
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Opens `http://127.0.0.1:4174` — drag a video in, pick 25 MB / 10 MB / 20 MB / 5 MB or type
|
|
118
|
+
your own limit, and watch the encode. The file goes to a temporary folder **on your own
|
|
119
|
+
machine**; nothing is sent anywhere.
|
|
120
|
+
|
|
121
|
+
<p align="center">
|
|
122
|
+
<img src="https://raw.githubusercontent.com/YKLovePython/halfsize/main/docs/screenshot.webp" width="860" alt="halfsize local web app showing 23.2 MB compressed to 1.99 MB with VMAF 84.4">
|
|
123
|
+
</p>
|
|
124
|
+
|
|
125
|
+
After each run you can download a **share card** (the image at the top of this README) with the
|
|
126
|
+
real numbers on it.
|
|
127
|
+
|
|
128
|
+
## How it works
|
|
129
|
+
|
|
130
|
+
1. **Probe.** Read duration, resolution, frame rate, audio bitrate, rotation, and HDR metadata.
|
|
131
|
+
2. **Budget.** Total bitrate = target bytes × 8 ÷ duration, minus audio, minus a small
|
|
132
|
+
container overhead.
|
|
133
|
+
3. **Pick a resolution.** Walk a ladder from the source size down to 240p and choose the
|
|
134
|
+
largest one whose pixels fit the budget at that codec's bits-per-pixel target
|
|
135
|
+
(H.264: 0.072 bpp, H.265: 0.045 bpp). This is why a 12× shrink still looks reasonable:
|
|
136
|
+
the encoder gets to spend its bits on fewer pixels.
|
|
137
|
+
4. **Encode twice.** Two-pass ABR with `libx264` or `libx265`. Pass one learns where the bits
|
|
138
|
+
are needed; pass two spends them. Output is `yuv420p`, AAC audio, `+faststart`, and tagged
|
|
139
|
+
`hvc1` for H.265 so it plays on Apple devices.
|
|
140
|
+
5. **Prove it.** Sample three points in the video, compute SSIM at each, and VMAF at the
|
|
141
|
+
midpoint. Compare against thresholds and print a verdict in plain language.
|
|
142
|
+
|
|
143
|
+
```
|
|
144
|
+
VMAF ≥ 95 indistinguishable
|
|
145
|
+
VMAF ≥ 90 looks the same
|
|
146
|
+
VMAF ≥ 80 slight softening
|
|
147
|
+
VMAF ≥ 70 visible loss
|
|
148
|
+
below 70 clearly degraded
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
VMAF is Netflix's perceptual metric; it correlates with human judgement far better than PSNR
|
|
152
|
+
or bitrate. It is not magic, but it is a much better answer than "trust me".
|
|
153
|
+
|
|
154
|
+
## CLI reference
|
|
155
|
+
|
|
156
|
+
```
|
|
157
|
+
halfsize <file> [options]
|
|
158
|
+
|
|
159
|
+
--to <size> Target size: 25mb, 700k, 1.5gb
|
|
160
|
+
--crf <n> Quality mode instead of size mode (18 best, 23 balanced, 28 small)
|
|
161
|
+
--codec <name> h264 (default, maximum compatibility) or h265 (about 30% smaller)
|
|
162
|
+
--preset <name> ultrafast .. veryslow (default: medium)
|
|
163
|
+
--out <file> Output path
|
|
164
|
+
--no-verify Skip SSIM/VMAF
|
|
165
|
+
--no-downscale Never reduce resolution (the target may then be missed)
|
|
166
|
+
--json Machine-readable result
|
|
167
|
+
--dry-run Print the plan, do not encode
|
|
168
|
+
--web Start the local web app
|
|
169
|
+
--doctor Show which FFmpeg and encoders were found
|
|
170
|
+
-h, --help Help
|
|
171
|
+
-v, --version Version
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
Sizes accept `b`, `k`/`kb`, `m`/`mb`, `g`/`gb`, with or without a space. No unit means MB.
|
|
175
|
+
|
|
176
|
+
Exit codes: `0` success, `1` error. `--json` returns the plan, the measured quality, and the
|
|
177
|
+
target accuracy, which makes it easy to wrap in a script.
|
|
178
|
+
|
|
179
|
+
## Install notes
|
|
180
|
+
|
|
181
|
+
`ffmpeg-static` and `ffprobe-static` download prebuilt binaries during `npm install`. If you are
|
|
182
|
+
behind a proxy or a TLS-inspecting network, that download can fail. `halfsize` then falls back
|
|
183
|
+
to any `ffmpeg` on your `PATH`, or to a build you point at explicitly:
|
|
184
|
+
|
|
185
|
+
```bash
|
|
186
|
+
HALFSIZE_FFMPEG=/path/to/ffmpeg HALFSIZE_FFPROBE=/path/to/ffprobe npx halfsize video.mp4 --to 25mb
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
On Windows, `winget install Gyan.FFmpeg` is enough. On macOS, `brew install ffmpeg`. On
|
|
190
|
+
Debian/Ubuntu, `sudo apt install ffmpeg`. Run `npx halfsize --doctor` to see what was found.
|
|
191
|
+
|
|
192
|
+
## FAQ
|
|
193
|
+
|
|
194
|
+
**Does my video get uploaded?**
|
|
195
|
+
No. The CLI never touches the network. The web app talks only to `127.0.0.1`, and uploaded
|
|
196
|
+
files go to a temp directory that is deleted when the process exits.
|
|
197
|
+
|
|
198
|
+
**Why did it lower my resolution?**
|
|
199
|
+
Because 25 MB of 4K is not enough bits to look good. Lowering resolution and keeping the
|
|
200
|
+
bitrate per pixel sane almost always looks better than keeping 4K and starving it. Use
|
|
201
|
+
`--no-downscale` if you want to override that.
|
|
202
|
+
|
|
203
|
+
**Why is the output slightly under the target, not exactly on it?**
|
|
204
|
+
Container overhead and two-pass ABR both vary a little. `halfsize` aims ~3% under so it never
|
|
205
|
+
overshoots a hard platform cap. In the example above it landed 0.3% under.
|
|
206
|
+
|
|
207
|
+
**H.264 or H.265?**
|
|
208
|
+
H.264 plays everywhere, including old Android phones and browsers. H.265 is roughly 30% smaller
|
|
209
|
+
at the same quality but can be awkward in some editors and older hardware. Default is H.264.
|
|
210
|
+
|
|
211
|
+
**Does it work on Windows / macOS / Linux?**
|
|
212
|
+
Yes. It is plain Node.js and FFmpeg.
|
|
213
|
+
|
|
214
|
+
**Can I batch a folder?**
|
|
215
|
+
Not built in yet. A shell loop works today:
|
|
216
|
+
|
|
217
|
+
```bash
|
|
218
|
+
for f in *.mp4; do npx halfsize "$f" --to 25mb; done
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
**Why "halfsize"?**
|
|
222
|
+
Because the honest answer to "how much smaller will it get?" is usually "about half, or a lot
|
|
223
|
+
more than half". The tool reports the real number either way.
|
|
224
|
+
|
|
225
|
+
## Roadmap
|
|
226
|
+
|
|
227
|
+
- Hardware encoding (NVENC / QSV / VideoToolbox) as an opt-in fast path
|
|
228
|
+
- Batch mode and a folder watcher
|
|
229
|
+
- Subtitle and multiple audio track passthrough
|
|
230
|
+
- HDR-to-SDR tonemapping, so iPhone HDR clips keep their colors
|
|
231
|
+
- AV1 (`libsvtav1`) when the source is already AV1-friendly
|
|
232
|
+
- A single-file binary, so Node.js is not required
|
|
233
|
+
- Presets for specific platform caps (WeChat, Discord, email, X, Bluesky)
|
|
234
|
+
|
|
235
|
+
If you want one of these, open an issue and say which. Pull requests are welcome; see
|
|
236
|
+
[CONTRIBUTING.md](CONTRIBUTING.md).
|
|
237
|
+
|
|
238
|
+
## Development
|
|
239
|
+
|
|
240
|
+
```bash
|
|
241
|
+
git clone https://github.com/YKLovePython/halfsize.git
|
|
242
|
+
cd halfsize
|
|
243
|
+
npm install
|
|
244
|
+
npm test
|
|
245
|
+
node bin/halfsize.mjs --web
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
The test suite includes a real end-to-end encode: it generates a clip with FFmpeg, compresses
|
|
249
|
+
it to a target, probes the output, and asserts the size and SSIM. No fixtures in the repo.
|
|
250
|
+
|
|
251
|
+
## License
|
|
252
|
+
|
|
253
|
+
MIT. Use it, fork it, ship it.
|
|
254
|
+
|
|
255
|
+
---
|
|
256
|
+
|
|
257
|
+
If `halfsize` saved you from guessing a bitrate, a star helps the next person find it.
|
package/README.zh-CN.md
ADDED
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="https://raw.githubusercontent.com/YKLovePython/halfsize/main/docs/card.webp" width="760" alt="halfsize:23.2 MB 压缩到 1.99 MB,小了 12 倍,VMAF 84.4">
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
<h1 align="center">halfsize</h1>
|
|
6
|
+
|
|
7
|
+
<p align="center">
|
|
8
|
+
<b>把任何视频塞进任何大小限制。全程本地。并且证明画质没垮。</b>
|
|
9
|
+
</p>
|
|
10
|
+
|
|
11
|
+
<p align="center">
|
|
12
|
+
<a href="https://www.npmjs.com/package/halfsize"><img alt="npm" src="https://img.shields.io/npm/v/halfsize?color=b6f36a"></a>
|
|
13
|
+
<a href="https://www.npmjs.com/package/halfsize"><img alt="downloads" src="https://img.shields.io/npm/dm/halfsize?color=b6f36a"></a>
|
|
14
|
+
<a href="LICENSE"><img alt="license" src="https://img.shields.io/badge/license-MIT-b6f36a"></a>
|
|
15
|
+
<img alt="node" src="https://img.shields.io/badge/node-%3E%3D18-b6f36a">
|
|
16
|
+
<img alt="no upload" src="https://img.shields.io/badge/%E4%B8%8D%E4%B8%8A%E4%BC%A0-%E6%9C%AC%E5%9C%B0-b6f36a">
|
|
17
|
+
</p>
|
|
18
|
+
|
|
19
|
+
<p align="center">
|
|
20
|
+
<a href="README.md">English</a> ·
|
|
21
|
+
<a href="#快速开始">快速开始</a> ·
|
|
22
|
+
<a href="#它怎么工作">它怎么工作</a> ·
|
|
23
|
+
<a href="#常见问题">常见问题</a>
|
|
24
|
+
</p>
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
微信不让发超过 **25 MB**,Discord 只给 **10 MB**,邮件大多卡在 **20 MB**。
|
|
29
|
+
于是你打开剪辑软件,猜一个码率,导出,看一眼大小,再导一次——最后还是不知道画质到底掉了多少。
|
|
30
|
+
|
|
31
|
+
`halfsize` 把这件事变成一行命令:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
npx halfsize 婚礼.mp4 --to 25mb
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
它会算码率、挑一个真正塞得下的分辨率、做两遍编码,最后用 **SSIM** 和 **VMAF** 测给你看:
|
|
38
|
+
画质到底还在不在。
|
|
39
|
+
|
|
40
|
+
```
|
|
41
|
+
halfsize 0.1.0
|
|
42
|
+
|
|
43
|
+
source.mp4
|
|
44
|
+
23.2 MB 8s 1920x1080 30.00 fps h264
|
|
45
|
+
|
|
46
|
+
target 2.00 MB
|
|
47
|
+
plan H.264 960x540 1.81 Mbps audio 128 kbps 2-pass
|
|
48
|
+
scale 1920x1080 -> 960x540
|
|
49
|
+
estimate 1.94 MB (12x smaller)
|
|
50
|
+
output source.2mb.mp4
|
|
51
|
+
|
|
52
|
+
measuring quality done in 2s
|
|
53
|
+
|
|
54
|
+
23.2 MB -> 1.99 MB 12x smaller 0.3% under target
|
|
55
|
+
VMAF 84.4 SSIM 0.968 slight softening
|
|
56
|
+
|
|
57
|
+
-> C:\Videos\source.2mb.mp4
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## 为什么做这个
|
|
61
|
+
|
|
62
|
+
「把视频变小」早就有人解决了。真正没人做好的是:**「刚好塞进 25 MB,然后告诉我画质还行不行」**。
|
|
63
|
+
|
|
64
|
+
- HandBrake 很好用,但它要你想清楚编码器、RF 值、preset 这些词。
|
|
65
|
+
- 在线压缩网站也能用,直到你想起自己刚把私人视频传给了陌生人。
|
|
66
|
+
- `ffmpeg` 当然做得到,只是没人记得那 11 个参数,而且几乎没人压完再测一遍 VMAF。
|
|
67
|
+
|
|
68
|
+
`halfsize` 补的就是这最后 5%:你输入一个目标大小,它给你一个看得懂的画质分数。
|
|
69
|
+
|
|
70
|
+
| | halfsize | HandBrake | 在线压缩 |
|
|
71
|
+
|---|---|---|---|
|
|
72
|
+
| 精确压到指定大小 | ✅ 一个参数 | 要自己算码率 | 通常只是盲压 |
|
|
73
|
+
| 告诉你画质代价 | ✅ VMAF + SSIM | ❌ | ❌ |
|
|
74
|
+
| 文件留在本机 | ✅ | ✅ | ❌ |
|
|
75
|
+
| 能写进脚本 / CI | ✅ CLI + JSON | ❌ 图形界面 | ❌ |
|
|
76
|
+
| 需要重新学一套界面 | 不用 | 要 | 要 |
|
|
77
|
+
|
|
78
|
+
## 快速开始
|
|
79
|
+
|
|
80
|
+
需要 [Node.js 18+](https://nodejs.org)。首次安装会下载 FFmpeg;下载失败时会自动使用你系统里已有的 FFmpeg。
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
# 压到指定大小
|
|
84
|
+
npx halfsize video.mp4 --to 25mb
|
|
85
|
+
|
|
86
|
+
# 更小的文件,H.265
|
|
87
|
+
npx halfsize video.mp4 --to 10mb --codec h265
|
|
88
|
+
|
|
89
|
+
# 不在乎大小,只想要好画质
|
|
90
|
+
npx halfsize video.mp4 --crf 23
|
|
91
|
+
|
|
92
|
+
# 只看方案,不真的压
|
|
93
|
+
npx halfsize video.mp4 --to 25mb --dry-run
|
|
94
|
+
|
|
95
|
+
# 给脚本用的 JSON
|
|
96
|
+
npx halfsize video.mp4 --to 25mb --json
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
> npm 版本正在发布中;在那之前,同一条命令可以直接从仓库运行:
|
|
100
|
+
> `npx github:YKLovePython/halfsize video.mp4 --to 25mb`
|
|
101
|
+
|
|
102
|
+
常用的话装到全局:
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
npm install -g halfsize
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### 本地网页版
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
npx halfsize --web
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
打开 `http://127.0.0.1:4174`,把视频拖进去,选 25 MB / 10 MB / 20 MB / 5 MB,或者自己输入一个数,
|
|
115
|
+
然后看着它压。文件只会写进**你自己电脑**的临时目录,不会发到任何地方。
|
|
116
|
+
|
|
117
|
+
<p align="center">
|
|
118
|
+
<img src="https://raw.githubusercontent.com/YKLovePython/halfsize/main/docs/screenshot.webp" width="860" alt="halfsize 本地网页版:23.2 MB 压缩到 1.99 MB,VMAF 84.4">
|
|
119
|
+
</p>
|
|
120
|
+
|
|
121
|
+
每次压完可以生成一张**分享图**(就是本文最上面那张),上面是这次真实的数字。
|
|
122
|
+
|
|
123
|
+
## 它怎么工作
|
|
124
|
+
|
|
125
|
+
1. **探测。** 读取时长、分辨率、帧率、音频码率、旋转信息、HDR 标记。
|
|
126
|
+
2. **算预算。** 总码率 = 目标字节 × 8 ÷ 时长,减掉音频,再留一点容器开销。
|
|
127
|
+
3. **选分辨率。** 从原分辨率开始,沿着 2160p → 240p 的阶梯往下走,挑「按这个编码器每像素比特数
|
|
128
|
+
(H.264 0.072 bpp,H.265 0.045 bpp)刚好塞得下」的最大一档。这就是 12 倍压缩还能看的原因:
|
|
129
|
+
让编码器把有限的比特花在更少的像素上,而不是硬撑 4K。
|
|
130
|
+
4. **压两遍。** `libx264` 或 `libx265` 两遍 ABR。第一遍找出哪里需要比特,第二遍把比特花在那里。
|
|
131
|
+
输出 `yuv420p`、AAC 音频、`+faststart`,H.265 额外打 `hvc1` 标签,保证苹果设备能播。
|
|
132
|
+
5. **证明。** 在视频里取三个采样点算 SSIM,在中间点算 VMAF,对照阈值给一句人话结论。
|
|
133
|
+
|
|
134
|
+
```
|
|
135
|
+
VMAF ≥ 95 几乎看不出差别
|
|
136
|
+
VMAF ≥ 90 看起来一样
|
|
137
|
+
VMAF ≥ 80 轻微变软
|
|
138
|
+
VMAF ≥ 70 能看出损失
|
|
139
|
+
低于 70 明显变差
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
VMAF 是 Netflix 提出的感知质量指标,比 PSNR 或「码率多少」更接近人眼判断。
|
|
143
|
+
它不是魔法,但比「相信我」靠谱得多。
|
|
144
|
+
|
|
145
|
+
## 命令行参数
|
|
146
|
+
|
|
147
|
+
```
|
|
148
|
+
halfsize <file> [options]
|
|
149
|
+
|
|
150
|
+
--to <size> 目标大小:25mb、700k、1.5gb
|
|
151
|
+
--crf <n> 质量模式,不按大小(18 最好,23 均衡,28 更小)
|
|
152
|
+
--codec <name> h264(默认,兼容性最好)或 h265(大约再小 30%)
|
|
153
|
+
--preset <name> ultrafast .. veryslow(默认 medium)
|
|
154
|
+
--out <file> 输出路径
|
|
155
|
+
--no-verify 跳过 SSIM/VMAF 检测
|
|
156
|
+
--no-downscale 绝不降分辨率(可能会压不到目标)
|
|
157
|
+
--json 机器可读结果
|
|
158
|
+
--dry-run 只打印方案,不编码
|
|
159
|
+
--web 启动本地网页版
|
|
160
|
+
--doctor 查看找到的 FFmpeg 和编码器
|
|
161
|
+
-h, --help 帮助
|
|
162
|
+
-v, --version 版本
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
大小支持 `b`、`k`/`kb`、`m`/`mb`、`g`/`gb`,中间可以有空格。不写单位按 MB 算。
|
|
166
|
+
|
|
167
|
+
退出码:`0` 成功,`1` 出错。`--json` 会返回方案、画质测量和目标误差,方便包进脚本。
|
|
168
|
+
|
|
169
|
+
## 安装说明
|
|
170
|
+
|
|
171
|
+
`ffmpeg-static` 和 `ffprobe-static` 会在 `npm install` 时下载预编译二进制。如果你在公司代理或
|
|
172
|
+
会拦截 TLS 的网络后面,这一步可能失败。`halfsize` 会退回到 `PATH` 里的 `ffmpeg`,或者你手动指定的版本:
|
|
173
|
+
|
|
174
|
+
```bash
|
|
175
|
+
HALFSIZE_FFMPEG=/path/to/ffmpeg HALFSIZE_FFPROBE=/path/to/ffprobe npx halfsize video.mp4 --to 25mb
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
Windows 上 `winget install Gyan.FFmpeg` 就够了;macOS 用 `brew install ffmpeg`;
|
|
179
|
+
Debian/Ubuntu 用 `sudo apt install ffmpeg`。跑一下 `npx halfsize --doctor` 能看到实际用的是哪个。
|
|
180
|
+
|
|
181
|
+
## 常见问题
|
|
182
|
+
|
|
183
|
+
**视频会被上传吗?**
|
|
184
|
+
不会。命令行完全不联网。网页版只和 `127.0.0.1` 通信,上传的文件放在临时目录,进程退出就删掉。
|
|
185
|
+
|
|
186
|
+
**为什么给我降分辨率了?**
|
|
187
|
+
因为 25 MB 的 4K 根本不够比特,硬撑 4K 只会满屏马赛克。降分辨率、把每像素比特数保住,
|
|
188
|
+
通常比「分辨率不变但饿死编码器」好看得多。不想降就加 `--no-downscale`。
|
|
189
|
+
|
|
190
|
+
**为什么输出比目标小一点,而不是刚好等于?**
|
|
191
|
+
容器开销和两遍 ABR 都会有波动。`halfsize` 会刻意瞄准低 3%,保证不会超过平台的硬上限。
|
|
192
|
+
上面那个例子里,最终比目标低 0.3%。
|
|
193
|
+
|
|
194
|
+
**H.264 还是 H.265?**
|
|
195
|
+
H.264 到处都能播,包括老安卓机和浏览器。H.265 同画质大约小 30%,但部分剪辑软件和老硬件
|
|
196
|
+
支持得别扭。默认 H.264。
|
|
197
|
+
|
|
198
|
+
**Windows / macOS / Linux 都能用吗?**
|
|
199
|
+
能。就是 Node.js + FFmpeg,没有平台特定的东西。
|
|
200
|
+
|
|
201
|
+
**能批量压一个文件夹吗?**
|
|
202
|
+
还没内置。现在可以用一行 shell:
|
|
203
|
+
|
|
204
|
+
```bash
|
|
205
|
+
for f in *.mp4; do npx halfsize "$f" --to 25mb; done
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
**为什么叫 halfsize?**
|
|
209
|
+
因为「能小多少」的诚实答案通常是「差不多一半,或者远不止一半」。它每次都会把真实数字告诉你。
|
|
210
|
+
|
|
211
|
+
## 路线图
|
|
212
|
+
|
|
213
|
+
- 硬件编码(NVENC / QSV / VideoToolbox)作为可选的快速路径
|
|
214
|
+
- 批量模式与文件夹监听
|
|
215
|
+
- 字幕和多音轨原样保留
|
|
216
|
+
- HDR 转 SDR(让 iPhone 拍的 HDR 视频保住颜色)
|
|
217
|
+
- 源已经是 AV1 时改用 AV1(`libsvtav1`)
|
|
218
|
+
- 单文件二进制,不依赖 Node.js
|
|
219
|
+
- 面向具体平台上限的预设(微信、Discord、邮件、X、Bluesky)
|
|
220
|
+
|
|
221
|
+
想要哪个就开 issue 说一声。欢迎 PR,见 [CONTRIBUTING.md](CONTRIBUTING.md)。
|
|
222
|
+
|
|
223
|
+
## 开发
|
|
224
|
+
|
|
225
|
+
```bash
|
|
226
|
+
git clone https://github.com/YKLovePython/halfsize.git
|
|
227
|
+
cd halfsize
|
|
228
|
+
npm install
|
|
229
|
+
npm test
|
|
230
|
+
node bin/halfsize.mjs --web
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
测试里包含一次真实的端到端编码:用 FFmpeg 生成素材、压到目标大小、探测输出、断言大小和 SSIM。
|
|
234
|
+
仓库里不放视频素材。
|
|
235
|
+
|
|
236
|
+
## 许可证
|
|
237
|
+
|
|
238
|
+
MIT。随便用,随便改,随便发布。
|
|
239
|
+
|
|
240
|
+
---
|
|
241
|
+
|
|
242
|
+
如果 `halfsize` 帮你少猜了一次码率,点个 star 能让下一个人也找到它。
|
package/bin/halfsize.mjs
ADDED
package/package.json
CHANGED
|
@@ -1,6 +1,47 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "halfsize",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "Compress any video to an exact target size, locally, and prove the quality did not drop.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"halfsize": "bin/halfsize.mjs"
|
|
8
|
+
},
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "git+https://github.com/YKLovePython/halfsize.git"
|
|
12
|
+
},
|
|
13
|
+
"homepage": "https://github.com/YKLovePython/halfsize#readme",
|
|
14
|
+
"bugs": {
|
|
15
|
+
"url": "https://github.com/YKLovePython/halfsize/issues"
|
|
16
|
+
},
|
|
17
|
+
"engines": {
|
|
18
|
+
"node": ">=18"
|
|
19
|
+
},
|
|
20
|
+
"files": [
|
|
21
|
+
"bin",
|
|
22
|
+
"src",
|
|
23
|
+
"web",
|
|
24
|
+
"README.md",
|
|
25
|
+
"LICENSE"
|
|
26
|
+
],
|
|
27
|
+
"scripts": {
|
|
28
|
+
"start": "node bin/halfsize.mjs",
|
|
29
|
+
"web": "node bin/halfsize.mjs --web",
|
|
30
|
+
"test": "node --test test/human.test.mjs test/plan.test.mjs test/quality.test.mjs test/cli.test.mjs test/e2e.test.mjs"
|
|
31
|
+
},
|
|
32
|
+
"keywords": [
|
|
33
|
+
"video",
|
|
34
|
+
"compress",
|
|
35
|
+
"video-compression",
|
|
36
|
+
"ffmpeg",
|
|
37
|
+
"target-size",
|
|
38
|
+
"vmaf",
|
|
39
|
+
"cli",
|
|
40
|
+
"windows"
|
|
41
|
+
],
|
|
42
|
+
"license": "MIT",
|
|
43
|
+
"dependencies": {
|
|
44
|
+
"ffmpeg-static": "^5.3.0",
|
|
45
|
+
"ffprobe-static": "^3.1.0"
|
|
46
|
+
}
|
|
47
|
+
}
|