iye c0bce80dd1
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 5m13s
chore(tools): asset compression pipeline for TOS bucket upload
新增 tools/asset-pipeline/ 用于把 public/portraits & videos 压缩成桶友好体积:
- sharp:    PNG → WebP q82, 最大宽 1600 (-view 三视图 2400)
- ffmpeg:   MP4 → libx264 CRF 28, 最大宽 1920, AAC 96k, faststart
- pack.mjs: tar -czf 整目录 → cyber-star-assets.tar.gz

效果 (146 portraits + 33 videos):
- 立绘:  768.6MB → 26.8MB  (-96%)
- 视频:  251.7MB → 76.1MB  (-70%)
- 总计:  1020MB  → 103MB   压缩到 1/10, 95s 跑完

输出位于仓库外 ../assets-compressed/ 与 ../cyber-star-assets.tar.gz, 不入 git。

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 14:26:42 +08:00

48 lines
1.4 KiB
JavaScript

/**
* 把 assets-compressed/ 整目录打包成 cyber-star-assets.tar.gz
* 用 node 自带 tar 模块的等价方式 (调系统 tar)
*/
import { spawn } from "node:child_process";
import { existsSync, statSync } from "node:fs";
import { resolve, dirname } from "node:path";
import { fileURLToPath } from "node:url";
const __dirname = dirname(fileURLToPath(import.meta.url));
const REPO_ROOT = resolve(__dirname, "..", "..");
const PARENT = resolve(REPO_ROOT, "..");
const SRC_DIR = "assets-compressed";
const OUT_FILE = "cyber-star-assets.tar.gz";
function fmt(n) {
if (n >= 1024 * 1024 * 1024) return `${(n / 1024 / 1024 / 1024).toFixed(2)}GB`;
if (n >= 1024 * 1024) return `${(n / 1024 / 1024).toFixed(1)}MB`;
return `${(n / 1024).toFixed(0)}KB`;
}
const srcAbs = resolve(PARENT, SRC_DIR);
if (!existsSync(srcAbs)) {
console.error(`❌ 找不到压缩输出目录: ${srcAbs}\n请先运行 npm run compress`);
process.exit(1);
}
console.log("打包源:", srcAbs);
console.log("输出: ", resolve(PARENT, OUT_FILE));
const t0 = Date.now();
const tar = spawn(
"tar",
["-czf", OUT_FILE, SRC_DIR],
{ cwd: PARENT, stdio: "inherit" },
);
tar.on("close", (code) => {
if (code !== 0) {
console.error(`❌ tar 退出码 ${code}`);
process.exit(code ?? 1);
}
const size = statSync(resolve(PARENT, OUT_FILE)).size;
console.log(
`✅ 打包完成: ${OUT_FILE} ${fmt(size)} 用时 ${((Date.now() - t0) / 1000).toFixed(1)}s`,
);
});