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 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
- # Temporary Holding Version
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
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
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.
@@ -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 能让下一个人也找到它。
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env node
2
+ import { main } from "../src/cli.mjs";
3
+
4
+ main(process.argv.slice(2)).catch((err) => {
5
+ console.error(`\n x ${err?.message || err}`);
6
+ if (process.env.HALFSIZE_DEBUG) console.error(err?.stack);
7
+ process.exit(1);
8
+ });
package/package.json CHANGED
@@ -1,6 +1,47 @@
1
1
  {
2
2
  "name": "halfsize",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
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
+ }