koishi-plugin-imgdraw-selfuse 0.0.7 → 0.0.8
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/lib/index.d.ts +10 -0
- package/lib/index.js +90 -7
- package/package.json +1 -1
package/lib/index.d.ts
CHANGED
|
@@ -26,6 +26,11 @@ export declare const Config: Schema<Schemastery.ObjectS<{
|
|
|
26
26
|
txt2imgModel: Schema<string, string>;
|
|
27
27
|
img2imgModel: Schema<string, string>;
|
|
28
28
|
maxImages: Schema<number, number>;
|
|
29
|
+
enableImgCompress: Schema<boolean, boolean>;
|
|
30
|
+
imgMaxWidth: Schema<number, number>;
|
|
31
|
+
imgMaxHeight: Schema<number, number>;
|
|
32
|
+
imgQuality: Schema<number, number>;
|
|
33
|
+
imgMaxFileSize: Schema<number, number>;
|
|
29
34
|
apiList: Schema<Schemastery.ObjectS<{
|
|
30
35
|
enable: Schema<boolean, boolean>;
|
|
31
36
|
apiKey: Schema<string, string>;
|
|
@@ -105,6 +110,11 @@ export declare const Config: Schema<Schemastery.ObjectS<{
|
|
|
105
110
|
txt2imgModel: Schema<string, string>;
|
|
106
111
|
img2imgModel: Schema<string, string>;
|
|
107
112
|
maxImages: Schema<number, number>;
|
|
113
|
+
enableImgCompress: Schema<boolean, boolean>;
|
|
114
|
+
imgMaxWidth: Schema<number, number>;
|
|
115
|
+
imgMaxHeight: Schema<number, number>;
|
|
116
|
+
imgQuality: Schema<number, number>;
|
|
117
|
+
imgMaxFileSize: Schema<number, number>;
|
|
108
118
|
apiList: Schema<Schemastery.ObjectS<{
|
|
109
119
|
enable: Schema<boolean, boolean>;
|
|
110
120
|
apiKey: Schema<string, string>;
|
package/lib/index.js
CHANGED
|
@@ -10,6 +10,14 @@ const axios_1 = __importDefault(require("axios"));
|
|
|
10
10
|
const yaml_1 = __importDefault(require("yaml"));
|
|
11
11
|
const fs_1 = __importDefault(require("fs"));
|
|
12
12
|
const path_1 = __importDefault(require("path"));
|
|
13
|
+
// 尝试导入 sharp,如果未安装则给出提示
|
|
14
|
+
let sharp;
|
|
15
|
+
try {
|
|
16
|
+
sharp = require('sharp');
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
sharp = null;
|
|
20
|
+
}
|
|
13
21
|
exports.name = 'ai-image';
|
|
14
22
|
exports.inject = {
|
|
15
23
|
required: ['console', 'i18n', 'database'],
|
|
@@ -30,6 +38,13 @@ exports.Config = koishi_1.Schema.object({
|
|
|
30
38
|
txt2imgModel: koishi_1.Schema.string().default('').description('文生图专用模型,留空则使用通用模型'),
|
|
31
39
|
img2imgModel: koishi_1.Schema.string().default('').description('图生图专用模型,留空则使用通用模型'),
|
|
32
40
|
maxImages: koishi_1.Schema.number().default(5).description('图生图最大支持图片数量'),
|
|
41
|
+
// ==================== 新增:图片压缩配置 ====================
|
|
42
|
+
enableImgCompress: koishi_1.Schema.boolean().default(true).description('启用图生图图片压缩(推荐开启,可防止大图超时)'),
|
|
43
|
+
imgMaxWidth: koishi_1.Schema.number().default(1536).description('图片压缩最大宽度(像素)'),
|
|
44
|
+
imgMaxHeight: koishi_1.Schema.number().default(1536).description('图片压缩最大高度(像素)'),
|
|
45
|
+
imgQuality: koishi_1.Schema.number().default(85).description('JPEG 压缩质量 1-100(越高越清晰,建议 80-90)'),
|
|
46
|
+
imgMaxFileSize: koishi_1.Schema.number().default(3).description('图片最大体积(MB),超过会进一步压缩'),
|
|
47
|
+
// ==========================================================
|
|
33
48
|
apiList: koishi_1.Schema.array(koishi_1.Schema.object({
|
|
34
49
|
enable: koishi_1.Schema.boolean().default(true).description('启用此 API'),
|
|
35
50
|
apiKey: koishi_1.Schema.string().description('API Key'),
|
|
@@ -74,6 +89,11 @@ exports.Config = koishi_1.Schema.object({
|
|
|
74
89
|
// ==================== 主函数 ====================
|
|
75
90
|
async function apply(ctx, cfg) {
|
|
76
91
|
const debug = cfg.debug;
|
|
92
|
+
// 检查 sharp 是否安装
|
|
93
|
+
if (cfg.enableImgCompress && !sharp) {
|
|
94
|
+
logger.warn('图片压缩已启用,但未检测到 sharp 库。请运行:npm install sharp');
|
|
95
|
+
logger.warn('在未安装 sharp 的情况下,将跳过压缩,可能导致大图超时');
|
|
96
|
+
}
|
|
77
97
|
// 加载本地化文件
|
|
78
98
|
try {
|
|
79
99
|
const loc = path_1.default.join(__dirname, 'locales', 'zh-CN.yml');
|
|
@@ -126,11 +146,11 @@ async function apply(ctx, cfg) {
|
|
|
126
146
|
if (!str)
|
|
127
147
|
return '';
|
|
128
148
|
// 1. 清理标准 HTML 标签
|
|
129
|
-
let cleaned = str.replace(
|
|
149
|
+
let cleaned = str.replace(/<[^>]+>/g, ' ');
|
|
130
150
|
// 2. 清理 QQ XML 图片标签(如 <img src="..." file="..."/>)
|
|
131
|
-
cleaned = cleaned.replace(
|
|
151
|
+
cleaned = cleaned.replace(/<img\s+[^>]+\/>/gi, ' ');
|
|
132
152
|
// 3. 清理其他 XML 标签
|
|
133
|
-
cleaned = cleaned.replace(
|
|
153
|
+
cleaned = cleaned.replace(/<[^>]+>/g, ' ');
|
|
134
154
|
// 4. 清理多余空格和换行
|
|
135
155
|
cleaned = cleaned.replace(/\s+/g, ' ').trim();
|
|
136
156
|
return cleaned;
|
|
@@ -153,11 +173,74 @@ async function apply(ctx, cfg) {
|
|
|
153
173
|
return markdownMatch[1];
|
|
154
174
|
return null;
|
|
155
175
|
}
|
|
156
|
-
// ====================
|
|
176
|
+
// ==================== 新增:图片压缩函数 ====================
|
|
177
|
+
async function compressImage(buffer) {
|
|
178
|
+
if (!sharp || !cfg.enableImgCompress) {
|
|
179
|
+
return buffer;
|
|
180
|
+
}
|
|
181
|
+
try {
|
|
182
|
+
let image = sharp(buffer);
|
|
183
|
+
const metadata = await image.metadata();
|
|
184
|
+
// 计算缩放比例
|
|
185
|
+
let width = metadata.width || cfg.imgMaxWidth;
|
|
186
|
+
let height = metadata.height || cfg.imgMaxHeight;
|
|
187
|
+
const ratio = Math.min(cfg.imgMaxWidth / width, cfg.imgMaxHeight / height, 1 // 不放大
|
|
188
|
+
);
|
|
189
|
+
if (ratio < 1) {
|
|
190
|
+
width = Math.round(width * ratio);
|
|
191
|
+
height = Math.round(height * ratio);
|
|
192
|
+
image = image.resize(width, height, { fit: 'inside' });
|
|
193
|
+
}
|
|
194
|
+
// 压缩并转换格式
|
|
195
|
+
let quality = Math.max(1, Math.min(100, cfg.imgQuality));
|
|
196
|
+
let compressed = await image
|
|
197
|
+
.jpeg({ quality, progressive: true, mozjpeg: true })
|
|
198
|
+
.toBuffer();
|
|
199
|
+
// 如果还超过限制,进一步降低质量
|
|
200
|
+
const maxBytes = cfg.imgMaxFileSize * 1024 * 1024;
|
|
201
|
+
while (compressed.length > maxBytes && quality > 40) {
|
|
202
|
+
quality -= 5;
|
|
203
|
+
compressed = await image
|
|
204
|
+
.jpeg({ quality, progressive: true, mozjpeg: true })
|
|
205
|
+
.toBuffer();
|
|
206
|
+
}
|
|
207
|
+
if (debug) {
|
|
208
|
+
logger.info(`图片压缩: ${buffer.length} -> ${compressed.length} bytes ` +
|
|
209
|
+
`(${Math.round(compressed.length / buffer.length * 100)}%), ` +
|
|
210
|
+
`尺寸: ${Math.round(width)}x${Math.round(height)}, 质量: ${quality}`);
|
|
211
|
+
}
|
|
212
|
+
return compressed;
|
|
213
|
+
}
|
|
214
|
+
catch (e) {
|
|
215
|
+
logger.error('图片压缩失败,使用原图', e);
|
|
216
|
+
return buffer;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
async function compressBase64Image(base64Url) {
|
|
220
|
+
if (!sharp || !cfg.enableImgCompress) {
|
|
221
|
+
return base64Url;
|
|
222
|
+
}
|
|
223
|
+
try {
|
|
224
|
+
const base64Data = base64Url.split(',')[1];
|
|
225
|
+
const buffer = Buffer.from(base64Data, 'base64');
|
|
226
|
+
const compressed = await compressImage(buffer);
|
|
227
|
+
return `data:image/jpeg;base64,${compressed.toString('base64')}`;
|
|
228
|
+
}
|
|
229
|
+
catch (e) {
|
|
230
|
+
logger.error('base64 图片压缩失败,使用原图', e);
|
|
231
|
+
return base64Url;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
// ==========================================================
|
|
235
|
+
// ==================== 修改:URL 转 base64(带压缩)====================
|
|
157
236
|
async function urlToBase64(url) {
|
|
158
237
|
if (!url)
|
|
159
238
|
return null;
|
|
160
239
|
if (url.startsWith('data:image/')) {
|
|
240
|
+
// 如果已经是 base64,检查是否需要压缩
|
|
241
|
+
if (cfg.enableImgCompress && sharp) {
|
|
242
|
+
return await compressBase64Image(url);
|
|
243
|
+
}
|
|
161
244
|
return url;
|
|
162
245
|
}
|
|
163
246
|
try {
|
|
@@ -165,9 +248,9 @@ async function apply(ctx, cfg) {
|
|
|
165
248
|
responseType: 'arraybuffer',
|
|
166
249
|
timeout: 30000,
|
|
167
250
|
});
|
|
168
|
-
|
|
169
|
-
const
|
|
170
|
-
return `data
|
|
251
|
+
// 压缩图片
|
|
252
|
+
const compressed = await compressImage(Buffer.from(res.data));
|
|
253
|
+
return `data:image/jpeg;base64,${compressed.toString('base64')}`;
|
|
171
254
|
}
|
|
172
255
|
catch (e) {
|
|
173
256
|
logger.error('图片转 base64 失败', e);
|
package/package.json
CHANGED