stnh70 commited on
Commit
bf34df1
·
verified ·
1 Parent(s): 73c1141

Update subtitle.ts

Browse files
Files changed (1) hide show
  1. subtitle.ts +77 -16
subtitle.ts CHANGED
@@ -43,7 +43,7 @@ const config = {
43
  url: Deno.env.get("OPENAI_API_URL_3") || "https://translate.doi9.top",
44
  apiKey: Deno.env.get("OPENAI_API_KEY_3") || "123",
45
  model: Deno.env.get("OPENAI_MODEL_3") || "gpt-4.1-mini",
46
- maxLinesPerRequest: 25,
47
  maxWorkers: 3,
48
  minInterval: 300
49
  },
@@ -52,7 +52,7 @@ const config = {
52
  url: Deno.env.get("OPENAI_API_URL_2") || "https://tbai.xin",
53
  apiKey: Deno.env.get("OPENAI_API_KEY_2") || "sk-8Pgi3KczqqCo4Hg0XJjDelRFnRHSII6nKTJMpCjWdGVfJJVA",
54
  model: Deno.env.get("OPENAI_MODEL_2") || "gpt-4.1-nano",
55
- maxLinesPerRequest: 25,
56
  maxWorkers: 3,
57
  minInterval: 300
58
  },
@@ -121,6 +121,15 @@ interface SubtitleEntry {
121
  originalSubtitles?: SubtitleEntry[];
122
  }
