@playcraft/cli 0.0.53 → 0.0.55
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/dist/commands/create.js +164 -98
- package/dist/index.js +4 -1
- package/dist/scaffold/templates.js +35 -0
- package/package.json +3 -3
- package/project-templates/playable-template-phaser/.playcraftignore +10 -0
- package/project-templates/playable-template-phaser/CLAUDE.md +178 -0
- package/project-templates/playable-template-phaser/PLAYCRAFT-MIGRATION.md +328 -0
- package/project-templates/playable-template-phaser/README.md +381 -0
- package/project-templates/playable-template-phaser/assets/brain.png +0 -0
- package/project-templates/playable-template-phaser/assets/button.png +0 -0
- package/project-templates/playable-template-phaser/assets/interact.png +0 -0
- package/project-templates/playable-template-phaser/assets/logo.webp +0 -0
- package/project-templates/playable-template-phaser/assets/warn_brain.png +0 -0
- package/project-templates/playable-template-phaser/builds.config.js +12 -0
- package/project-templates/playable-template-phaser/globals.d.ts +2 -0
- package/project-templates/playable-template-phaser/index.html +12 -0
- package/project-templates/playable-template-phaser/package.json +45 -0
- package/project-templates/playable-template-phaser/scripts/base64-to-mp3.js +340 -0
- package/project-templates/playable-template-phaser/scripts/convert-to-webp.js +370 -0
- package/project-templates/playable-template-phaser/src/Game.ts +154 -0
- package/project-templates/playable-template-phaser/src/config/LoadingConfig.ts +40 -0
- package/project-templates/playable-template-phaser/src/config/ThemeConfig.ts +81 -0
- package/project-templates/playable-template-phaser/src/index.css +18 -0
- package/project-templates/playable-template-phaser/src/index.html +9 -0
- package/project-templates/playable-template-phaser/src/index.ts +30 -0
- package/project-templates/playable-template-phaser/src/json5.d.ts +4 -0
- package/project-templates/playable-template-phaser/src/managers/AudioManager.ts +559 -0
- package/project-templates/playable-template-phaser/src/scenes/MainScene.ts +301 -0
- package/project-templates/playable-template-phaser/src/scenes/PreloadScene.ts +256 -0
- package/project-templates/playable-template-phaser/src/theme/README.md +194 -0
- package/project-templates/playable-template-phaser/src/theme/blue-theme/assets/.gitkeep +24 -0
- package/project-templates/playable-template-phaser/src/theme/blue-theme/index.ts +27 -0
- package/project-templates/playable-template-phaser/src/theme/blue-theme/theme.data.json5 +19 -0
- package/project-templates/playable-template-phaser/src/theme/green-theme/assets/.gitkeep +3 -0
- package/project-templates/playable-template-phaser/src/theme/green-theme/index.ts +20 -0
- package/project-templates/playable-template-phaser/src/theme/green-theme/theme.data.json5 +19 -0
- package/project-templates/playable-template-phaser/src/theme/index.ts +1 -0
- package/project-templates/playable-template-phaser/src/theme/red-theme/assets/.gitkeep +3 -0
- package/project-templates/playable-template-phaser/src/theme/red-theme/index.ts +20 -0
- package/project-templates/playable-template-phaser/src/theme/red-theme/theme.data.json5 +19 -0
- package/project-templates/playable-template-phaser/src/theme/theme.metadata.md +60 -0
- package/project-templates/playable-template-phaser/src/theme/theme.schema.json5 +149 -0
- package/project-templates/playable-template-phaser/src/ui/BrainAgeUI.ts +681 -0
- package/project-templates/playable-template-phaser/src/ui/CountdownUI.ts +565 -0
- package/project-templates/playable-template-phaser/src/ui/PlayButton.ts +51 -0
- package/project-templates/playable-template-phaser/src/ui/ScoreUI.ts +207 -0
- package/project-templates/playable-template-phaser/src/ui/SuccessUI.ts +43 -0
- package/project-templates/playable-template-phaser/src/ui/index.ts +15 -0
- package/project-templates/playable-template-phaser/tsconfig.json +26 -0
- package/project-templates/playable-template-phaser/vite.config.ts +64 -0
- package/project-templates/playable-template-phaser//346/226/260/346/211/213/345/274/200/345/217/221/346/214/207/345/215/227.md +209 -0
- package/project-templates/playable-template-three/README.md +795 -0
- package/project-templates/playable-template-three/assets/Stork.glb +0 -0
- package/project-templates/playable-template-three/assets/button.png +0 -0
- package/project-templates/playable-template-three/assets/logo.webp +0 -0
- package/project-templates/playable-template-three/build.json +7 -0
- package/project-templates/playable-template-three/builds.config.example.js +151 -0
- package/project-templates/playable-template-three/builds.config.js +158 -0
- package/project-templates/playable-template-three/package.json +36 -0
- package/project-templates/playable-template-three/scripts/convert-to-webp.js +370 -0
- package/project-templates/playable-template-three/scripts/theme-builds.js +514 -0
- package/project-templates/playable-template-three/src/Game.ts +215 -0
- package/project-templates/playable-template-three/src/config/LoadingConfig.ts +27 -0
- package/project-templates/playable-template-three/src/config/ThemeConfig.ts +36 -0
- package/project-templates/playable-template-three/src/global.d.ts +108 -0
- package/project-templates/playable-template-three/src/index.css +18 -0
- package/project-templates/playable-template-three/src/index.html +9 -0
- package/project-templates/playable-template-three/src/index.ts +20 -0
- package/project-templates/playable-template-three/src/managers/AudioManager.ts +538 -0
- package/project-templates/playable-template-three/src/scenes/PreloadScene.ts +305 -0
- package/project-templates/playable-template-three/src/theme/README.md +225 -0
- package/project-templates/playable-template-three/src/theme/blue-theme/assets/.gitkeep +28 -0
- package/project-templates/playable-template-three/src/theme/blue-theme/index.ts +45 -0
- package/project-templates/playable-template-three/src/theme/current.ts +12 -0
- package/project-templates/playable-template-three/src/theme/green-theme/assets/.gitkeep +3 -0
- package/project-templates/playable-template-three/src/theme/green-theme/index.ts +34 -0
- package/project-templates/playable-template-three/src/theme/red-theme/assets/.gitkeep +3 -0
- package/project-templates/playable-template-three/src/theme/red-theme/index.ts +34 -0
- package/project-templates/playable-template-three/tsconfig.json +24 -0
|
@@ -0,0 +1,370 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 图片转 WebP 格式脚本
|
|
3
|
+
*
|
|
4
|
+
* 使用方法:
|
|
5
|
+
* node scripts/convert-to-webp.js // 默认质量 100,保留原图
|
|
6
|
+
* node scripts/convert-to-webp.js 90 // 质量 90
|
|
7
|
+
* node scripts/convert-to-webp.js --quality=80 // 质量 80
|
|
8
|
+
* node scripts/convert-to-webp.js -q 90 --delete-original // 质量 90,删除原图
|
|
9
|
+
* node scripts/convert-to-webp.js --dry-run // 预览模式
|
|
10
|
+
* node scripts/convert-to-webp.js --force // 强制覆盖
|
|
11
|
+
* node scripts/convert-to-webp.js --dir=./public/images // 指定目录
|
|
12
|
+
* node scripts/convert-to-webp.js --help // 显示帮助
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
const webp = require('webp-converter');
|
|
16
|
+
const fs = require('fs');
|
|
17
|
+
const path = require('path');
|
|
18
|
+
const readline = require('readline');
|
|
19
|
+
|
|
20
|
+
// 授予权限
|
|
21
|
+
webp.grant_permission();
|
|
22
|
+
|
|
23
|
+
// 默认配置
|
|
24
|
+
const DEFAULT_CONFIG = {
|
|
25
|
+
dir: './src/assets',
|
|
26
|
+
quality: 100,
|
|
27
|
+
deleteOriginal: false,
|
|
28
|
+
force: false,
|
|
29
|
+
isDryRun: false
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const SUPPORTED_EXTENSIONS = ['.png', '.jpg', '.jpeg', '.jfif'];
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* 解析命令行参数
|
|
36
|
+
*/
|
|
37
|
+
function parseArgs() {
|
|
38
|
+
const args = process.argv.slice(2);
|
|
39
|
+
const config = { ...DEFAULT_CONFIG };
|
|
40
|
+
|
|
41
|
+
// 第一个参数如果是数字,视为质量值
|
|
42
|
+
if (args[0] && !args[0].startsWith('-') && !isNaN(args[0])) {
|
|
43
|
+
config.quality = parseInt(args[0]);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
args.forEach((arg, index) => {
|
|
47
|
+
if (arg === '--dry-run') {
|
|
48
|
+
config.isDryRun = true;
|
|
49
|
+
} else if (arg === '--delete-original' || arg === '-d') {
|
|
50
|
+
config.deleteOriginal = true;
|
|
51
|
+
} else if (arg === '--force' || arg === '-f') {
|
|
52
|
+
config.force = true;
|
|
53
|
+
} else if (arg.startsWith('--quality=')) {
|
|
54
|
+
config.quality = parseInt(arg.split('=')[1]) || DEFAULT_CONFIG.quality;
|
|
55
|
+
} else if (arg === '-q' || arg === '--quality') {
|
|
56
|
+
const nextArg = args[index + 1];
|
|
57
|
+
if (nextArg && !isNaN(nextArg)) {
|
|
58
|
+
config.quality = parseInt(nextArg);
|
|
59
|
+
}
|
|
60
|
+
} else if (arg.startsWith('--dir=')) {
|
|
61
|
+
config.dir = arg.split('=')[1];
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
// 质量范围检查
|
|
66
|
+
if (config.quality < 0 || config.quality > 100) {
|
|
67
|
+
console.error('❌ 质量参数必须在 0-100 之间');
|
|
68
|
+
process.exit(1);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return config;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// 统计信息
|
|
75
|
+
let stats = {
|
|
76
|
+
total: 0,
|
|
77
|
+
success: 0,
|
|
78
|
+
failed: 0,
|
|
79
|
+
skipped: 0,
|
|
80
|
+
deleted: 0,
|
|
81
|
+
originalSize: 0,
|
|
82
|
+
convertedSize: 0
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* 递归获取目录下所有图片文件
|
|
87
|
+
*/
|
|
88
|
+
function getAllImageFiles(dir, fileList = []) {
|
|
89
|
+
if (!fs.existsSync(dir)) {
|
|
90
|
+
return fileList;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const files = fs.readdirSync(dir);
|
|
94
|
+
|
|
95
|
+
files.forEach(file => {
|
|
96
|
+
const filePath = path.join(dir, file);
|
|
97
|
+
const stat = fs.statSync(filePath);
|
|
98
|
+
|
|
99
|
+
if (stat.isDirectory()) {
|
|
100
|
+
getAllImageFiles(filePath, fileList);
|
|
101
|
+
} else {
|
|
102
|
+
const ext = path.extname(file).toLowerCase();
|
|
103
|
+
if (SUPPORTED_EXTENSIONS.includes(ext)) {
|
|
104
|
+
fileList.push(filePath);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
return fileList;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* 格式化文件大小
|
|
114
|
+
*/
|
|
115
|
+
function formatSize(bytes) {
|
|
116
|
+
if (bytes === 0) return '0 B';
|
|
117
|
+
const k = 1024;
|
|
118
|
+
const sizes = ['B', 'KB', 'MB', 'GB'];
|
|
119
|
+
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
120
|
+
return (bytes / Math.pow(k, i)).toFixed(2) + ' ' + sizes[i];
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* 询问用户确认
|
|
125
|
+
*/
|
|
126
|
+
function askConfirmation(question) {
|
|
127
|
+
const rl = readline.createInterface({
|
|
128
|
+
input: process.stdin,
|
|
129
|
+
output: process.stdout
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
return new Promise(resolve => {
|
|
133
|
+
rl.question(question, answer => {
|
|
134
|
+
rl.close();
|
|
135
|
+
resolve(answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes');
|
|
136
|
+
});
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* 转换单个图片
|
|
142
|
+
*/
|
|
143
|
+
async function convertImage(inputPath, config) {
|
|
144
|
+
const outputPath = inputPath.replace(/\.(png|jpg|jpeg|jfif)$/i, '.webp');
|
|
145
|
+
|
|
146
|
+
// 如果已经是 webp 格式,跳过
|
|
147
|
+
if (inputPath.endsWith('.webp')) {
|
|
148
|
+
console.log(`⏭️ 跳过: ${inputPath} (已是 WebP 格式)`);
|
|
149
|
+
stats.skipped++;
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// 如果输出文件已存在,直接覆盖
|
|
154
|
+
if (fs.existsSync(outputPath)) {
|
|
155
|
+
if (config.isDryRun) {
|
|
156
|
+
console.log(`🔍 [预览] 覆盖: ${outputPath}`);
|
|
157
|
+
} else {
|
|
158
|
+
console.log(`⚠️ 覆盖: ${outputPath}`);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
try {
|
|
163
|
+
const originalSize = fs.statSync(inputPath).size;
|
|
164
|
+
stats.originalSize += originalSize;
|
|
165
|
+
|
|
166
|
+
if (config.isDryRun) {
|
|
167
|
+
console.log(`🔍 [预览] ${inputPath} -> ${outputPath}`);
|
|
168
|
+
console.log(` 原始大小: ${formatSize(originalSize)}`);
|
|
169
|
+
if (config.deleteOriginal) {
|
|
170
|
+
console.log(` ⚠️ 将删除原图`);
|
|
171
|
+
}
|
|
172
|
+
stats.total++;
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
console.log(`🔄 转换中: ${inputPath}`);
|
|
177
|
+
|
|
178
|
+
// 转换图片
|
|
179
|
+
const result = await webp.cwebp(
|
|
180
|
+
inputPath,
|
|
181
|
+
outputPath,
|
|
182
|
+
`-q ${config.quality}`,
|
|
183
|
+
'-v'
|
|
184
|
+
);
|
|
185
|
+
|
|
186
|
+
if (result) {
|
|
187
|
+
const convertedSize = fs.statSync(outputPath).size;
|
|
188
|
+
stats.convertedSize += convertedSize;
|
|
189
|
+
|
|
190
|
+
const ratio = ((1 - convertedSize / originalSize) * 100).toFixed(2);
|
|
191
|
+
const sizeChange = ratio > 0 ? '压缩' : '增加';
|
|
192
|
+
console.log(`✅ 成功: ${outputPath}`);
|
|
193
|
+
console.log(` ${formatSize(originalSize)} -> ${formatSize(convertedSize)} (${sizeChange} ${Math.abs(ratio)}%)`);
|
|
194
|
+
|
|
195
|
+
// 删除原图
|
|
196
|
+
if (config.deleteOriginal) {
|
|
197
|
+
try {
|
|
198
|
+
fs.unlinkSync(inputPath);
|
|
199
|
+
console.log(`🗑️ 已删除原图: ${inputPath}`);
|
|
200
|
+
stats.deleted++;
|
|
201
|
+
} catch (deleteError) {
|
|
202
|
+
console.error(`❌ 删除失败: ${inputPath}`);
|
|
203
|
+
console.error(` 错误: ${deleteError.message}`);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
stats.success++;
|
|
208
|
+
}
|
|
209
|
+
} catch (error) {
|
|
210
|
+
console.error(`❌ 失败: ${inputPath}`);
|
|
211
|
+
console.error(` 错误: ${error.message}`);
|
|
212
|
+
stats.failed++;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
stats.total++;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* 显示帮助信息
|
|
220
|
+
*/
|
|
221
|
+
function showHelp() {
|
|
222
|
+
console.log(`
|
|
223
|
+
📸 图片转 WebP 格式工具
|
|
224
|
+
|
|
225
|
+
使用方法:
|
|
226
|
+
node scripts/convert-to-webp.js [质量值] [选项]
|
|
227
|
+
|
|
228
|
+
参数:
|
|
229
|
+
质量值 设置转换质量 0-100 (默认: 100)
|
|
230
|
+
示例: node scripts/convert-to-webp.js 90
|
|
231
|
+
|
|
232
|
+
选项:
|
|
233
|
+
--quality=<0-100>, -q <0-100> 设置转换质量 (默认: 100)
|
|
234
|
+
--delete-original, -d 转换后删除原图 ⚠️
|
|
235
|
+
--force, -f 强制覆盖已存在的文件,不询问
|
|
236
|
+
--dry-run 预览模式,不实际转换
|
|
237
|
+
--dir=<路径> 指定源目录 (默认: ./src/assets)
|
|
238
|
+
--help, -h 显示此帮助信息
|
|
239
|
+
|
|
240
|
+
使用示例:
|
|
241
|
+
# 基础用法
|
|
242
|
+
node scripts/convert-to-webp.js # 质量 100
|
|
243
|
+
node scripts/convert-to-webp.js 90 # 质量 90
|
|
244
|
+
node scripts/convert-to-webp.js --quality=80 # 质量 80
|
|
245
|
+
|
|
246
|
+
# 预览模式(推荐首次使用)
|
|
247
|
+
node scripts/convert-to-webp.js --dry-run # 预览
|
|
248
|
+
node scripts/convert-to-webp.js 90 --dry-run # 质量 90 预览
|
|
249
|
+
|
|
250
|
+
# 删除原图(谨慎使用)
|
|
251
|
+
node scripts/convert-to-webp.js 90 --delete-original
|
|
252
|
+
node scripts/convert-to-webp.js -q 85 -d
|
|
253
|
+
|
|
254
|
+
# 强制覆盖
|
|
255
|
+
node scripts/convert-to-webp.js --force # 不询问直接覆盖
|
|
256
|
+
node scripts/convert-to-webp.js 80 -f -d # 组合使用
|
|
257
|
+
|
|
258
|
+
# 指定目录
|
|
259
|
+
node scripts/convert-to-webp.js --dir=./public/images
|
|
260
|
+
node scripts/convert-to-webp.js --dir=./assets -q 90
|
|
261
|
+
|
|
262
|
+
NPM 快捷命令:
|
|
263
|
+
npm run webp # 质量 100
|
|
264
|
+
npm run webp:preview # 预览模式
|
|
265
|
+
npm run webp:80 # 质量 80
|
|
266
|
+
npm run webp:90 # 质量 90
|
|
267
|
+
|
|
268
|
+
质量推荐:
|
|
269
|
+
100 最高质量,文件较大 - 适合重要图标、UI 元素
|
|
270
|
+
90 极佳质量,推荐使用 - 适合游戏角色、场景图
|
|
271
|
+
80 优秀质量,文件较小 - 适合背景图、装饰图
|
|
272
|
+
70 良好质量,大幅压缩 - 适合缩略图、临时图片
|
|
273
|
+
|
|
274
|
+
注意事项:
|
|
275
|
+
⚠️ 使用 --delete-original 前请确保备份
|
|
276
|
+
💡 首次使用建议先运行 --dry-run 预览
|
|
277
|
+
📦 支持格式: .png, .jpg, .jpeg, .jfif
|
|
278
|
+
`);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* 主函数
|
|
283
|
+
*/
|
|
284
|
+
async function main() {
|
|
285
|
+
// 显示帮助
|
|
286
|
+
if (process.argv.includes('--help') || process.argv.includes('-h')) {
|
|
287
|
+
showHelp();
|
|
288
|
+
process.exit(0);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
const config = parseArgs();
|
|
292
|
+
|
|
293
|
+
console.log('='.repeat(60));
|
|
294
|
+
console.log('📸 图片转 WebP 格式工具');
|
|
295
|
+
console.log('='.repeat(60));
|
|
296
|
+
console.log(`📁 源目录: ${config.dir}`);
|
|
297
|
+
console.log(`🎨 质量: ${config.quality}%`);
|
|
298
|
+
console.log(`🔧 模式: ${config.isDryRun ? '预览模式(不会实际转换)' : '转换模式'}`);
|
|
299
|
+
console.log(`🗑️ 删除原图: ${config.deleteOriginal ? '是 ⚠️' : '否'}`);
|
|
300
|
+
console.log(`⚡ 强制覆盖: ${config.force ? '是' : '否'}`);
|
|
301
|
+
console.log('='.repeat(60));
|
|
302
|
+
console.log('');
|
|
303
|
+
|
|
304
|
+
// 检查源目录是否存在
|
|
305
|
+
if (!fs.existsSync(config.dir)) {
|
|
306
|
+
console.error(`❌ 错误: 目录 ${config.dir} 不存在`);
|
|
307
|
+
process.exit(1);
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
// 获取所有图片文件
|
|
311
|
+
console.log('🔍 扫描图片文件...');
|
|
312
|
+
const imageFiles = getAllImageFiles(config.dir);
|
|
313
|
+
|
|
314
|
+
if (imageFiles.length === 0) {
|
|
315
|
+
console.log('⚠️ 未找到支持的图片文件');
|
|
316
|
+
console.log(` 支持格式: ${SUPPORTED_EXTENSIONS.join(', ')}`);
|
|
317
|
+
process.exit(0);
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
console.log(`📝 找到 ${imageFiles.length} 个图片文件\n`);
|
|
321
|
+
|
|
322
|
+
// 如果要删除原图且不是预览模式,要求确认
|
|
323
|
+
if (config.deleteOriginal && !config.isDryRun && !config.force) {
|
|
324
|
+
console.log('⚠️ 警告: 您选择了删除原图选项!');
|
|
325
|
+
const confirmed = await askConfirmation('确定要继续吗? (y/n): ');
|
|
326
|
+
if (!confirmed) {
|
|
327
|
+
console.log('❌ 已取消操作');
|
|
328
|
+
process.exit(0);
|
|
329
|
+
}
|
|
330
|
+
console.log('');
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// 逐个转换
|
|
334
|
+
for (const imagePath of imageFiles) {
|
|
335
|
+
await convertImage(imagePath, config);
|
|
336
|
+
console.log('');
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
// 输出统计信息
|
|
340
|
+
console.log('='.repeat(60));
|
|
341
|
+
console.log('📊 转换统计');
|
|
342
|
+
console.log('='.repeat(60));
|
|
343
|
+
console.log(`总计: ${stats.total} 个文件`);
|
|
344
|
+
console.log(`成功: ${stats.success} 个`);
|
|
345
|
+
console.log(`失败: ${stats.failed} 个`);
|
|
346
|
+
console.log(`跳过: ${stats.skipped} 个`);
|
|
347
|
+
|
|
348
|
+
if (config.deleteOriginal && !config.isDryRun) {
|
|
349
|
+
console.log(`删除: ${stats.deleted} 个原图`);
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
if (!config.isDryRun && stats.success > 0) {
|
|
353
|
+
console.log('');
|
|
354
|
+
console.log(`原始总大小: ${formatSize(stats.originalSize)}`);
|
|
355
|
+
console.log(`转换后总大小: ${formatSize(stats.convertedSize)}`);
|
|
356
|
+
const totalRatio = ((1 - stats.convertedSize / stats.originalSize) * 100).toFixed(2);
|
|
357
|
+
console.log(`总体压缩率: ${totalRatio}%`);
|
|
358
|
+
console.log(`节省空间: ${formatSize(stats.originalSize - stats.convertedSize)}`);
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
console.log('='.repeat(60));
|
|
362
|
+
console.log(config.isDryRun ? '🔍 预览完成!' : '✨ 转换完成!');
|
|
363
|
+
console.log('='.repeat(60));
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
// 运行
|
|
367
|
+
main().catch(error => {
|
|
368
|
+
console.error('❌ 发生错误:', error);
|
|
369
|
+
process.exit(1);
|
|
370
|
+
});
|