play.wenpai.net/scripts/img-process
elementary-qa 6146665366 feat: 验收工具链入库 + play-verify 增强 --report/--post-forum
入库 4 个验收脚本(play-verify/play-smoke/play-daily-check/img-process),
play-verify 新增 markdown 报告生成和论坛发布功能。

同时完成但不在 git 跟踪范围的变更:
- ~/bin/session-end(会话收尾脚本)
- ~/bin/play-add-plugin(交互式添加插件)
- staging-watcher systemd user service
- cron 自动清理 test-results

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 15:38:43 +08:00

78 lines
2.6 KiB
JavaScript
Executable file
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env node
// img-process — 截图后处理工具
// 用法: img-process <input> [options]
// --resize <width> 缩放到指定宽度(保持比例)
// --quality <1-100> 压缩质量(默认 85
// --watermark <text> 添加水印文字
// --trim 自动裁剪空白边
// --output <path> 输出路径(默认覆盖原文件)
// --webp 转为 WebP 格式
const sharp = require('sharp');
const path = require('path');
const args = process.argv.slice(2);
const input = args.find(a => !a.startsWith('--') && !args[args.indexOf(a) - 1]?.startsWith('--'));
function getArg(name) {
const idx = args.indexOf(`--${name}`);
return idx >= 0 ? args[idx + 1] : null;
}
const hasFlag = (name) => args.includes(`--${name}`);
if (!input) {
console.log('用法: img-process <input.png> [--resize 1280] [--quality 85] [--trim] [--webp] [--output out.png]');
process.exit(0);
}
(async () => {
let img = sharp(input);
const meta = await img.metadata();
console.log(`输入: ${input} (${meta.width}x${meta.height}, ${meta.format})`);
if (hasFlag('trim')) {
img = img.trim();
console.log(' 裁剪空白边');
}
const resizeW = getArg('resize');
if (resizeW) {
img = img.resize(parseInt(resizeW));
console.log(` 缩放到宽度 ${resizeW}`);
}
const quality = parseInt(getArg('quality') || '85');
const watermark = getArg('watermark');
if (watermark) {
const { width, height } = await img.metadata();
const w = width || meta.width;
const h = height || meta.height;
const fontSize = Math.max(16, Math.floor(w / 40));
const svg = `<svg width="${w}" height="${h}">
<text x="${w - 10}" y="${h - 10}" font-size="${fontSize}" fill="rgba(255,255,255,0.5)"
text-anchor="end" font-family="sans-serif">${watermark}</text>
</svg>`;
img = img.composite([{ input: Buffer.from(svg), gravity: 'southeast' }]);
console.log(` 水印: ${watermark}`);
}
let output = getArg('output') || input;
if (hasFlag('webp')) {
img = img.webp({ quality });
if (!getArg('output')) output = input.replace(/\.\w+$/, '.webp');
console.log(` 转 WebP (quality: ${quality})`);
} else {
img = img.png({ quality: Math.min(quality, 100) });
}
await img.toFile(output === input ? output + '.tmp' : output);
if (output === input) {
const fs = require('fs');
fs.renameSync(output + '.tmp', output);
}
const outMeta = await sharp(output).metadata();
const stats = require('fs').statSync(output);
console.log(`输出: ${output} (${outMeta.width}x${outMeta.height}, ${(stats.size / 1024).toFixed(0)}KB)`);
})();