ai-yuca 1.4.7 → 1.4.9

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/bin/cli.js CHANGED
@@ -47,6 +47,7 @@ const deploy_1 = require("../src/deploy");
47
47
  const transKey_1 = require("../src/transKey");
48
48
  const useTrans_1 = require("../src/useTrans");
49
49
  const tinify_1 = require("../src/tinify");
50
+ const zipAnim_1 = require("../src/zipAnim");
50
51
  const pullCrowdin_1 = require("../src/pullCrowdin");
51
52
  const sharp_1 = require("../src/sharp");
52
53
  const program = new commander_1.Command();
@@ -85,6 +86,16 @@ program
85
86
  .action(async (options) => {
86
87
  await (0, tinify_1.compressImagesWithTinify)(options);
87
88
  });
89
+ // 添加 zip-anim 命令
90
+ program
91
+ .command('zip-anim')
92
+ .description('提取JSON文件中的base64位图,压缩后替换原位置')
93
+ .requiredOption('-s, --source <path>', 'JSON文件路径 (相对路径)')
94
+ .option('-k, --key <string>', 'Tinify API Key (如果不提供则尝试从配置文件读取)')
95
+ .option('-c, --config <path>', '指定配置文件路径(默认为项目根目录下的vs.config.json)')
96
+ .action(async (options) => {
97
+ await (0, zipAnim_1.zipAnim)(options);
98
+ });
88
99
  // 添加deploy命令
89
100
  program
90
101
  .command('deploy')
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ai-yuca",
3
- "version": "1.4.7",
3
+ "version": "1.4.9",
4
4
  "description": "一个用AI生成的开发辅助工具",
5
5
  "main": "dist/src/index.js",
6
6
  "types": "dist/src/index.d.ts",
@@ -10,3 +10,4 @@ export * from './deploy';
10
10
  export * from './pullCrowdin';
11
11
  export * from './sharp';
12
12
  export * from './tinify';
13
+ export * from './zipAnim';
@@ -34,3 +34,4 @@ __exportStar(require("./pullCrowdin"), exports);
34
34
  __exportStar(require("./sharp"), exports);
35
35
  // 导出所有tinify相关类型
36
36
  __exportStar(require("./tinify"), exports);
