task pool

This commit is contained in:
Gabe Yuan
2023-08-04 16:05:14 +08:00
parent 3631219b92
commit 7d2fafcd0e
11 changed files with 13621 additions and 375 deletions

13094
dist/chrome/content.js vendored

File diff suppressed because one or more lines are too long

View File

@@ -1,207 +1,338 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>%REACT_APP_NAME%</title>
<style>
img {
width: 1.2em;
height: 1.2em;
}
<head> svg {
<meta charset="utf-8" /> max-width: 1.2em;
<meta name="viewport" content="width=device-width, initial-scale=1" /> max-height: 1.2em;
<title>%REACT_APP_NAME%</title> }
<style> </style>
img { </head>
width: 1.2em;
height: 1.2em;
}
svg { <body>
max-width: 1.2em; <noscript>You need to enable JavaScript to run this app.</noscript>
max-height: 1.2em; <div id="root">
}
</style>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root">
<div class="cont cont1">
<h2>React is a JavaScript library for building user interfaces.</h2> <h2>React is a JavaScript library for building user interfaces.</h2>
<ul> <br />
<li> <br />
Declarative: React makes it painless to create interactive UIs. <br />
Design simple views for each state in your application, and React <br />
will efficiently update and render just the right components when <br />
your data changes. Declarative views make your code more <br />
predictable, simpler to understand, and easier to debug. <br />
</li> <br />
<li> <br />
Component-Based: Build encapsulated components that manage their own <br />
state, then compose them to make complex UIs. Since component logic <br />
is written in JavaScript instead of templates, you can easily pass <br />
rich data through your app and keep the state out of the DOM. <br />
</li> <br />
<li> <br />
React 使创建交互式 UI <br />
变得轻而易举。为你应用的每一个状态设计简洁的视图,当数据变动时 React <br />
能高效更新并渲染合适的组件。 <br />
</li> <br />
<li>以声明式编写 UI可以让你的代码更加可靠且方便调试。</li> <br />
</ul> <br />
</div> <br />
<p></p> <br />
<p></p> <br />
<p></p> <br />
<p></p> <br />
<p></p> <br />
<p></p> <br />
<p></p> <br />
<p></p> <br />
<p></p> <br />
<p></p> <br />
<p></p>
<p></p>
<div class="cont cont2">
<h2>React is a JavaScript library for building user interfaces.</h2> <h2>React is a JavaScript library for building user interfaces.</h2>
<ul> <br />
<li> <br />
Declarative: React makes it painless to create interactive UIs. <br />
Design simple views for each state in your application, and React <br />
will efficiently update and render just the right components when <br />
your data changes. Declarative views make your code more <br />
predictable, simpler to understand, and easier to debug. <br />
</li> <br />
<li> <br />
Component-Based: Build encapsulated components that manage their own <br />
state, then compose them to make complex UIs. Since component logic <br />
is written in JavaScript instead of templates, you can easily pass <br />
rich data through your app and keep the state out of the DOM. <br />
</li> <br />
<li> <br />
React 使创建交互式 UI <br />
变得轻而易举。为你应用的每一个状态设计简洁的视图,当数据变动时 React <br />
能高效更新并渲染合适的组件。 <br />
</li> <br />
<li>以声明式编写 UI可以让你的代码更加可靠且方便调试。</li> <br />
</ul> <br />
</div> <br />
<p></p> <br />
<p></p> <br />
<p></p> <br />
<p></p> <br />
<p></p> <br />
<p></p> <br />
<p></p> <br />
<p></p> <br />
<p></p> <br />
<p></p> <br />
<p></p>
<p></p>
<p></p>
<p></p>
<p></p>
<p></p>
<p></p>
<p></p>
<div class="cont cont3">
<h2>React is a JavaScript library for building user interfaces.</h2> <h2>React is a JavaScript library for building user interfaces.</h2>
<ul> <br />
<li> <br />
Declarative: React makes it painless to create interactive UIs. <br />
Design simple views for each state in your application, and React <br />
will efficiently update and render just the right components when <br />
your data changes. Declarative views make your code more <br />
predictable, simpler to understand, and easier to debug. <br />
</li> <br />
<li> <br />
Component-Based: Build encapsulated components that manage their own <br />
state, then compose them to make complex UIs. Since component logic <br />
is written in JavaScript instead of templates, you can easily pass <br />
rich data through your app and keep the state out of the DOM. <br />
</li> <br />
<li> <br />
React 使创建交互式 UI <br />
变得轻而易举。为你应用的每一个状态设计简洁的视图,当数据变动时 React <br />
能高效更新并渲染合适的组件。 <br />
</li> <br />
<li>以声明式编写 UI可以让你的代码更加可靠且方便调试。</li> <br />
</ul> <br />
</div> <br />
<p></p> <br />
<p></p> <br />
<p></p> <br />
<p></p> <br />
<p></p> <br />
<p></p> <br />
<p></p> <br />
<p></p> <br />
<p></p> <br />
<p></p> <br />
<p></p>
<p></p>
<p></p>
<p></p>
<div class="cont cont4">
<h2>
React is a <code>JavaScript</code> <a href="#">library</a> for
building user interfaces.
</h2>
<ul>
<li>
Declarative: React makes it painless to create interactive UIs.
Design simple views for each state in your application, and React
will efficiently update and render just the right components when
your data changes. Declarative views make your code more
predictable, simpler to understand, and easier to debug.
</li>
<li>
Component-Based: Build encapsulated components that manage their own
state, then compose them to make complex UIs. Since component logic
is written in JavaScript instead of templates, you can easily pass
rich data through your app and keep the state out of the DOM.
</li>
<li>
React 使创建交互式 UI
变得轻而易举。为你应用的每一个状态设计简洁的视图,当数据变动时 React
能高效更新并渲染合适的组件。
</li>
<li>以声明式编写 UI可以让你的代码更加可靠且方便调试。</li>
</ul>
</div>
<p></p>
<p></p>
<p></p>
<p></p>
<p></p>
<p></p>
<p></p>
<p></p>
<p></p>
<p></p>
<p></p>
<p></p>
<div class="cont cont5">
<h2>React is a JavaScript library for building user interfaces.</h2> <h2>React is a JavaScript library for building user interfaces.</h2>
<ul> <br />
<li> <br />
Declarative: React makes it painless to create interactive UIs. <br />
Design simple views for each state in your application, and React <br />
will efficiently update and render just the right components when <br />
your data changes. Declarative views make your code more <br />
predictable, simpler to understand, and easier to debug. <br />
</li> <br />
<li> <br />
Component-Based: Build encapsulated components that manage their own <br />
state, then compose them to make complex UIs. Since component logic <br />
is written in JavaScript instead of templates, you can easily pass <br />
rich data through your app and keep the state out of the DOM. <br />
</li> <br />
<li> <br />
React 使创建交互式 UI <br />
变得轻而易举。为你应用的每一个状态设计简洁的视图,当数据变动时 React <br />
能高效更新并渲染合适的组件。 <br />
</li> <br />
<li>以声明式编写 UI可以让你的代码更加可靠且方便调试。</li> <br />
</ul> <br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<div class="cont cont1">
<h2>React is a JavaScript library for building user interfaces.</h2>
<ul>
<li>
Declarative: React makes it painless to create interactive UIs.
Design simple views for each state in your application, and React
will efficiently update and render just the right components when
your data changes. Declarative views make your code more
predictable, simpler to understand, and easier to debug.
</li>
<li>
Component-Based: Build encapsulated components that manage their own
state, then compose them to make complex UIs. Since component logic
is written in JavaScript instead of templates, you can easily pass
rich data through your app and keep the state out of the DOM.
</li>
<li>
React 使创建交互式 UI
变得轻而易举。为你应用的每一个状态设计简洁的视图,当数据变动时 React
能高效更新并渲染合适的组件。
</li>
<li>以声明式编写 UI可以让你的代码更加可靠且方便调试。</li>
</ul>
</div>
<p></p>
<p></p>
<p></p>
<p></p>
<p></p>
<p></p>
<p></p>
<p></p>
<p></p>
<p></p>
<p></p>
<p></p>
<div class="cont cont2">
<h2>React is a JavaScript library for building user interfaces.</h2>
<ul>
<li>
Declarative: React makes it painless to create interactive UIs.
Design simple views for each state in your application, and React
will efficiently update and render just the right components when
your data changes. Declarative views make your code more
predictable, simpler to understand, and easier to debug.
</li>
<li>
Component-Based: Build encapsulated components that manage their own
state, then compose them to make complex UIs. Since component logic
is written in JavaScript instead of templates, you can easily pass
rich data through your app and keep the state out of the DOM.
</li>
<li>
React 使创建交互式 UI
变得轻而易举。为你应用的每一个状态设计简洁的视图,当数据变动时 React
能高效更新并渲染合适的组件。
</li>
<li>以声明式编写 UI可以让你的代码更加可靠且方便调试。</li>
</ul>
</div>
<p></p>
<p></p>
<p></p>
<p></p>
<p></p>
<p></p>
<p></p>
<p></p>
<p></p>
<p></p>
<p></p>
<p></p>
<p></p>
<p></p>
<p></p>
<p></p>
<p></p>
<p></p>
<div class="cont cont3">
<h2>React is a JavaScript library for building user interfaces.</h2>
<ul>
<li>
Declarative: React makes it painless to create interactive UIs.
Design simple views for each state in your application, and React
will efficiently update and render just the right components when
your data changes. Declarative views make your code more
predictable, simpler to understand, and easier to debug.
</li>
<li>
Component-Based: Build encapsulated components that manage their own
state, then compose them to make complex UIs. Since component logic
is written in JavaScript instead of templates, you can easily pass
rich data through your app and keep the state out of the DOM.
</li>
<li>
React 使创建交互式 UI
变得轻而易举。为你应用的每一个状态设计简洁的视图,当数据变动时 React
能高效更新并渲染合适的组件。
</li>
<li>以声明式编写 UI可以让你的代码更加可靠且方便调试。</li>
</ul>
</div>
<p></p>
<p></p>
<p></p>
<p></p>
<p></p>
<p></p>
<p></p>
<p></p>
<p></p>
<p></p>
<p></p>
<p></p>
<p></p>
<p></p>
<div class="cont cont4">
<h2>
React is a <code>JavaScript</code> <a href="#">library</a> for
building user interfaces.
</h2>
<ul>
<li>
Declarative: React makes it painless to create interactive UIs.
Design simple views for each state in your application, and React
will efficiently update and render just the right components when
your data changes. Declarative views make your code more
predictable, simpler to understand, and easier to debug.
</li>
<li>
Component-Based: Build encapsulated components that manage their own
state, then compose them to make complex UIs. Since component logic
is written in JavaScript instead of templates, you can easily pass
rich data through your app and keep the state out of the DOM.
</li>
<li>
React 使创建交互式 UI
变得轻而易举。为你应用的每一个状态设计简洁的视图,当数据变动时 React
能高效更新并渲染合适的组件。
</li>
<li>以声明式编写 UI可以让你的代码更加可靠且方便调试。</li>
</ul>
</div>
<p></p>
<p></p>
<p></p>
<p></p>
<p></p>
<p></p>
<p></p>
<p></p>
<p></p>
<p></p>
<p></p>
<p></p>
<div class="cont cont5">
<h2>React is a JavaScript library for building user interfaces.</h2>
<ul>
<li>
Declarative: React makes it painless to create interactive UIs.
Design simple views for each state in your application, and React
will efficiently update and render just the right components when
your data changes. Declarative views make your code more
predictable, simpler to understand, and easier to debug.
</li>
<li>
Component-Based: Build encapsulated components that manage their own
state, then compose them to make complex UIs. Since component logic
is written in JavaScript instead of templates, you can easily pass
rich data through your app and keep the state out of the DOM.
</li>
<li>
React 使创建交互式 UI
变得轻而易举。为你应用的每一个状态设计简洁的视图,当数据变动时 React
能高效更新并渲染合适的组件。
</li>
<li>以声明式编写 UI可以让你的代码更加可靠且方便调试。</li>
</ul>
</div>
</div> </div>
</div> <!--
<!--
This HTML file is a template. This HTML file is a template.
If you open it directly in the browser, you will see an empty page. If you open it directly in the browser, you will see an empty page.
@@ -211,6 +342,5 @@
To begin the development, run `npm start` or `yarn start`. To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`. To create a production bundle, use `npm run build` or `yarn build`.
--> -->
</body> </body>
</html>
</html>

View File

@@ -36,7 +36,7 @@ export const apiSyncData = async (url, key, data) =>
* @param {*} from * @param {*} from
* @returns * @returns
*/ */
const apiGoogleTranslate = async (text, to, from) => { const apiGoogleTranslate = async (translator, text, to, from) => {
const params = { const params = {
client: "gtx", client: "gtx",
dt: "t", dt: "t",
@@ -48,14 +48,15 @@ const apiGoogleTranslate = async (text, to, from) => {
}; };
const { googleUrl } = await getSetting(); const { googleUrl } = await getSetting();
const input = `${googleUrl}?${queryString.stringify(params)}`; const input = `${googleUrl}?${queryString.stringify(params)}`;
return fetchPolyfill(input, { return fetchPolyfill(
useCache: true, input,
usePool: true, {
headers: { headers: {
"Content-type": "application/json", "Content-type": "application/json",
"X-Translator": OPT_TRANS_GOOGLE, },
}, },
}); { useCache: true, translator }
);
}; };
/** /**
@@ -65,23 +66,25 @@ const apiGoogleTranslate = async (text, to, from) => {
* @param {*} from * @param {*} from
* @returns * @returns
*/ */
const apiMicrosoftTranslate = (text, to, from) => { const apiMicrosoftTranslate = (translator, text, to, from, token) => {
const params = { const params = {
from, from,
to, to,
"api-version": "3.0", "api-version": "3.0",
}; };
const input = `${URL_MICROSOFT_TRANS}?${queryString.stringify(params)}`; const input = `${URL_MICROSOFT_TRANS}?${queryString.stringify(params)}`;
return fetchPolyfill(input, { return fetchPolyfill(
useCache: true, input,
usePool: true, {
headers: { headers: {
"Content-type": "application/json", "Content-type": "application/json",
"X-Translator": OPT_TRANS_MICROSOFT, Authorization: `Bearer ${token}`,
},
method: "POST",
body: JSON.stringify([{ Text: text }]),
}, },
method: "POST", { useCache: true, translator }
body: JSON.stringify([{ Text: text }]), );
});
}; };
/** /**
@@ -91,35 +94,39 @@ const apiMicrosoftTranslate = (text, to, from) => {
* @param {*} from * @param {*} from
* @returns * @returns
*/ */
const apiOpenaiTranslate = async (text, to, from) => { const apiOpenaiTranslate = async (translator, text, to, from) => {
const { openaiUrl, openaiModel, openaiPrompt } = await getSetting(); const { openaiUrl, openaiKey, openaiModel, openaiPrompt } =
await getSetting();
let prompt = openaiPrompt let prompt = openaiPrompt
.replaceAll(PROMPT_PLACE_FROM, from) .replaceAll(PROMPT_PLACE_FROM, from)
.replaceAll(PROMPT_PLACE_TO, to); .replaceAll(PROMPT_PLACE_TO, to);
return fetchPolyfill(openaiUrl, { return fetchPolyfill(
useCache: true, openaiUrl,
usePool: true, {
headers: { headers: {
"Content-type": "application/json", "Content-type": "application/json",
"X-Translator": OPT_TRANS_OPENAI, Authorization: `Bearer ${openaiKey}`, // OpenAI
"api-key": openaiKey, // Azure OpenAI
},
method: "POST",
body: JSON.stringify({
model: openaiModel,
messages: [
{
role: "system",
content: prompt,
},
{
role: "user",
content: text,
},
],
temperature: 0,
max_tokens: 256,
}),
}, },
method: "POST", { useCache: true, translator }
body: JSON.stringify({ );
model: openaiModel,
messages: [
{
role: "system",
content: prompt,
},
{
role: "user",
content: text,
},
],
temperature: 0,
max_tokens: 256,
}),
});
}; };
/** /**
@@ -127,7 +134,10 @@ const apiOpenaiTranslate = async (text, to, from) => {
* @param {*} param0 * @param {*} param0
* @returns * @returns
*/ */
export const apiTranslate = async ({ translator, q, fromLang, toLang }) => { export const apiTranslate = async (
{ translator, q, fromLang, toLang },
{ token }
) => {
let trText = ""; let trText = "";
let isSame = false; let isSame = false;
@@ -135,15 +145,15 @@ export const apiTranslate = async ({ translator, q, fromLang, toLang }) => {
let to = OPT_LANGS_SPECIAL?.[translator]?.get(toLang) ?? toLang; let to = OPT_LANGS_SPECIAL?.[translator]?.get(toLang) ?? toLang;
if (translator === OPT_TRANS_GOOGLE) { if (translator === OPT_TRANS_GOOGLE) {
const res = await apiGoogleTranslate(q, to, from); const res = await apiGoogleTranslate(translator, q, to, from);
trText = res.sentences.map((item) => item.trans).join(" "); trText = res.sentences.map((item) => item.trans).join(" ");
isSame = to === res.src; isSame = to === res.src;
} else if (translator === OPT_TRANS_MICROSOFT) { } else if (translator === OPT_TRANS_MICROSOFT) {
const res = await apiMicrosoftTranslate(q, to, from); const res = await apiMicrosoftTranslate(translator, q, to, from, token);
trText = res[0].translations[0].text; trText = res[0].translations[0].text;
isSame = to === res[0].detectedLanguage.language; isSame = to === res[0].detectedLanguage.language;
} else if (translator === OPT_TRANS_OPENAI) { } else if (translator === OPT_TRANS_OPENAI) {
const res = await apiOpenaiTranslate(q, to, from); const res = await apiOpenaiTranslate(translator, q, to, from);
trText = res?.choices?.[0].message.content; trText = res?.choices?.[0].message.content;
isSame = (await detectLang(q)) === (await detectLang(trText)); isSame = (await detectLang(q)) === (await detectLang(trText));
} }

View File

@@ -1,7 +1,6 @@
import browser from "webextension-polyfill"; import browser from "webextension-polyfill";
import { import {
MSG_FETCH, MSG_FETCH,
MSG_FETCH_LIMIT,
DEFAULT_SETTING, DEFAULT_SETTING,
DEFAULT_RULES, DEFAULT_RULES,
DEFAULT_SYNC, DEFAULT_SYNC,
@@ -10,7 +9,7 @@ import {
STOKEY_SYNC, STOKEY_SYNC,
CACHE_NAME, CACHE_NAME,
} from "./config"; } from "./config";
import { fetchData, setFetchLimit } from "./libs/fetch"; import { fetchData } from "./libs/fetch";
import storage from "./libs/storage"; import storage from "./libs/storage";
import { getSetting } from "./libs"; import { getSetting } from "./libs";
import { syncAll } from "./libs/sync"; import { syncAll } from "./libs/sync";
@@ -48,7 +47,7 @@ browser.runtime.onMessage.addListener(
({ action, args }, sender, sendResponse) => { ({ action, args }, sender, sendResponse) => {
switch (action) { switch (action) {
case MSG_FETCH: case MSG_FETCH:
fetchData(args.input, args.init) fetchData(args.input, args.init, args.opts)
.then((data) => { .then((data) => {
sendResponse({ data }); sendResponse({ data });
}) })
@@ -56,10 +55,6 @@ browser.runtime.onMessage.addListener(
sendResponse({ error: error.message }); sendResponse({ error: error.message });
}); });
break; break;
case MSG_FETCH_LIMIT:
setFetchLimit(args.limit);
sendResponse({ data: "ok" });
break;
default: default:
sendResponse({ error: `message action is unavailable: ${action}` }); sendResponse({ error: `message action is unavailable: ${action}` });
} }

View File

@@ -43,8 +43,12 @@ export const I18N = {
en: `Interface Language`, en: `Interface Language`,
}, },
fetch_limit: { fetch_limit: {
zh: `并发请求数量`, zh: `最大请求数量`,
en: `Concurrent Requests Limit`, en: `Maximum Number Of Request`,
},
fetch_interval: {
zh: `请求间隔时间(ms)`,
en: `Request Interval(ms)`,
}, },
translate_service: { translate_service: {
zh: `翻译服务`, zh: `翻译服务`,

View File

@@ -109,8 +109,8 @@ export const OPT_STYLE_ALL = [
OPT_STYLE_HIGHTLIGHT, OPT_STYLE_HIGHTLIGHT,
]; ];
export const DEFAULT_FETCH_LIMIT = 1; // 默认并发请求数 export const DEFAULT_FETCH_LIMIT = 10; // 默认最大任务数量
export const DEFAULT_FETCH_INTERVAL = 500; // 默认请求间隔时间 export const DEFAULT_FETCH_INTERVAL = 500; // 默认任务间隔时间
export const PROMPT_PLACE_FROM = "{{from}}"; // 占位符 export const PROMPT_PLACE_FROM = "{{from}}"; // 占位符
export const PROMPT_PLACE_TO = "{{to}}"; // 占位符 export const PROMPT_PLACE_TO = "{{to}}"; // 占位符
@@ -122,13 +122,14 @@ export const DEFAULT_RULE = {
fromLang: "auto", fromLang: "auto",
toLang: "zh-CN", toLang: "zh-CN",
textStyle: OPT_STYLE_DASHLINE, textStyle: OPT_STYLE_DASHLINE,
transOpen: false, transOpen: true,
}; };
export const DEFAULT_SETTING = { export const DEFAULT_SETTING = {
darkMode: false, // 深色模式 darkMode: false, // 深色模式
uiLang: "zh", // 界面语言 uiLang: "zh", // 界面语言
fetchLimit: DEFAULT_FETCH_LIMIT, // 请求并发数量 fetchLimit: DEFAULT_FETCH_LIMIT, // 最大任务数量
fetchInterval: DEFAULT_FETCH_INTERVAL, // 任务间隔时间
clearCache: false, // 是否在浏览器下次启动时清除缓存 clearCache: false, // 是否在浏览器下次启动时清除缓存
googleUrl: "https://translate.googleapis.com/translate_a/single", // 谷歌翻译接口 googleUrl: "https://translate.googleapis.com/translate_a/single", // 谷歌翻译接口
openaiUrl: "https://api.openai.com/v1/chat/completions", openaiUrl: "https://api.openai.com/v1/chat/completions",

View File

@@ -11,6 +11,8 @@ import {
import Content from "./views/Content"; import Content from "./views/Content";
import { StoragesProvider } from "./hooks/Storage"; import { StoragesProvider } from "./hooks/Storage";
import { queryEls, getRules, matchRule } from "./libs"; import { queryEls, getRules, matchRule } from "./libs";
import { getSetting } from "./libs";
import { transPool } from "./libs/pool";
/** /**
* 翻译类 * 翻译类
@@ -127,6 +129,9 @@ class Translator {
* 入口函数 * 入口函数
*/ */
(async () => { (async () => {
const { fetchInterval, fetchLimit } = await getSetting();
transPool.update(fetchInterval, fetchLimit);
const rules = await getRules(); const rules = await getRules();
const rule = matchRule(rules, document.location.href); const rule = matchRule(rules, document.location.href);
const translator = new Translator(rule); const translator = new Translator(rule);

View File

@@ -1,14 +1,8 @@
import { useEffect } from "react"; import { useEffect } from "react";
import { useState } from "react"; import { useState } from "react";
import { apiTranslate } from "../apis"; import { transPool } from "../libs/pool";
import browser from "../libs/browser"; import browser from "../libs/browser";
import { import { MSG_TRANS_PUTRULE } from "../config";
MSG_TRANS_PUTRULE,
DEFAULT_FETCH_LIMIT,
MSG_FETCH_LIMIT,
} from "../config";
import { useSetting } from "./Setting";
import { sendMsg } from "../libs/msg";
import { detectLang } from "../libs"; import { detectLang } from "../libs";
/** /**
@@ -21,7 +15,6 @@ export function useTranslate(q, initRule) {
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const [sameLang, setSamelang] = useState(false); const [sameLang, setSamelang] = useState(false);
const [rule, setRule] = useState(initRule); const [rule, setRule] = useState(initRule);
const { fetchLimit = DEFAULT_FETCH_LIMIT } = useSetting() || {};
const { translator, fromLang, toLang, textStyle } = rule; const { translator, fromLang, toLang, textStyle } = rule;
@@ -39,10 +32,6 @@ export function useTranslate(q, initRule) {
}; };
}, []); }, []);
useEffect(() => {
sendMsg(MSG_FETCH_LIMIT, { limit: fetchLimit });
}, [fetchLimit]);
useEffect(() => { useEffect(() => {
(async () => { (async () => {
try { try {
@@ -51,7 +40,7 @@ export function useTranslate(q, initRule) {
if (toLang.includes(deLang)) { if (toLang.includes(deLang)) {
setSamelang(true); setSamelang(true);
} else { } else {
const [trText, isSame] = await apiTranslate({ const [trText, isSame] = await transPool.push({
translator, translator,
q, q,
fromLang, fromLang,

View File

@@ -2,117 +2,34 @@ import browser from "./browser";
import { sendMsg } from "./msg"; import { sendMsg } from "./msg";
import { import {
MSG_FETCH, MSG_FETCH,
DEFAULT_FETCH_LIMIT,
DEFAULT_FETCH_INTERVAL,
CACHE_NAME, CACHE_NAME,
OPT_TRANS_MICROSOFT, OPT_TRANS_MICROSOFT,
OPT_TRANS_OPENAI, OPT_TRANS_OPENAI,
} from "../config"; } from "../config";
import { msAuth } from "./auth";
import { getSetting } from ".";
/** /**
* request 改造因缓存必须是GET方法 * request 改造因缓存必须是GET方法
* @param {*} request * @param {*} request
* @returns * @returns
*/ */
const newCacheReq = async (request) => { const newCacheReq = async (request, translator) => {
if (request.method === "GET") {
return request;
}
const body = await request.clone().text();
const cacheUrl = new URL(request.url);
cacheUrl.pathname += body;
return new Request(cacheUrl.toString(), { method: "GET" });
};
/**
* request 改造,根据不同翻译服务
* @param {*} request
* @returns
*/
const newReq = async (request) => {
const translator = request.headers.get("X-Translator");
if (translator === OPT_TRANS_MICROSOFT) { if (translator === OPT_TRANS_MICROSOFT) {
const [token] = await msAuth(); request.headers.delete("Authorization");
request.headers.set("Authorization", `Bearer ${token}`);
} else if (translator === OPT_TRANS_OPENAI) { } else if (translator === OPT_TRANS_OPENAI) {
const { openaiKey } = await getSetting(); request.headers.delete("Authorization");
request.headers.set("Authorization", `Bearer ${openaiKey}`); // OpenAI request.headers.delete("api-key");
request.headers.set("api-key", openaiKey); // Azure OpenAI
} }
request.headers.delete("X-Translator");
if (request.method !== "GET") {
const body = await request.text();
const cacheUrl = new URL(request.url);
cacheUrl.pathname += body;
request = new Request(cacheUrl.toString(), { method: "GET" });
}
return request; return request;
}; };
/**
* 请求池
* @param {*} l
* @param {*} t
* @returns
*/
const _fetchPool = (l = 1, t = 1000) => {
let limitCount = l; // 限制并发数量
const intervalTime = t; // 请求间隔时间
const pool = []; // 请求池
const maxRetry = 2; // 最大重试次数
let currentCount = 0; // 当前请求数量
setInterval(async () => {
const count = limitCount - currentCount;
if (pool.length === 0 || count <= 0) {
return;
}
for (let i = 0; i < count; i++) {
const item = pool.shift();
if (item) {
const { request, resolve, reject, retry } = item;
currentCount++;
try {
const req = await request();
const res = await fetch(req);
resolve(res);
} catch (err) {
if (retry < maxRetry) {
pool.push({ request, resolve, reject, retry: retry + 1 });
} else {
reject(err);
}
} finally {
currentCount--;
}
}
}
}, intervalTime);
return [
async (req, usePool) => {
const request = () => newReq(req.clone());
if (usePool) {
return new Promise((resolve, reject) => {
pool.push({ request, resolve, reject, retry: 0 });
});
} else {
return fetch(await request());
}
},
(limit = -1) => {
if (limit >= 1 && limit <= 10 && limitCount !== limit) {
limitCount = limit;
}
},
];
};
export const [_fetch, setFetchLimit] = _fetchPool(
DEFAULT_FETCH_LIMIT,
DEFAULT_FETCH_INTERVAL
);
/** /**
* 调用fetch接口 * 调用fetch接口
* @param {*} input * @param {*} input
@@ -121,15 +38,17 @@ export const [_fetch, setFetchLimit] = _fetchPool(
*/ */
export const fetchData = async ( export const fetchData = async (
input, input,
{ useCache = false, usePool = false, ...init } = {} init,
{ useCache = false, translator } = {}
) => { ) => {
const req = new Request(input, init); const req = new Request(input, init);
const cacheReq = await newCacheReq(req); const cacheReq = await newCacheReq(req.clone(), translator);
const cache = await caches.open(CACHE_NAME); const cache = await caches.open(CACHE_NAME);
let res; let res;
// 查询缓存 // 查询缓存
if (useCache) { if (useCache) {
// console.log("usecache")
try { try {
res = await cache.match(cacheReq); res = await cache.match(cacheReq);
} catch (err) { } catch (err) {
@@ -139,7 +58,8 @@ export const fetchData = async (
// 发送请求 // 发送请求
if (!res) { if (!res) {
res = await _fetch(req, usePool); // console.log("usefetch")
res = await fetch(req);
} }
if (!res?.ok) { if (!res?.ok) {
@@ -166,12 +86,13 @@ export const fetchData = async (
* 兼容性封装 * 兼容性封装
* @param {*} input * @param {*} input
* @param {*} init * @param {*} init
* @param {*} opts
* @returns * @returns
*/ */
export const fetchPolyfill = async (input, init) => { export const fetchPolyfill = async (input, init, opts) => {
if (browser?.runtime) { if (browser?.runtime) {
// 插件调用 // 插件调用
const res = await sendMsg(MSG_FETCH, { input, init }); const res = await sendMsg(MSG_FETCH, { input, init, opts });
if (res.error) { if (res.error) {
throw new Error(res.error); throw new Error(res.error);
} }
@@ -179,5 +100,5 @@ export const fetchPolyfill = async (input, init) => {
} }
// 网页直接调用 // 网页直接调用
return await fetchData(input, init); return await fetchData(input, init, opts);
}; };

86
src/libs/pool.js Normal file
View File

@@ -0,0 +1,86 @@
import {
DEFAULT_FETCH_INTERVAL,
DEFAULT_FETCH_LIMIT,
OPT_TRANS_MICROSOFT,
} from "../config";
import { apiTranslate } from "../apis";
import { msAuth } from "./auth";
const _taskPool = (fn, preFn, _interval = 500, _limit = 100) => {
const pool = [];
const maxRetry = 2; // 最大重试次数
let maxCount = _limit; // 最大数量
let curCount = 0; // 当前数量
let interval = _interval; // 间隔时间
let timer;
const handleTask = async (item, preArgs) => {
curCount++;
const { args, resolve, reject, retry } = item;
try {
const res = await fn(args, preArgs);
resolve(res);
} catch (err) {
if (retry < maxRetry) {
pool.push({ args, resolve, reject, retry: retry + 1 });
} else {
reject(err);
}
} finally {
curCount--;
}
};
(async function run() {
// console.log("timer", timer);
if (curCount < maxCount) {
const item = pool.shift();
if (item) {
try {
const preArgs = await preFn(item.args);
handleTask(item, preArgs);
} catch (err) {
console.log("[preFn]", err);
pool.push(item);
}
}
}
timer && clearTimeout(timer);
timer = setTimeout(run, interval);
})();
return {
push: async (args) => {
return new Promise((resolve, reject) => {
pool.push({ args, resolve, reject, retry: 0 });
});
},
update: (_interval = 500, _limit = 100) => {
if (_interval >= 0 && _interval <= 5000 && _interval !== interval) {
interval = _interval;
}
if (_limit >= 1 && _limit <= 100 && _limit !== maxCount) {
maxCount = _limit;
}
},
clear: () => {
pool.length = 0;
curCount = 0;
timer && clearTimeout(timer);
},
};
};
export const transPool = _taskPool(
apiTranslate,
async ({ translator }) => {
if (translator === OPT_TRANS_MICROSOFT) {
const [token] = await msAuth();
return { token };
}
return {};
},
DEFAULT_FETCH_INTERVAL,
DEFAULT_FETCH_LIMIT
);

View File

@@ -23,6 +23,7 @@ export default function Settings() {
uiLang, uiLang,
googleUrl, googleUrl,
fetchLimit, fetchLimit,
fetchInterval,
openaiUrl, openaiUrl,
openaiKey, openaiKey,
openaiModel, openaiModel,
@@ -59,7 +60,19 @@ export default function Settings() {
defaultValue={fetchLimit} defaultValue={fetchLimit}
onChange={(e) => { onChange={(e) => {
updateSetting({ updateSetting({
fetchLimit: limitNumber(e.target.value, 1, 10), fetchLimit: limitNumber(e.target.value, 1, 100),
});
}}
/>
<TextField
size="small"
label={i18n("fetch_interval")}
type="number"
defaultValue={fetchInterval}
onChange={(e) => {
updateSetting({
fetchInterval: limitNumber(e.target.value, 0, 5000),
}); });
}} }}
/> />