Merge branch 'main' into imagine
This commit is contained in:
9 files changed
+156
-91
No files matched your search
@@ -37,7 +37,8 @@ DOWNLOAD_API = "https://assets.grok.com"
|
||||
LOCK_DIR = Path(__file__).parent.parent.parent.parent.parent / "data" / ".locks"
|
||||
|
||||
TIMEOUT = 120
|
||||
BROWSER = "chrome136"
|
||||
DEFAULT_BROWSER = "chrome136"
|
||||
DEFAULT_USER_AGENT = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36"
|
||||
DEFAULT_MIME = "application/octet-stream"
|
||||
|
||||
# 并发控制
|
||||
@@ -179,6 +180,7 @@ class BaseService:
|
||||
|
||||
def _headers(self, token: str, referer: str = "https://grok.com/") -> dict:
|
||||
"""构建请求头"""
|
||||
user_agent = get_config("grok.user_agent", DEFAULT_USER_AGENT)
|
||||
headers = {
|
||||
"Accept": "*/*",
|
||||
"Accept-Encoding": "gzip, deflate, br, zstd",
|
||||
@@ -199,7 +201,7 @@ class BaseService:
|
||||
"Sec-Fetch-Dest": "empty",
|
||||
"Sec-Fetch-Mode": "cors",
|
||||
"Sec-Fetch-Site": "same-origin",
|
||||
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36",
|
||||
"User-Agent": user_agent,
|
||||
}
|
||||
|
||||
apply_statsig(headers)
|
||||
@@ -354,7 +356,7 @@ class UploadService(BaseService):
|
||||
UPLOAD_API,
|
||||
headers=headers,
|
||||
json=payload,
|
||||
impersonate=BROWSER,
|
||||
impersonate=get_config("grok.browser", DEFAULT_BROWSER),
|
||||
timeout=self.timeout,
|
||||
proxies=self._proxies(),
|
||||
)
|
||||
@@ -448,7 +450,7 @@ class ListService(BaseService):
|
||||
LIST_API,
|
||||
headers=headers,
|
||||
params=params,
|
||||
impersonate=BROWSER,
|
||||
impersonate=get_config("grok.browser", DEFAULT_BROWSER),
|
||||
timeout=self.timeout,
|
||||
proxies=self._proxies(),
|
||||
)
|
||||
@@ -527,7 +529,7 @@ class DeleteService(BaseService):
|
||||
response = await session.delete(
|
||||
url,
|
||||
headers=headers,
|
||||
impersonate=BROWSER,
|
||||
impersonate=get_config("grok.browser", DEFAULT_BROWSER),
|
||||
timeout=self.timeout,
|
||||
proxies=self._proxies(),
|
||||
)
|
||||
@@ -663,7 +665,7 @@ class DownloadService(BaseService):
|
||||
proxies=self._proxies(),
|
||||
timeout=self.timeout,
|
||||
allow_redirects=True,
|
||||
impersonate=BROWSER,
|
||||
impersonate=get_config("grok.browser", DEFAULT_BROWSER),
|
||||
stream=True,
|
||||
)
|
||||
|
||||
|
||||
@@ -26,7 +26,8 @@ from app.services.token import get_token_manager, EffortType
|
||||
|
||||
CHAT_API = "https://grok.com/rest/app-chat/conversations/new"
|
||||
TIMEOUT = 120
|
||||
BROWSER = "chrome136"
|
||||
DEFAULT_BROWSER = "chrome136"
|
||||
DEFAULT_USER_AGENT = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36"
|
||||
|
||||
|
||||
@dataclass
|
||||
@@ -162,6 +163,7 @@ class ChatRequestBuilder:
|
||||
@staticmethod
|
||||
def build_headers(token: str) -> Dict[str, str]:
|
||||
"""构造请求头"""
|
||||
user_agent = get_config("grok.user_agent", DEFAULT_USER_AGENT)
|
||||
headers = {
|
||||
"Accept": "*/*",
|
||||
"Accept-Encoding": "gzip, deflate, br, zstd",
|
||||
@@ -182,7 +184,7 @@ class ChatRequestBuilder:
|
||||
"Sec-Fetch-Dest": "empty",
|
||||
"Sec-Fetch-Mode": "cors",
|
||||
"Sec-Fetch-Site": "same-origin",
|
||||
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36",
|
||||
"User-Agent": user_agent,
|
||||
}
|
||||
|
||||
apply_statsig(headers)
|
||||
@@ -321,7 +323,8 @@ class GrokChatService:
|
||||
# 建立连接函数
|
||||
async def establish_connection():
|
||||
"""建立连接并返回 response 对象"""
|
||||
session = AsyncSession(impersonate=BROWSER)
|
||||
browser = get_config("grok.browser", DEFAULT_BROWSER)
|
||||
session = AsyncSession(impersonate=browser)
|
||||
try:
|
||||
response = await session.post(
|
||||
CHAT_API,
|
||||
|
||||
@@ -26,7 +26,8 @@ CREATE_POST_API = "https://grok.com/rest/media/post/create"
|
||||
CHAT_API = "https://grok.com/rest/app-chat/conversations/new"
|
||||
|
||||
# 常量
|
||||
BROWSER = "chrome136"
|
||||
DEFAULT_BROWSER = "chrome136"
|
||||
DEFAULT_USER_AGENT = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36"
|
||||
TIMEOUT = 300
|
||||
DEFAULT_MAX_CONCURRENT = 50
|
||||
_MEDIA_SEMAPHORE = asyncio.Semaphore(DEFAULT_MAX_CONCURRENT)
|
||||
@@ -58,6 +59,7 @@ class VideoService:
|
||||
self, token: str, referer: str = "https://grok.com/imagine"
|
||||
) -> dict:
|
||||
"""构建请求头"""
|
||||
user_agent = get_config("grok.user_agent", DEFAULT_USER_AGENT)
|
||||
headers = {
|
||||
"Accept": "*/*",
|
||||
"Accept-Encoding": "gzip, deflate, br, zstd",
|
||||
@@ -78,7 +80,7 @@ class VideoService:
|
||||
"Sec-Fetch-Dest": "empty",
|
||||
"Sec-Fetch-Mode": "cors",
|
||||
"Sec-Fetch-Site": "same-origin",
|
||||
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36",
|
||||
"User-Agent": user_agent,
|
||||
}
|
||||
|
||||
apply_statsig(headers)
|
||||
@@ -119,11 +121,12 @@ class VideoService:
|
||||
payload = {"mediaType": media_type, "prompt": prompt}
|
||||
|
||||
async with AsyncSession() as session:
|
||||
browser = get_config("grok.browser", DEFAULT_BROWSER)
|
||||
response = await session.post(
|
||||
CREATE_POST_API,
|
||||
headers=headers,
|
||||
json=payload,
|
||||
impersonate=BROWSER,
|
||||
impersonate=browser,
|
||||
timeout=30,
|
||||
proxies=self._build_proxies(),
|
||||
)
|
||||
@@ -243,7 +246,8 @@ class VideoService:
|
||||
prompt, post_id, aspect_ratio, video_length, resolution_name, preset
|
||||
)
|
||||
|
||||
session = AsyncSession(impersonate=BROWSER)
|
||||
browser = get_config("grok.browser", DEFAULT_BROWSER)
|
||||
session = AsyncSession(impersonate=browser)
|
||||
response = await session.post(
|
||||
CHAT_API,
|
||||
headers=headers,
|
||||
@@ -323,7 +327,8 @@ class VideoService:
|
||||
prompt, post_id, aspect_ratio, video_length, resolution, preset
|
||||
)
|
||||
|
||||
session = AsyncSession(impersonate=BROWSER)
|
||||
browser = get_config("grok.browser", DEFAULT_BROWSER)
|
||||
session = AsyncSession(impersonate=browser)
|
||||
response = await session.post(
|
||||
CHAT_API,
|
||||
headers=headers,
|
||||
|
||||
@@ -23,7 +23,8 @@ from app.services.grok.utils.headers import build_sso_cookie
|
||||
|
||||
NSFW_API = "https://grok.com/auth_mgmt.AuthManagement/UpdateUserFeatureControls"
|
||||
BIRTH_DATE_API = "https://grok.com/rest/auth/set-birth-date"
|
||||
BROWSER = "chrome136"
|
||||
DEFAULT_BROWSER = "chrome136"
|
||||
DEFAULT_USER_AGENT = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36"
|
||||
TIMEOUT = 30
|
||||
|
||||
|
||||
@@ -61,12 +62,13 @@ class NSFWService:
|
||||
def _build_headers(self, token: str) -> dict:
|
||||
"""构造 gRPC-Web 请求头"""
|
||||
cookie = build_sso_cookie(token, include_rw=True)
|
||||
user_agent = get_config("grok.user_agent", DEFAULT_USER_AGENT)
|
||||
return {
|
||||
"accept": "*/*",
|
||||
"content-type": "application/grpc-web+proto",
|
||||
"origin": "https://grok.com",
|
||||
"referer": "https://grok.com/",
|
||||
"user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36",
|
||||
"user-agent": user_agent,
|
||||
"x-grpc-web": "1",
|
||||
"x-user-agent": "connect-es/2.1.1",
|
||||
"cookie": cookie,
|
||||
@@ -75,12 +77,13 @@ class NSFWService:
|
||||
def _build_birth_headers(self, token: str) -> dict:
|
||||
"""构造设置出生日期请求头"""
|
||||
cookie = build_sso_cookie(token, include_rw=True)
|
||||
user_agent = get_config("grok.user_agent", DEFAULT_USER_AGENT)
|
||||
return {
|
||||
"accept": "*/*",
|
||||
"content-type": "application/json",
|
||||
"origin": "https://grok.com",
|
||||
"referer": "https://grok.com/?_s=account",
|
||||
"user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36",
|
||||
"user-agent": user_agent,
|
||||
"cookie": cookie,
|
||||
}
|
||||
|
||||
@@ -127,7 +130,8 @@ class NSFWService:
|
||||
proxies = {"http": self.proxy, "https": self.proxy} if self.proxy else None
|
||||
|
||||
try:
|
||||
async with AsyncSession(impersonate=BROWSER) as session:
|
||||
browser = get_config("grok.browser", DEFAULT_BROWSER)
|
||||
async with AsyncSession(impersonate=browser) as session:
|
||||
ok, birth_status, birth_err = await self._set_birth_date(
|
||||
session, token
|
||||
)
|
||||
|
||||
@@ -15,7 +15,8 @@ from app.services.grok.utils.retry import retry_on_status
|
||||
|
||||
|
||||
LIMITS_API = "https://grok.com/rest/rate-limits"
|
||||
BROWSER = "chrome136"
|
||||
DEFAULT_BROWSER = "chrome136"
|
||||
DEFAULT_USER_AGENT = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36"
|
||||
TIMEOUT = 10
|
||||
DEFAULT_MAX_CONCURRENT = 25
|
||||
_USAGE_SEMAPHORE = asyncio.Semaphore(DEFAULT_MAX_CONCURRENT)
|
||||
@@ -45,6 +46,7 @@ class UsageService:
|
||||
|
||||
def _build_headers(self, token: str) -> dict:
|
||||
"""构建请求头"""
|
||||
user_agent = get_config("grok.user_agent", DEFAULT_USER_AGENT)
|
||||
headers = {
|
||||
"Accept": "*/*",
|
||||
"Accept-Encoding": "gzip, deflate, br, zstd",
|
||||
@@ -65,7 +67,7 @@ class UsageService:
|
||||
"Sec-Fetch-Dest": "empty",
|
||||
"Sec-Fetch-Mode": "cors",
|
||||
"Sec-Fetch-Site": "same-origin",
|
||||
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36",
|
||||
"User-Agent": user_agent,
|
||||
}
|
||||
|
||||
apply_statsig(headers)
|
||||
@@ -103,13 +105,14 @@ class UsageService:
|
||||
try:
|
||||
headers = self._build_headers(token)
|
||||
payload = {"requestKind": "DEFAULT", "modelName": model_name}
|
||||
browser = get_config("grok.browser", DEFAULT_BROWSER)
|
||||
|
||||
async with AsyncSession() as session:
|
||||
response = await session.post(
|
||||
LIMITS_API,
|
||||
headers=headers,
|
||||
json=payload,
|
||||
impersonate=BROWSER,
|
||||
impersonate=browser,
|
||||
timeout=self.timeout,
|
||||
proxies=self._build_proxies(),
|
||||
)
|
||||
|
||||
@@ -15,7 +15,7 @@ from app.services.grok.utils.headers import apply_statsig, build_sso_cookie
|
||||
|
||||
LIVEKIT_TOKEN_API = "https://grok.com/rest/livekit/tokens"
|
||||
TIMEOUT = 30
|
||||
BROWSER = "chrome136"
|
||||
DEFAULT_BROWSER = "chrome136"
|
||||
|
||||
|
||||
class VoiceService:
|
||||
@@ -45,7 +45,8 @@ class VoiceService:
|
||||
proxies = {"http": self.proxy, "https": self.proxy} if self.proxy else None
|
||||
|
||||
try:
|
||||
async with AsyncSession(impersonate=BROWSER) as session:
|
||||
browser = get_config("grok.browser", DEFAULT_BROWSER)
|
||||
async with AsyncSession(impersonate=browser) as session:
|
||||
response = await session.post(
|
||||
LIVEKIT_TOKEN_API,
|
||||
headers=headers,
|
||||
|
||||
@@ -50,7 +50,7 @@
|
||||
<script src="/static/common/header.js?v=3"></script>
|
||||
<script src="/static/common/footer.js?v=1"></script>
|
||||
<script src="/static/common/toast.js"></script>
|
||||
<script src="/static/config/config.js?v=8"></script>
|
||||
<script src="/static/config/config.js?v=13"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
+110
-67
@@ -43,6 +43,7 @@ const LOCALE_MAP = {
|
||||
"grok": {
|
||||
"label": "Grok 设置",
|
||||
"temporary": { title: "临时对话", desc: "是否启用临时对话模式。" },
|
||||
"disable_memory": { title: "禁用记忆", desc: "禁用 Grok 记忆功能,以防止响应中出现不相关上下文" },
|
||||
"stream": { title: "流式响应", desc: "是否默认启用流式输出。" },
|
||||
"thinking": { title: "思维链", desc: "是否启用模型思维链输出。" },
|
||||
"dynamic_statsig": { title: "动态指纹", desc: "是否启用动态生成 Statsig 值。" },
|
||||
@@ -50,7 +51,6 @@ const LOCALE_MAP = {
|
||||
"timeout": { title: "超时时间", desc: "请求 Grok 服务的超时时间(秒)。" },
|
||||
"base_proxy_url": { title: "基础代理 URL", desc: "代理请求到 Grok 官网的基础服务地址。" },
|
||||
"asset_proxy_url": { title: "资源代理 URL", desc: "代理请求到 Grok 官网的静态资源(图片/视频)地址。" },
|
||||
"cf_clearance": { title: "CF Clearance", desc: "Cloudflare 验证 Cookie,用于验证 Cloudflare 的验证。" },
|
||||
"image_ws": { title: "Imagine WebSocket 生成", desc: "启用后 /v1/images/generations 走 WebSocket 直连。" },
|
||||
"image_ws_nsfw": { title: "Imagine NSFW", desc: "WS 请求里是否启用 NSFW。" },
|
||||
"image_ws_blocked_seconds": { title: "Imagine WebSocket Blocked 阈值", desc: "收到中等图后超过该秒数仍无最终图则判定 blocked。" },
|
||||
@@ -62,7 +62,10 @@ const LOCALE_MAP = {
|
||||
"retry_backoff_max": { title: "退避上限", desc: "单次重试等待的最大延迟(秒)。" },
|
||||
"retry_budget": { title: "退避预算", desc: "单次请求的最大重试总耗时(秒)。" },
|
||||
"stream_idle_timeout": { title: "流空闲超时", desc: "流式响应空闲超时(秒),超过将断开。" },
|
||||
"video_idle_timeout": { title: "视频空闲超时", desc: "视频生成空闲超时(秒),超过将断开。" }
|
||||
"video_idle_timeout": { title: "视频空闲超时", desc: "视频生成空闲超时(秒),超过将断开。" },
|
||||
"cf_clearance": { title: "CF Clearance", desc: "" },
|
||||
"browser": { title: "curl_cffi", desc: "" },
|
||||
"user_agent": { title: "User-Agent", desc: "" }
|
||||
},
|
||||
"token": {
|
||||
"label": "Token 池设置",
|
||||
@@ -94,6 +97,13 @@ const LOCALE_MAP = {
|
||||
}
|
||||
};
|
||||
|
||||
// 反爬虫验证字段(从 grok 中单独分组显示)
|
||||
const ANTIBOT_FIELDS = ['cf_clearance', 'browser', 'user_agent'];
|
||||
const ANTIBOT_GROUP = {
|
||||
label: "反爬虫验证",
|
||||
desc: "以下三个参数用于绕过 Cloudflare 反爬虫验证。配置不正确将导致 403 错误。服务首次请求 Grok 时的 IP 必须与获取 CF Clearance 时的 IP 一致,后续服务器请求 IP 变化不会导致403。"
|
||||
};
|
||||
|
||||
const SECTION_ORDER = new Map(Object.keys(LOCALE_MAP).map((key, index) => [key, index]));
|
||||
|
||||
function getText(section, key) {
|
||||
@@ -240,80 +250,53 @@ function renderConfig(data) {
|
||||
const localeSection = LOCALE_MAP[section];
|
||||
const keyOrder = localeSection ? new Map(Object.keys(localeSection).map((k, i) => [k, i])) : null;
|
||||
|
||||
const card = document.createElement('div');
|
||||
card.className = 'config-section';
|
||||
// 分离反爬虫字段和普通字段
|
||||
const allKeys = sortByOrder(Object.keys(items), keyOrder);
|
||||
const normalKeys = section === 'grok' ? allKeys.filter(k => !ANTIBOT_FIELDS.includes(k)) : allKeys;
|
||||
const antibotKeys = section === 'grok' ? allKeys.filter(k => ANTIBOT_FIELDS.includes(k)) : [];
|
||||
|
||||
const header = document.createElement('div');
|
||||
header.innerHTML = `<div class="config-section-title">${getSectionLabel(section)}</div>`;
|
||||
card.appendChild(header);
|
||||
// 渲染普通字段
|
||||
if (normalKeys.length > 0) {
|
||||
const card = document.createElement('div');
|
||||
card.className = 'config-section';
|
||||
|
||||
const grid = document.createElement('div');
|
||||
grid.className = 'config-grid';
|
||||
const header = document.createElement('div');
|
||||
header.innerHTML = `<div class="config-section-title">${getSectionLabel(section)}</div>`;
|
||||
card.appendChild(header);
|
||||
|
||||
const keys = sortByOrder(Object.keys(items), keyOrder);
|
||||
const grid = document.createElement('div');
|
||||
grid.className = 'config-grid';
|
||||
|
||||
keys.forEach(key => {
|
||||
const val = items[key];
|
||||
const text = getText(section, key);
|
||||
normalKeys.forEach(key => {
|
||||
const fieldCard = buildFieldCard(section, key, items[key]);
|
||||
grid.appendChild(fieldCard);
|
||||
});
|
||||
|
||||
// Container
|
||||
const fieldCard = document.createElement('div');
|
||||
fieldCard.className = 'config-field';
|
||||
|
||||
// Title
|
||||
const titleEl = document.createElement('div');
|
||||
titleEl.className = 'config-field-title';
|
||||
titleEl.textContent = text.title;
|
||||
fieldCard.appendChild(titleEl);
|
||||
|
||||
// Description (Muted)
|
||||
const descEl = document.createElement('p');
|
||||
descEl.className = 'config-field-desc';
|
||||
descEl.textContent = text.desc;
|
||||
fieldCard.appendChild(descEl);
|
||||
|
||||
// Input Wrapper
|
||||
const inputWrapper = document.createElement('div');
|
||||
inputWrapper.className = 'config-field-input';
|
||||
|
||||
// Input Logic
|
||||
let built;
|
||||
if (typeof val === 'boolean') {
|
||||
built = buildBooleanInput(section, key, val);
|
||||
}
|
||||
else if (key === 'image_format') {
|
||||
built = buildSelectInput(section, key, val, [
|
||||
{ val: 'url', text: 'URL' },
|
||||
{ val: 'base64', text: 'Base64' }
|
||||
]);
|
||||
}
|
||||
else if (key === 'video_format') {
|
||||
built = buildSelectInput(section, key, val, [
|
||||
{ val: 'html', text: 'HTML' },
|
||||
{ val: 'url', text: 'URL' }
|
||||
]);
|
||||
}
|
||||
else if (Array.isArray(val) || typeof val === 'object') {
|
||||
built = buildJsonInput(section, key, val);
|
||||
}
|
||||
else {
|
||||
if (key === 'api_key' || key === 'app_key') {
|
||||
built = buildSecretInput(section, key, val);
|
||||
} else {
|
||||
built = buildTextInput(section, key, val);
|
||||
}
|
||||
card.appendChild(grid);
|
||||
if (grid.children.length > 0) {
|
||||
fragment.appendChild(card);
|
||||
}
|
||||
}
|
||||
|
||||
if (built) {
|
||||
inputWrapper.appendChild(built.node);
|
||||
}
|
||||
fieldCard.appendChild(inputWrapper);
|
||||
grid.appendChild(fieldCard);
|
||||
});
|
||||
// 渲染反爬虫验证分组(仅 grok section)
|
||||
if (antibotKeys.length > 0) {
|
||||
const card = document.createElement('div');
|
||||
card.className = 'config-section';
|
||||
|
||||
card.appendChild(grid);
|
||||
const header = document.createElement('div');
|
||||
header.innerHTML = `<div class="config-section-title">${ANTIBOT_GROUP.label}</div>
|
||||
<p class="text-[var(--accents-4)] text-sm mt-1 mb-4">${ANTIBOT_GROUP.desc}</p>`;
|
||||
card.appendChild(header);
|
||||
|
||||
if (grid.children.length > 0) {
|
||||
const grid = document.createElement('div');
|
||||
grid.className = 'config-grid';
|
||||
|
||||
antibotKeys.forEach(key => {
|
||||
const fieldCard = buildFieldCard(section, key, items[key]);
|
||||
grid.appendChild(fieldCard);
|
||||
});
|
||||
|
||||
card.appendChild(grid);
|
||||
fragment.appendChild(card);
|
||||
}
|
||||
});
|
||||
@@ -321,6 +304,66 @@ function renderConfig(data) {
|
||||
container.appendChild(fragment);
|
||||
}
|
||||
|
||||
function buildFieldCard(section, key, val) {
|
||||
const text = getText(section, key);
|
||||
|
||||
const fieldCard = document.createElement('div');
|
||||
fieldCard.className = 'config-field';
|
||||
|
||||
// Title
|
||||
const titleEl = document.createElement('div');
|
||||
titleEl.className = 'config-field-title';
|
||||
titleEl.textContent = text.title;
|
||||
fieldCard.appendChild(titleEl);
|
||||
|
||||
// Description (Muted) - 只在有描述时显示
|
||||
if (text.desc) {
|
||||
const descEl = document.createElement('p');
|
||||
descEl.className = 'config-field-desc';
|
||||
descEl.textContent = text.desc;
|
||||
fieldCard.appendChild(descEl);
|
||||
}
|
||||
|
||||
// Input Wrapper
|
||||
const inputWrapper = document.createElement('div');
|
||||
inputWrapper.className = 'config-field-input';
|
||||
|
||||
// Input Logic
|
||||
let built;
|
||||
if (typeof val === 'boolean') {
|
||||
built = buildBooleanInput(section, key, val);
|
||||
}
|
||||
else if (key === 'image_format') {
|
||||
built = buildSelectInput(section, key, val, [
|
||||
{ val: 'url', text: 'URL' },
|
||||
{ val: 'base64', text: 'Base64' }
|
||||
]);
|
||||
}
|
||||
else if (key === 'video_format') {
|
||||
built = buildSelectInput(section, key, val, [
|
||||
{ val: 'html', text: 'HTML' },
|
||||
{ val: 'url', text: 'URL' }
|
||||
]);
|
||||
}
|
||||
else if (Array.isArray(val) || typeof val === 'object') {
|
||||
built = buildJsonInput(section, key, val);
|
||||
}
|
||||
else {
|
||||
if (key === 'api_key' || key === 'app_key') {
|
||||
built = buildSecretInput(section, key, val);
|
||||
} else {
|
||||
built = buildTextInput(section, key, val);
|
||||
}
|
||||
}
|
||||
|
||||
if (built) {
|
||||
inputWrapper.appendChild(built.node);
|
||||
}
|
||||
fieldCard.appendChild(inputWrapper);
|
||||
|
||||
return fieldCard;
|
||||
}
|
||||
|
||||
async function saveConfig() {
|
||||
const btn = byId('save-btn');
|
||||
const originalText = btn.innerText;
|
||||
|
||||
@@ -8,6 +8,10 @@ timeout = 120
|
||||
base_proxy_url = ""
|
||||
asset_proxy_url = ""
|
||||
cf_clearance = ""
|
||||
# 浏览器指纹配置
|
||||
browser = "chrome136"
|
||||
user_agent = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36"
|
||||
disable_memory = true
|
||||
max_retry = 3
|
||||
retry_status_codes = [401,429,403]
|
||||
image_ws = true
|
||||
|
||||
Reference in new issue
Block a user