123
 
 
 
 
 
 
 
 
 
 
124
  class AdaptiveRateLimiter {
125
  private queue: Array<{
126
  fn: () => Promise<any>,
@@ -374,7 +383,7 @@ class ChatGPT_WorkerPool {
374
  state.runningWorkers++;
375
  state.lastRunTime = now;
376
  const { task, resolve, reject } = state.queue.shift()!;
377
- console.log(`[WorkerPool] 🚀 [${providerConfig.name}] Task start. (Running: ${state.runningWorkers}, Queue: ${state.queue.length})`);
378
 
379
  this.singleTranslationTask(task, 0, providerIndex)
380
  .then(resolve)
@@ -471,13 +480,13 @@ class ChatGPT_WorkerPool {
471
  console.log(`[WorkerPool-Cache] HIT for task ${task.subtitleId}`);
472
  return cachedResult;
473
  }
474
- console.log(`[WorkerPool-Cache] MISS for task ${task.subtitleId}`);
475
  } catch (cacheError) {
476
  console.warn(`[WorkerPool-Cache] Cache check failed: ${cacheError.message}`);
477
  }
478
 
479
  let systemPrompt = '';
480
- if (provider.model.includes('glm-')) {
481
  systemPrompt = `You are a text translation API. Your task is to translate the user's text from ${task.sourceLanguage} to ${task.targetLanguage}.
482
  RULES:
483
  1. Translate the text inside the square brackets for each numbered item.
@@ -515,7 +524,7 @@ Output:
515
  task.signal.addEventListener('abort', onAbort, { once: true });
516
 
517
  try {
518
- console.log(`[Worker] Attempt #${retryCount + 1} for ID: ${task.subtitleId} using [${provider.name}]`);
519
 
520
  if (task.signal.aborted) throw new Error("Aborted before fetch");
521
 
@@ -538,14 +547,28 @@ Output:
538
  task.signal.removeEventListener('abort', onAbort);
539
 
540
  if (!response.ok) {
541
- if ([429, 500, 502, 503, 504].includes(response.status)) {
542
- throw new Error("RETRYABLE_PROXY_ERROR");
 
 
 
 
 
 
 
 
 
 
543
  }
 
 
544
  const errorBody = await response.text();
545
- throw new Error(`API Error: ${response.status}, Body: ${errorBody.substring(0, 100)}`);
 
546
  }
547
 
548
  const result = await response.json();
 
549
  const translation = result.choices[0]?.message?.content?.trim();
550
 
551
  // === 缓存成功结果 ===
@@ -554,7 +577,7 @@ Output:
554
  const textHash = await sha256(task.text.trim());
555
  const cacheKey = `chatgpt-worker-${task.sourceLanguage}-${task.targetLanguage}-${textHash}`;
556
  translationCache.set(cacheKey, translation);
557
- console.log(`[WorkerPool-Cache] STORED result for task ${task.subtitleId}`);
558
  } catch (cacheError) {
559
  console.warn(`[WorkerPool-Cache] Failed to cache result: ${cacheError.message}`);
560
  }
@@ -569,14 +592,35 @@ Output:
569
  throw error;
570
  }
571
 
572
- const statusCode = parseInt(error.message.match(/API Error (\d{3})/)?.[1] || '0');
573
- const isRetryableServerError = [429, 500, 502, 503, 504].includes(statusCode);
574
- const isFatalApiClientError = statusCode >= 400 && statusCode < 500 && !isRetryableServerError;
575
- const isNetworkOrTimeoutError = error.message.includes("timed out") || (error instanceof TypeError && error.message.includes("fetch"));
576
- const isEmptyResponseError = error.message === "EMPTY_RESPONSE";
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
577
 
578
  // 分支 1: 可重试错误
579
  if ((isRetryableServerError || isNetworkOrTimeoutError || isEmptyResponseError) && retryCount < MAX_RETRIES) {
 
 
580
  const delay = Math.pow(2, retryCount) * 1500 + Math.random() * 1000;
581
  console.warn(`[Worker] Retrying on ${provider.name} for ID: ${task.subtitleId} (Attempt ${retryCount + 2}) after ${delay.toFixed(0)}ms. Reason: ${error.message}`);
582
  await new Promise(r => setTimeout(r, delay));
@@ -777,11 +821,28 @@ async function processSingleBatchWithSmartFallback(
777
  const formatRegex = /^\s*\d+\.\s*\[.*]\s*$/;
778
  if (!translatedParts.every(p => formatRegex.test(p.trim()))) {
779
  console.warn(`[SmartFallback] Format error detected in ${batchId}.`);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
780
  throw new Error("Format error detected.");
781
  }
782
 
783
  // --- 4. 如果所有检查都通过,就是成功! ---
784
- console.log(`[SmartFallback] ✅ SUCCESS for batch: ${batchId}`);
785
  return batch.map((original, index) => ({
786
  ...original,
787
  translatedText: translatedParts[index].replace(/^\d+\.\s*\[?/, '').replace(/]?\s*$/, '').trim(),
 
43
  url: Deno.env.get("OPENAI_API_URL_3") || "https://translate.doi9.top",
44
  apiKey: Deno.env.get("OPENAI_API_KEY_3") || "123",
45
  model: Deno.env.get("OPENAI_MODEL_3") || "gpt-4.1-mini",
46
+ maxLinesPerRequest: 50,
47
  maxWorkers: 3,
48
  minInterval: 300
49
  },
 
52
  url: Deno.env.get("OPENAI_API_URL_2") || "https://tbai.xin",
53
  apiKey: Deno.env.get("OPENAI_API_KEY_2") || "sk-8Pgi3KczqqCo4Hg0XJjDelRFnRHSII6nKTJMpCjWdGVfJJVA",
54
  model: Deno.env.get("OPENAI_MODEL_2") || "gpt-4.1-nano",
55
+ maxLinesPerRequest: 50,
56
  maxWorkers: 3,
57
  minInterval: 300
58
  },
 
121
  originalSubtitles?: SubtitleEntry[];
122
  }
123
 
124
+ class ApiError extends Error {
125
+ constructor(message, status, isRetryable = false) {
126
+ super(message);
127
+ this.name = 'ApiError';
128
+ this.status = status; // 存储原始状态码
129
+ this.isRetryable = isRetryable; // 明确标记是否可重试
130
+ }
131
+ }
132
+
133
  class AdaptiveRateLimiter {
134
  private queue: Array<{
135
  fn: () => Promise<any>,
 
383
  state.runningWorkers++;
384
  state.lastRunTime = now;
385
  const { task, resolve, reject } = state.queue.shift()!;
386
+ console.log(`[WorkerPool] 🚀 [${providerConfig.name}] 翻译任务启动. (正在运行: ${state.runningWorkers}, 队列剩余: ${state.queue.length})`);
387
 
388
  this.singleTranslationTask(task, 0, providerIndex)
389
  .then(resolve)
 
480
  console.log(`[WorkerPool-Cache] HIT for task ${task.subtitleId}`);
481
  return cachedResult;
482
  }
483
+ // console.log(`[WorkerPool-Cache] MISS for task ${task.subtitleId}`);
484
  } catch (cacheError) {
485
  console.warn(`[WorkerPool-Cache] Cache check failed: ${cacheError.message}`);
486
  }
487
 
488
  let systemPrompt = '';
489
+ if (provider.model.toLowerCase().includes('glm-')) {
490
  systemPrompt = `You are a text translation API. Your task is to translate the user's text from ${task.sourceLanguage} to ${task.targetLanguage}.
491
  RULES:
492
  1. Translate the text inside the square brackets for each numbered item.
 
524
  task.signal.addEventListener('abort', onAbort, { once: true });
525
 
526
  try {
527
+ console.log(`[Worker] #${retryCount + 1} 次尝试, ID: ${task.subtitleId} 使用 [${provider.name}] 翻译。`);
528
 
529
  if (task.signal.aborted) throw new Error("Aborted before fetch");
530
 
 
547
  task.signal.removeEventListener('abort', onAbort);
548
 
549
  if (!response.ok) {
550
+ // if ([429, 500, 502, 503, 504].includes(response.status)) {
551
+ // throw new Error("RETRYABLE_PROXY_ERROR");
552
+ // }
553
+ // const errorBody = await response.text();
554
+ // throw new Error(`API Error: ${response.status}, Body: ${errorBody.substring(0, 100)}`);
555
+
556
+ const status = response.status;
557
+ const retryableStatusCodes = [429, 500, 502, 503, 504];
558
+
559
+ if (retryableStatusCodes.includes(status)) {
560
+ // 对于可重试错误,将 isRetryable 设为 true
561
+ throw new ApiError(`Retryable API Error: ${status}`, status, true);
562
  }
563
+
564
+ // 对于其他 API 错误
565
  const errorBody = await response.text();
566
+ throw new ApiError(`API Error: ${status}, Body: ${errorBody.substring(0, 100)}`, status, false);
567
+
568
  }
569
 
570
  const result = await response.json();
571
+ // console.log("完整响应结构:", JSON.stringify(result, null, 2));
572
  const translation = result.choices[0]?.message?.content?.trim();
573
 
574
  // === 缓存成功结果 ===
 
577
  const textHash = await sha256(task.text.trim());
578
  const cacheKey = `chatgpt-worker-${task.sourceLanguage}-${task.targetLanguage}-${textHash}`;
579
  translationCache.set(cacheKey, translation);
580
+ // console.log(`[WorkerPool-Cache] STORED result for task ${task.subtitleId}`);
581
  } catch (cacheError) {
582
  console.warn(`[WorkerPool-Cache] Failed to cache result: ${cacheError.message}`);
583
  }
 
592
  throw error;
593
  }
594
 
595
+ // const statusCode = parseInt(error.message.match(/API Error (\d{3})/)?.[1] || '0');
596
+ // const isRetryableServerError = [429, 500, 502, 503, 504].includes(statusCode);
597
+ // const isFatalApiClientError = statusCode >= 400 && statusCode < 500 && !isRetryableServerError;
598
+ // const isNetworkOrTimeoutError = error.message.includes("timed out") || (error instanceof TypeError && error.message.includes("fetch"));
599
+ // const isEmptyResponseError = error.message === "EMPTY_RESPONSE";
600
+
601
+ let statusCode = 0;
602
+ let isRetryableServerError = false;
603
+ let isFatalApiClientError = false;
604
+ let isNetworkOrTimeoutError = false;
605
+ let isEmptyResponseError = false;
606
+
607
+ // 使用 instanceof 进行清晰、可靠的错误类型判断
608
+ if (error instanceof ApiError) {
609
+ // 这是我们定义的 API 错误,直接从属性读取信息,不再解析字符串!
610
+ statusCode = error.status;
611
+ isRetryableServerError = error.isRetryable;
612
+ isFatalApiClientError = !isRetryableServerError && statusCode >= 400 && statusCode < 500;
613
+ } else {
614
+ // 这是其他类型的错误(网络、超时、代码bug等)
615
+ // 保持原有的字符串检查逻辑来分类这些非 API 错误
616
+ isNetworkOrTimeoutError = error.message.includes("timed out") || (error instanceof TypeError && error.message.includes("fetch"));
617
+ isEmptyResponseError = error.message === "EMPTY_RESPONSE";
618
+ }
619
 
620
  // 分支 1: 可重试错误
621
  if ((isRetryableServerError || isNetworkOrTimeoutError || isEmptyResponseError) && retryCount < MAX_RETRIES) {
622
+ // if ((error.message === "RETRYABLE_PROXY_ERROR" || error.message === "EMPTY_RESPONSE" || error.message.includes("timed out")||
623
+ // (error instanceof TypeError && error.message.includes("fetch"))) && retryCount < MAX_RETRIES) {
624
  const delay = Math.pow(2, retryCount) * 1500 + Math.random() * 1000;
625
  console.warn(`[Worker] Retrying on ${provider.name} for ID: ${task.subtitleId} (Attempt ${retryCount + 2}) after ${delay.toFixed(0)}ms. Reason: ${error.message}`);
626
  await new Promise(r => setTimeout(r, delay));
 
821
  const formatRegex = /^\s*\d+\.\s*\[.*]\s*$/;
822
  if (!translatedParts.every(p => formatRegex.test(p.trim()))) {
823
  console.warn(`[SmartFallback] Format error detected in ${batchId}.`);
824
+
825
+ console.error(`[SmartFallback-Triage] Mismatch for ${batchId} (received: , expected: ${batch.length}). Activating final fallback.`);
826
+ console.error(`\n\n\n!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!`);
827
+ console.error(`[CRIME SCENE] BATCH #${batchId} FAILED: Mismatch Detected!`);
828
+ console.error(`!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!`);
829
+ console.error(` - Batch ID: ${batchId}`);
830
+ console.error(` - EXPECTED LINES: ${batch.length}`);
831
+ console.error(` - RECEIVED PARTS: `);
832
+ console.error("\n--- MERGED ORIGINAL TEXT (SENT TO API) ---");
833
+ console.error(mergedText);
834
+ console.error("\n--- RECEIVED TRANSLATED TEXT (FROM API) ---");
835
+ console.error(translatedMergedText);
836
+ console.error("\n--- SPLIT PARTS (FOR DEBUGGING) ---");
837
+ console.error(translatedParts);
838
+ console.error(`!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!`);
839
+ console.error(`[ACTION] Activating line-by-line fallback for this batch...`);
840
+ console.error(`!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n\n\n`);
841
  throw new Error("Format error detected.");
842
  }
843
 
844
  // --- 4. 如果所有检查都通过,就是成功! ---
845
+ console.log(`[SmartFallback] ✅ 批量翻译成功: ${batchId}`);
846
  return batch.map((original, index) => ({
847
  ...original,
848
  translatedText: translatedParts[index].replace(/^\d+\.\s*\[?/, '').replace(/]?\s*$/, '').trim(),