smart-image-scraper-mcp 2.9.0 → 2.9.2

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "smart-image-scraper-mcp",
3
- "version": "2.9.0",
3
+ "version": "2.9.2",
4
4
  "description": "全网智能图片抓取 MCP 服务器 - 支持 Bing/Google 图片搜索、验证和下载",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
@@ -79,6 +79,30 @@ export class Orchestrator {
79
79
  */
80
80
  async processKeywordLink(keyword, count, source, options = {}) {
81
81
  const startTime = Date.now();
82
+ const KEYWORD_TIMEOUT = 20000; // 单个关键词20秒超时
83
+
84
+ // 为单个关键词添加超时保护
85
+ const timeoutPromise = new Promise((_, reject) =>
86
+ setTimeout(() => reject(new Error(`关键词 "${keyword}" 处理超时(20秒)`)), KEYWORD_TIMEOUT)
87
+ );
88
+
89
+ try {
90
+ return await Promise.race([
91
+ this._processKeywordLinkInternal(keyword, count, source, options, startTime),
92
+ timeoutPromise
93
+ ]);
94
+ } catch (error) {
95
+ logger.error(`Process keyword error: ${keyword}`, { error: error.message });
96
+ return {
97
+ keyword,
98
+ success: false,
99
+ error: error.message,
100
+ duration: Date.now() - startTime,
101
+ };
102
+ }
103
+ }
104
+
105
+ async _processKeywordLinkInternal(keyword, count, source, options, startTime) {
82
106
  // 根据 quality 参数决定模式
83
107
  const qualityMode = options.quality || 'balanced';
84
108
  const fastMode = qualityMode === 'fast';
@@ -163,13 +187,7 @@ export class Orchestrator {
163
187
  duration: Date.now() - startTime,
164
188
  };
165
189
  } catch (error) {
166
- logger.error(`Process keyword error: ${keyword}`, { error: error.message });
167
- return {
168
- keyword,
169
- success: false,
170
- error: error.message,
171
- duration: Date.now() - startTime,
172
- };
190
+ throw error; // 抛出让外层处理
173
191
  }
174
192
  }
175
193
 
@@ -183,6 +201,30 @@ export class Orchestrator {
183
201
  */
184
202
  async processKeywordDownload(keyword, count, source, options = {}) {
185
203
  const startTime = Date.now();
204
+ const KEYWORD_TIMEOUT = 45000; // 下载模式45秒超时(比link模式长)
205
+
206
+ // 为单个关键词添加超时保护
207
+ const timeoutPromise = new Promise((_, reject) =>
208
+ setTimeout(() => reject(new Error(`关键词 "${keyword}" 下载超时(45秒)`)), KEYWORD_TIMEOUT)
209
+ );
210
+
211
+ try {
212
+ return await Promise.race([
213
+ this._processKeywordDownloadInternal(keyword, count, source, options, startTime),
214
+ timeoutPromise
215
+ ]);
216
+ } catch (error) {
217
+ logger.error(`Process keyword download error: ${keyword}`, { error: error.message });
218
+ return {
219
+ keyword,
220
+ success: false,
221
+ error: error.message,
222
+ duration: Date.now() - startTime,
223
+ };
224
+ }
225
+ }
226
+
227
+ async _processKeywordDownloadInternal(keyword, count, source, options, startTime) {
186
228
  // 根据 quality 参数决定模式(download 模式默认高质量)
187
229
  const qualityMode = options.quality || 'balanced';
188
230
  const prioritizeQuality = qualityMode !== 'fast';
@@ -292,13 +334,7 @@ export class Orchestrator {
292
334
  duration: Date.now() - startTime,
293
335
  };
294
336
  } catch (error) {
295
- logger.error(`Process keyword error: ${keyword}`, { error: error.message });
296
- return {
297
- keyword,
298
- success: false,
299
- error: error.message,
300
- duration: Date.now() - startTime,
301
- };
337
+ throw error; // 抛出让外层处理
302
338
  }
303
339
  }
304
340
 
@@ -310,16 +346,20 @@ export class Orchestrator {
310
346
  async execute(params) {
311
347
  metrics.recordRequest();
312
348
  const requestId = `req_${Date.now()}_${Math.random().toString(36).substr(2, 6)}`;
313
- const GLOBAL_TIMEOUT = 30000; // 30秒全局超时
349
+
350
+ // 根据关键词数量动态计算超时时间(每个关键词 10 秒,最少 30 秒,最多 120 秒)
351
+ const keywords = this.parseKeywords(params.query);
352
+ const keywordCount = keywords.length;
353
+ const GLOBAL_TIMEOUT = Math.min(Math.max(keywordCount * 10000, 30000), 120000);
314
354
 
315
355
  try {
316
- logger.info(`[Orchestrator] Starting request: ${requestId}`);
356
+ logger.info(`[Orchestrator] Starting request: ${requestId}, keywords: ${keywordCount}, timeout: ${GLOBAL_TIMEOUT/1000}s`);
317
357
 
318
358
  // 添加全局超时熔断机制
319
359
  const result = await Promise.race([
320
360
  this._executeInternal(params),
321
361
  new Promise((_, reject) =>
322
- setTimeout(() => reject(new Error('REQUEST_TIMEOUT: 请求超时(30秒),请稍后重试')), GLOBAL_TIMEOUT)
362
+ setTimeout(() => reject(new Error(`REQUEST_TIMEOUT: 请求超时(${GLOBAL_TIMEOUT/1000}秒),关键词过多请减少数量`)), GLOBAL_TIMEOUT)
323
363
  )
324
364
  ]);
325
365