From 86de5b979063d9d832a5e73cd2982da99d55064e Mon Sep 17 00:00:00 2001 From: guobao2333 Date: Thu, 26 Dec 2024 01:53:21 +0800 Subject: [PATCH 1/4] =?UTF-8?q?chore:=20=E4=BC=98=E5=8C=96=E9=94=99?= =?UTF-8?q?=E8=AF=AF=E6=8D=95=E8=8E=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 6 +++++- package.json | 4 ++-- src/server.js | 15 ++++++++++++--- src/translate.js | 14 ++++++++++---- 4 files changed, 29 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 7014932..d8a8790 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,9 @@ # DeepLX Serverless +

+Repository +

+ ***本项目3.0版本开始完全基于[OwO-Network/DeepLX](https://github.com/OwO-Network/DeepLX)和DeepL网页端API进行重写。*** **2.0及之前版本在[LegendLeo/deeplx-serverless](https://github.com/LegendLeo/deeplx-serverless)的基础上进行重构。** @@ -69,7 +73,7 @@ curl -X POST 'http://localhost:6119/translate' -H 'Content-Type: application/jso 简单的示例: ```javascript -import { translate } from './translate.js'; +import { translate } from './src/translate.js'; translate('how are you?', 'en', 'zh', '', false, false) .then(result => { console.log(result) diff --git a/package.json b/package.json index 6e15f7c..0ebcebc 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "version": "3.0.1", "description": "DeepL free API for Serverless", "type": "module", - "main": "server.js", + "main": "src/server.js", "keywords": [ "deeplxs", "deeplx", @@ -19,7 +19,7 @@ "scripts": { "start": "node src/server.js", "test": "node test.js", - "start:sl": "node src/index.js", + "start:sl": "node api/index.js", "test:post": "node test_post.js" }, "dependencies": { diff --git a/src/server.js b/src/server.js index 836047a..f5ea4e1 100644 --- a/src/server.js +++ b/src/server.js @@ -83,9 +83,18 @@ async function post(req, res) { return decompressedData; });*/ + if(result.status == 429) { + console.error(`[WARN] ${new Date().toISOString()} | POST "translate" | 429 | ${error.message} | ${duration}ms`); + res.status(429).json({ + code: 429, + message: "Too many requests", + error: result.statusText + }); + } + const duration = Date.now() - startTime; // 计算处理时间 // console.log(result); - if(result == "") { + if(result == "" || result.data == "") { console.error(`[ERROR] ${new Date().toISOString()} | POST "translate" | 500 | ${error.message} | ${duration}ms`); res.status(500).json({ code: 500, @@ -96,7 +105,7 @@ async function post(req, res) { console.log(`[LOG] ${new Date().toISOString()} | POST "translate" | 200 | ${duration}ms`); const responseData = { - code: 200, + code: result.code, id: result.id, data: result.data, // 取第一个翻译结果 method: "Free", @@ -125,7 +134,7 @@ async function post(req, res) { }; async function get(req, res) { - res.json({ + res.status(200).json({ code: 200, message: "Welcome to the DeepL Free API. Please POST to '/translate'. Visit 'https://github.com/guobao2333/DeepLX-Serverless' for more information." }); diff --git a/src/translate.js b/src/translate.js index 0efd34b..8a980fa 100644 --- a/src/translate.js +++ b/src/translate.js @@ -35,7 +35,7 @@ async function sendRequest(postData, urlMethod, dlSession, printResult) { ...(dlSession && {'Cookie': `dl_session=${dlSession}`}) }; postData = formatPostString(postData); - // console.log(postData); + // console.warn(postData); try { const response = await axios.post(urlFull, postData, { @@ -50,9 +50,15 @@ async function sendRequest(postData, urlMethod, dlSession, printResult) { }); return JSON.parse(decompressed.toString()); } + if(response.status == 429) { + throw { + code: splitResult.code, + message: splitResult.data.error + } + } return response.data; } catch (err) { - console.error(err); + throw new Error(err); } } @@ -81,7 +87,7 @@ async function translate(text, sourceLang, targetLang, dlSession, tagHandling, p // 分割文本 const splitResult = await splitText(text, tagHandling === 'html' || tagHandling === 'xml', dlSession, printResult); - // console.log(splitResult); + // console.warn(splitResult); if (splitResult.code == 429) { throw { @@ -157,7 +163,7 @@ async function translate(text, sourceLang, targetLang, dlSession, tagHandling, p } const ret = { - code: 200, + code: postData.status, id: postData.id, method: "Free", data: translatedText, From 25a7d1b6f1885120c50e6fd4b62475fba411e8f7 Mon Sep 17 00:00:00 2001 From: guobao2333 Date: Wed, 15 Jan 2025 09:55:07 +0800 Subject: [PATCH 2/4] =?UTF-8?q?fix(WIP):=20=E6=8A=A5=E9=94=99=E4=BF=A1?= =?UTF-8?q?=E6=81=AF=E4=B8=8D=E5=87=86=E7=A1=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/server.js | 17 ++++++++--------- src/translate.js | 49 ++++++++++++++++++++++++++++-------------------- 2 files changed, 37 insertions(+), 29 deletions(-) diff --git a/src/server.js b/src/server.js index f5ea4e1..49093d6 100644 --- a/src/server.js +++ b/src/server.js @@ -83,19 +83,19 @@ async function post(req, res) { return decompressedData; });*/ - if(result.status == 429) { - console.error(`[WARN] ${new Date().toISOString()} | POST "translate" | 429 | ${error.message} | ${duration}ms`); + let duration = Date.now() - startTime; + if(result.code === 429) { + console.error(`[WARN] ${new Date().toISOString()} | POST "translate" | 429 | ${result.message} | ${duration}ms`); res.status(429).json({ code: 429, - message: "Too many requests", - error: result.statusText + message: result.message }); } - const duration = Date.now() - startTime; // 计算处理时间 + duration = Date.now() - startTime; // console.log(result); if(result == "" || result.data == "") { - console.error(`[ERROR] ${new Date().toISOString()} | POST "translate" | 500 | ${error.message} | ${duration}ms`); + console.error(`[ERROR] ${new Date().toISOString()} | POST "translate" | 500 | ${result.message} | ${duration}ms`); res.status(500).json({ code: 500, message: "Translation failed", @@ -125,10 +125,9 @@ async function post(req, res) { } catch (err) { console.error(err, err.stack); - return res.status(500).json({ + res.status(500).json({ code: 500, - message: "Translation failed", - error: err.message + message: err.message }); } }; diff --git a/src/translate.js b/src/translate.js index 8a980fa..c6c8730 100644 --- a/src/translate.js +++ b/src/translate.js @@ -41,25 +41,28 @@ async function sendRequest(postData, urlMethod, dlSession, printResult) { const response = await axios.post(urlFull, postData, { headers: headers }); - // console.log(response.data); - + if (response.headers['content-encoding'] === 'br') { - const decompressed = brotliDecompress(response.data, (err, data) => { - if (err) console.error(err); - return data; + const decompressed = await new Promise((resolve, reject) => { + brotliDecompress(response.data, (err, data) => { + if (err) reject(err); + else resolve(data); + }); }); return JSON.parse(decompressed.toString()); } - if(response.status == 429) { - throw { - code: splitResult.code, - message: splitResult.data.error - } - } - return response.data; } catch (err) { - throw new Error(err); + if (err.response.status === 429) { + return { + code: err.response.status, + message: 'Too Many Requests' + }; + } else { + console.error(`[ERROR] sendRequest: ${err.message}`); + throw err; + } } + return response.data; } async function splitText(text, tagHandling, dlSession, printResult) { @@ -75,7 +78,12 @@ async function splitText(text, tagHandling, dlSession, printResult) { } }; - return await sendRequest(postData, 'LMT_split_text', dlSession, printResult); + try { + return await sendRequest(postData, 'LMT_split_text', dlSession, printResult); + } catch (err) { + console.error("[ERROR] splitText:", err); + throw new Error(`splitText failed: ${err.message || err}`); + } } // 执行翻译任务 @@ -87,12 +95,12 @@ async function translate(text, sourceLang, targetLang, dlSession, tagHandling, p // 分割文本 const splitResult = await splitText(text, tagHandling === 'html' || tagHandling === 'xml', dlSession, printResult); - // console.warn(splitResult); + console.warn(splitResult); - if (splitResult.code == 429) { - throw { + if (splitResult.code === 429) { + return { code: splitResult.code, - message: splitResult.data.error + message: splitResult.message } } @@ -173,8 +181,9 @@ async function translate(text, sourceLang, targetLang, dlSession, tagHandling, p } if(printResult) console.log(ret); return ret; - } catch(err) { - return err; + } catch (err) { + console.error("[ERROR] translate:", err); + throw new Error(err.message || err); } } From f4f8b113f16701ef6b640aed7ad3070b7e533a5e Mon Sep 17 00:00:00 2001 From: guobao2333 Date: Wed, 15 Jan 2025 10:03:04 +0800 Subject: [PATCH 3/4] =?UTF-8?q?fix:=20=E9=80=BB=E8=BE=91=E9=A1=BA=E5=BA=8F?= =?UTF-8?q?=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/translate.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/translate.js b/src/translate.js index c6c8730..3319251 100644 --- a/src/translate.js +++ b/src/translate.js @@ -51,6 +51,7 @@ async function sendRequest(postData, urlMethod, dlSession, printResult) { }); return JSON.parse(decompressed.toString()); } + return response.data; } catch (err) { if (err.response.status === 429) { return { @@ -62,7 +63,6 @@ async function sendRequest(postData, urlMethod, dlSession, printResult) { throw err; } } - return response.data; } async function splitText(text, tagHandling, dlSession, printResult) { From 0cac62406a46f9972e55724832275b18624ec11c Mon Sep 17 00:00:00 2001 From: guobao2333 Date: Wed, 15 Jan 2025 10:30:39 +0800 Subject: [PATCH 4/4] =?UTF-8?q?chore:=20=E5=AE=8C=E5=85=A8=E7=A7=BB?= =?UTF-8?q?=E9=99=A4=E6=97=A7=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/server.js | 1 - src/translate.js | 2 +- src/translate_old.js | 130 ------------------------------------------- 3 files changed, 1 insertion(+), 132 deletions(-) delete mode 100644 src/translate_old.js diff --git a/src/server.js b/src/server.js index 49093d6..3d17a54 100644 --- a/src/server.js +++ b/src/server.js @@ -72,7 +72,6 @@ async function post(req, res) { code: 405, message: "Alternative Translate Not Allowed" }); - // alt_count = 0; } try { diff --git a/src/translate.js b/src/translate.js index 3319251..b8c5dce 100644 --- a/src/translate.js +++ b/src/translate.js @@ -95,7 +95,7 @@ async function translate(text, sourceLang, targetLang, dlSession, tagHandling, p // 分割文本 const splitResult = await splitText(text, tagHandling === 'html' || tagHandling === 'xml', dlSession, printResult); - console.warn(splitResult); + // console.warn(splitResult); if (splitResult.code === 429) { return { diff --git a/src/translate_old.js b/src/translate_old.js deleted file mode 100644 index 40951f8..0000000 --- a/src/translate_old.js +++ /dev/null @@ -1,130 +0,0 @@ -// 由于代码已重构,此实现暂时停用,部分旧实现将会陆续合并到新的实现,以确保向前兼容 - -import axios from 'axios'; -import { brotliDecompress } from 'zlib'; - -const DEEPL_BASE_URL = 'https://www2.deepl.com/jsonrpc'/*, - DEEPL_PRO_URL = 'https://api.deepl.com', - DEEPL_FREE_URL = 'https://api-free.deepl.com'*/; -const headers = { - "Accept": "*/*", - "Accept-Language": "en-US,en;q=0.9,zh-CN;q=0.8,zh-TW;q=0.7,zh-HK;q=0.6,zh;q=0.5", - "Authorization": "None", - "Cache-Control": "no-cache", - "Content-Type": "application/json", - "DNT": "1", - "Origin": "chrome-extension://cofdbpoegempjloogbagkncekinflcnj", - "Pragma": "no-cache", - "Priority": "u=1, i", - "Referer": "https://www.deepl.com/", - "Sec-Fetch-Dest": "empty", - "Sec-Fetch-Mode": "cors", - "Sec-Fetch-Site": "none", - "Sec-GPC": "1", - "User-Agent": "DeepLBrowserExtension/1.28.0 Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36" -}; - -function getICount(translateText) { - return (translateText || '').split('i').length - 1; -} - -function getRandomInt(min=8300000, max=8399998) { - min = Math.ceil(min); - max = Math.floor(max); - return (Math.floor(Math.random() * (max - min + 1)) + min) * 1000; -} - -function getTimestamp(iCount) { - const ts = Date.now(); - if (iCount === 0) { - return ts; - } - iCount++; - return ts - (ts % iCount) + iCount; -} - -/** - * 你可以在其他js中调用,无需运行 'npm start' - * 如果无需在控制台打印,最后一个布尔值参数可以去掉 - * - * @async - * @param {string} text - 待翻译的文本 - * @param {string} [sourceLang='AUTO'] - 源语言国家/地区代号 默认自动识别 - * @param {string} targetLang - 目标语言国家/地区代号 - * @param {number} [alternativeCount] - 请求的备选翻译数量 - * @param {boolean} [printResult] - 控制台打印返回结果 - * @returns {Promise} translationData - 返回翻译数据JSON对象 - * @typedef {Object} translationData - * @property {number} code - http状态码 - * @property {string} data - 翻译结果 - * @property {number} id - * @property {string} method - 请求的接口类型 目前只有Free - * @property {string} source_lang - 源语言国家/地区代号 - * @property {string} target_lang - 目标语言国家/地区代号 - * @property {Array} alternatives - 备选翻译列表 - * @example - * await translate('你好,世界!', 'zh', 'en', 3, true); - */ -async function translate( - text, - sourceLang, - targetLang, - alternativeCount = 0, - printResult = false, -) { - const iCount = getICount(text), - id = getRandomInt(); - - alternativeCount = Math.max(Math.min(3, alternativeCount), 0); - - let postData = { - jsonrpc: "2.0", - method: "LMT_split_text", - params: { - texts: [text], - commonJobParams: { - mode: "translate", - textType: "plaintext" - }, - lang: { lang_user_selected: "auto" } - }, - id: id - }; - - postData = JSON.stringify(postData); - if ((id + 5) % 29 === 0 || (id + 3) % 13 === 0) { - postData = postData.replace('"method":"', '"method" : "'); - } else { - postData = postData.replace('"method":"', '"method": "'); - } - - try { - const response = await axios.post(DEEPL_BASE_URL + '?method=LMT_split_text', postData, { - headers: headers, - }); - - if (response.status === 429) { - throw new Error('Too many requests, your IP has been blocked by DeepL temporarily, please don\'t request it frequently in a short time.'); - } - - if (response.status !== 200) { - throw new Error(`Error code: ${response.status}\nError message: ${response.statusText}`); - } - - // console.log(response.data); - - const result = { - detected_source_language: sourceLang.toUpperCase(), - data: response.data.result.texts[0].chunks[0].sentences[0].text, - // alternatives: response.data.result.texts[0].alternatives.map(alternative => alternative.text), - method: "Free" - }; - if(printResult) console.log(result); - return result; - } catch (err) { - console.error(err, err.stack); - throw err; - } -} - -export { translate };