37
+ __exportStar(require("./zipAnim"), exports);
@@ -0,0 +1,5 @@
1
+ export interface ZipAnimCommandOptions {
2
+ source: string;
3
+ config?: string;
4
+ key?: string;
5
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,2 @@
1
+ import { ZipAnimCommandOptions } from './types/zipAnim';
2
+ export declare function zipAnim(options: ZipAnimCommandOptions): Promise<void>;
@@ -0,0 +1,208 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.zipAnim = zipAnim;
40
+ const tinify_1 = __importDefault(require("tinify"));
41
+ const fs = __importStar(require("fs"));
42
+ const path = __importStar(require("path"));
43
+ const chalk_1 = __importDefault(require("chalk"));
44
+ async function zipAnim(options) {
45
+ const { source, config, key } = options;
46
+ if (!source) {
47
+ console.error(chalk_1.default.red('错误: 必须提供源文件路径 (-s)'));
48
+ process.exit(1);
49
+ }
50
+ // 获取绝对路径
51
+ const sourceAbs = path.resolve(process.cwd(), source);
52
+ if (!fs.existsSync(sourceAbs)) {
53
+ console.error(chalk_1.default.red(`错误: 源文件不存在: ${sourceAbs}`));
54
+ process.exit(1);
55
+ }
56
+ // 获取 API Key
57
+ let apiKey = key;
58
+ if (!apiKey) {
59
+ // 尝试从配置文件读取
60
+ const configPath = config || path.join(process.cwd(), 'vs.config.json');
61
+ if (fs.existsSync(configPath)) {
62
+ try {
63
+ const configData = JSON.parse(fs.readFileSync(configPath, 'utf8'));
64
+ if (configData.apiKeys && configData.apiKeys.tinify) {
65
+ apiKey = configData.apiKeys.tinify;
66
+ }
67
+ else if (configData.tinify) {
68
+ apiKey = configData.tinify;
69
+ }
70
+ }
71
+ catch (e) {
72
+ console.error(chalk_1.default.red(`错误: 读取配置文件失败: ${e}`));
73
+ }
74
+ }
75
+ else if (config) {
76
+ console.error(chalk_1.default.red(`错误: 配置文件不存在: ${configPath}`));
77
+ process.exit(1);
78
+ }
79
+ }
80
+ if (!apiKey) {
81
+ console.error(chalk_1.default.red('错误: 未提供 API Key。请通过 -k 参数或配置文件 (apiKeys.tinify) 提供。'));
82
+ process.exit(1);
83
+ }
84
+ tinify_1.default.key = apiKey;
85
+ console.log(chalk_1.default.blue(`读取 JSON 文件: ${sourceAbs}`));
86
+ let jsonData;
87
+ try {
88
+ jsonData = JSON.parse(fs.readFileSync(sourceAbs, 'utf8'));
89
+ }
90
+ catch (e) {
91
+ console.error(chalk_1.default.red(`错误: 解析 JSON 失败: ${e}`));
92
+ process.exit(1);
93
+ }
94
+ // 查找 base64 图片
95
+ const images = [];
96
+ findBase64Images(jsonData, [], images);
97
+ if (images.length === 0) {
98
+ console.log(chalk_1.default.yellow('未找到任何 Base64 图片。'));
99
+ return;
100
+ }
101
+ console.log(chalk_1.default.blue(`找到 ${images.length} 张 Base64 图片,开始压缩...`));
102
+ let successCount = 0;
103
+ let failCount = 0;
104
+ let totalSavedBytes = 0;
105
+ for (let i = 0; i < images.length; i++) {
106
+ const img = images[i];
107
+ console.log(`正在压缩图片 ${i + 1}/${images.length}...`);
108
+ try {
109
+ // 将 base64 转为 buffer
110
+ const buffer = Buffer.from(img.data, 'base64');
111
+ const originalSize = buffer.length;
112
+ if (originalSize === 0) {
113
+ console.log(chalk_1.default.yellow(`⚠ 图片 ${i + 1}: Base64 数据无效或为空`));
114
+ failCount++;
115
+ continue;
116
+ }
117
+ // 修复: tinify.fromBuffer 可能对 Buffer 对象的检查比较严格
118
+ // 或者是因为 tinify 库本身的问题
119
+ // 尝试使用 Uint8Array 包装一下
120
+ const uint8Array = new Uint8Array(buffer);
121
+ // 压缩
122
+ const source = tinify_1.default.fromBuffer(buffer);
123
+ const resultData = await source.toBuffer();
124
+ const compressedSize = resultData.length;
125
+ // 计算节省空间
126
+ const savedBytes = originalSize - compressedSize;
127
+ // 如果压缩后反而变大(极少情况,或者本身就很小),则不替换
128
+ if (savedBytes <= 0) {
129
+ console.log(chalk_1.default.yellow(`⚠ 图片 ${i + 1}: 压缩后未减小,保持原样 (${(originalSize / 1024).toFixed(2)}KB)`));
130
+ failCount++; // 算作未优化
131
+ continue;
132
+ }
133
+ // 转换回 base64
134
+ const compressedBase64 = Buffer.from(resultData).toString('base64');
135
+ // 更新 JSON 数据
136
+ // 构造完整的前缀
137
+ const newValue = `data:${img.mimeType};base64,${compressedBase64}`;
138
+ updateJsonValue(jsonData, img.path, newValue);
139
+ totalSavedBytes += savedBytes;
140
+ const savedPercent = (savedBytes / originalSize * 100).toFixed(2);
141
+ console.log(chalk_1.default.green(`✓ 图片 ${i + 1}: ${(originalSize / 1024).toFixed(2)}KB -> ${(compressedSize / 1024).toFixed(2)}KB (节省 ${savedPercent}%)`));
142
+ successCount++;
143
+ }
144
+ catch (err) {
145
+ console.error(chalk_1.default.red(`✗ 图片 ${i + 1} 压缩失败: ${err.message}`));
146
+ if (err instanceof tinify_1.default.AccountError) {
147
+ console.error(chalk_1.default.red("API Key 验证失败或超出配额。请检查您的 API Key。"));
148
+ break;
149
+ }
150
+ failCount++;
151
+ }
152
+ }
153
+ // 生成新文件名
154
+ const dir = path.dirname(sourceAbs);
155
+ const ext = path.extname(sourceAbs);
156
+ const name = path.basename(sourceAbs, ext);
157
+ // 输出文件名改为 name_min.json
158
+ const outputAbs = path.join(dir, `${name}_min${ext}`);
159
+ try {
160
+ // 写入文件
161
+ fs.writeFileSync(outputAbs, JSON.stringify(jsonData, null, 2), 'utf8');
162
+ console.log(chalk_1.default.blue(`\n处理完成! 成功: ${successCount}, 失败/跳过: ${failCount}`));
163
+ if (totalSavedBytes > 0) {
164
+ console.log(chalk_1.default.blue(`共节省空间: ${(totalSavedBytes / 1024).toFixed(2)}KB`));
165
+ }
166
+ console.log(chalk_1.default.green(`新文件已保存至: ${outputAbs}`));
167
+ }
168
+ catch (e) {
169
+ console.error(chalk_1.default.red(`保存文件失败: ${e}`));
170
+ }
171
+ }
172
+ function findBase64Images(obj, currentPath, results) {
173
+ if (obj === null || typeof obj !== 'object') {
174
+ if (typeof obj === 'string') {
175
+ // 检查是否是 base64 图片
176
+ // 匹配 data:image/png;base64,... 格式
177
+ // 常见的 MIME 类型: image/png, image/jpeg, image/jpg, image/webp
178
+ const match = obj.match(/^data:(image\/(?:png|jpeg|jpg|webp));base64,(.+)$/);
179
+ if (match) {
180
+ results.push({
181
+ path: [...currentPath],
182
+ mimeType: match[1],
183
+ data: match[2]
184
+ });
185
+ }
186
+ }
187
+ return;
188
+ }
189
+ if (Array.isArray(obj)) {
190
+ for (let i = 0; i < obj.length; i++) {
191
+ findBase64Images(obj[i], [...currentPath, i], results);
192
+ }
193
+ }
194
+ else {
195
+ for (const key in obj) {
196
+ if (Object.prototype.hasOwnProperty.call(obj, key)) {
197
+ findBase64Images(obj[key], [...currentPath, key], results);
198
+ }
199
+ }
200
+ }
201
+ }
202
+ function updateJsonValue(obj, pathArr, value) {
203
+ let current = obj;
204
+ for (let i = 0; i < pathArr.length - 1; i++) {
205
+ current = current[pathArr[i]];
206
+ }
207
+ current[pathArr[pathArr.length - 1]] = value;
208
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ai-yuca",
3
- "version": "1.4.7",
3
+ "version": "1.4.9",
4
4
  "description": "一个用AI生成的开发辅助工具",
5
5
  "main": "dist/src/index.js",
6
6
  "types": "dist/src/index.d.ts",