mirror of
https://github.com/TheSmallHanCat/flow2api.git
synced 2026-06-20 17:06:14 +08:00
fix: 修复各个代码文件中对action参数的调用
- 将FLOW_GENERATION替换为IMAGE_GENERATION/VIDEO_GENERATION - browser_captcha_personal.py: get_token/execute方法支持action参数 - flow_client.py: _get_api_captcha_token支持动态action - 更新数据库和模型的默认值 - 添加GitHub Actions工作流用于构建ghcr.io镜像 Co-Authored-By: Warp <agent@warp.dev>
This commit is contained in:
64
.github/workflows/docker-publish.yml
vendored
Normal file
64
.github/workflows/docker-publish.yml
vendored
Normal file
@@ -0,0 +1,64 @@
|
||||
name: Build and Push Docker Image
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
tags:
|
||||
- 'v*'
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
REGISTRY: ghcr.io
|
||||
IMAGE_NAME: ${{ github.repository }}
|
||||
|
||||
jobs:
|
||||
build-and-push:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Log in to Container Registry
|
||||
if: github.event_name != 'pull_request'
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Extract metadata (tags, labels)
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
||||
tags: |
|
||||
type=ref,event=branch
|
||||
type=ref,event=pr
|
||||
type=semver,pattern={{version}}
|
||||
type=semver,pattern={{major}}.{{minor}}
|
||||
type=raw,value=latest,enable={{is_default_branch}}
|
||||
|
||||
- name: Build and push Docker image
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
platforms: linux/amd64,linux/arm64
|
||||
push: ${{ github.event_name != 'pull_request' }}
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -57,4 +57,5 @@ browser_data
|
||||
|
||||
data
|
||||
config/setting.toml
|
||||
config/setting_warp.toml
|
||||
config/setting_warp.toml
|
||||
config/setting_warp_example.toml
|
||||
|
||||
35
Dockerfile
35
Dockerfile
@@ -2,40 +2,9 @@ FROM python:3.11-slim
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# 使用清华镜像源加速 apt (Debian bookworm)
|
||||
RUN sed -i 's|deb.debian.org|mirrors.tuna.tsinghua.edu.cn|g' /etc/apt/sources.list.d/debian.sources \
|
||||
&& sed -i 's|security.debian.org|mirrors.tuna.tsinghua.edu.cn|g' /etc/apt/sources.list.d/debian.sources
|
||||
|
||||
# 安装 Playwright 所需的系统依赖
|
||||
RUN apt-get update && apt-get install -y \
|
||||
libnss3 \
|
||||
libnspr4 \
|
||||
libatk1.0-0 \
|
||||
libatk-bridge2.0-0 \
|
||||
libcups2 \
|
||||
libdrm2 \
|
||||
libxkbcommon0 \
|
||||
libxcomposite1 \
|
||||
libxdamage1 \
|
||||
libxfixes3 \
|
||||
libxrandr2 \
|
||||
libgbm1 \
|
||||
libasound2 \
|
||||
libpango-1.0-0 \
|
||||
libcairo2 \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# 安装 Python 依赖(使用清华 PyPI 镜像)
|
||||
# 安装 Python 依赖
|
||||
COPY requirements.txt .
|
||||
RUN pip install --no-cache-dir -r requirements.txt \
|
||||
-i https://pypi.tuna.tsinghua.edu.cn/simple/ \
|
||||
--trusted-host pypi.tuna.tsinghua.edu.cn
|
||||
|
||||
# 设置 Playwright 下载镜像(使用 npmmirror)
|
||||
ENV PLAYWRIGHT_DOWNLOAD_HOST=https://registry.npmmirror.com/-/binary/playwright
|
||||
|
||||
# 安装 Playwright 浏览器
|
||||
RUN playwright install chromium
|
||||
RUN pip install --no-cache-dir --root-user-action=ignore -r requirements.txt
|
||||
|
||||
COPY . .
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ max_poll_attempts = 200
|
||||
|
||||
[server]
|
||||
host = "0.0.0.0"
|
||||
port = 18282
|
||||
port = 8000
|
||||
|
||||
[debug]
|
||||
enabled = false
|
||||
@@ -21,8 +21,8 @@ log_responses = true
|
||||
mask_token = true
|
||||
|
||||
[proxy]
|
||||
proxy_enabled = true
|
||||
proxy_url = "http://localhost:7897"
|
||||
proxy_enabled = false
|
||||
proxy_url = ""
|
||||
|
||||
[generation]
|
||||
image_timeout = 300
|
||||
|
||||
@@ -12,7 +12,7 @@ max_poll_attempts = 200
|
||||
|
||||
[server]
|
||||
host = "0.0.0.0"
|
||||
port = 18282
|
||||
port = 8000
|
||||
|
||||
[debug]
|
||||
enabled = false
|
||||
@@ -21,8 +21,8 @@ log_responses = true
|
||||
mask_token = true
|
||||
|
||||
[proxy]
|
||||
proxy_enabled = true
|
||||
proxy_url = "http://localhost:7897"
|
||||
proxy_enabled = false
|
||||
proxy_url = ""
|
||||
|
||||
[generation]
|
||||
image_timeout = 300
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
[global]
|
||||
api_key = "han1234"
|
||||
admin_username = "admin"
|
||||
admin_password = "admin"
|
||||
|
||||
[flow]
|
||||
labs_base_url = "https://labs.google/fx/api"
|
||||
api_base_url = "https://aisandbox-pa.googleapis.com/v1"
|
||||
timeout = 120
|
||||
poll_interval = 3.0
|
||||
max_poll_attempts = 200
|
||||
|
||||
[server]
|
||||
host = "0.0.0.0"
|
||||
port = 8000
|
||||
|
||||
[debug]
|
||||
enabled = false
|
||||
log_requests = true
|
||||
log_responses = true
|
||||
mask_token = true
|
||||
|
||||
[proxy]
|
||||
proxy_enabled = true
|
||||
proxy_url = "socks5://warp:1080"
|
||||
|
||||
[generation]
|
||||
image_timeout = 300
|
||||
video_timeout = 1500
|
||||
|
||||
[admin]
|
||||
error_ban_threshold = 3
|
||||
|
||||
[cache]
|
||||
enabled = false
|
||||
timeout = 7200 # 缓存超时时间(秒), 默认2小时
|
||||
base_url = "" # 缓存文件访问的基础URL, 留空则使用服务器地址
|
||||
|
||||
[captcha]
|
||||
captcha_method = "browser" # 打码方式: yescaptcha 或 browser
|
||||
yescaptcha_api_key = "" # YesCaptcha API密钥
|
||||
yescaptcha_base_url = "https://api.yescaptcha.com"
|
||||
@@ -1,42 +0,0 @@
|
||||
[global]
|
||||
api_key = "han1234"
|
||||
admin_username = "admin"
|
||||
admin_password = "admin"
|
||||
|
||||
[flow]
|
||||
labs_base_url = "https://labs.google/fx/api"
|
||||
api_base_url = "https://aisandbox-pa.googleapis.com/v1"
|
||||
timeout = 120
|
||||
poll_interval = 3.0
|
||||
max_poll_attempts = 200
|
||||
|
||||
[server]
|
||||
host = "0.0.0.0"
|
||||
port = 8000
|
||||
|
||||
[debug]
|
||||
enabled = false
|
||||
log_requests = true
|
||||
log_responses = true
|
||||
mask_token = true
|
||||
|
||||
[proxy]
|
||||
proxy_enabled = true
|
||||
proxy_url = "socks5://warp:1080"
|
||||
|
||||
[generation]
|
||||
image_timeout = 300
|
||||
video_timeout = 1500
|
||||
|
||||
[admin]
|
||||
error_ban_threshold = 3
|
||||
|
||||
[cache]
|
||||
enabled = false
|
||||
timeout = 7200 # 缓存超时时间(秒), 默认2小时
|
||||
base_url = "" # 缓存文件访问的基础URL, 留空则使用服务器地址
|
||||
|
||||
[captcha]
|
||||
captcha_method = "browser" # 打码方式: yescaptcha 或 browser
|
||||
yescaptcha_api_key = "" # YesCaptcha API密钥
|
||||
yescaptcha_base_url = "https://api.yescaptcha.com"
|
||||
150
request.py
150
request.py
@@ -1,150 +0,0 @@
|
||||
import os
|
||||
import json
|
||||
import re
|
||||
import base64
|
||||
import aiohttp # Async test. Need to install
|
||||
import asyncio
|
||||
|
||||
|
||||
# --- 配置区域 ---
|
||||
BASE_URL = os.getenv('GEMINI_FLOW2API_URL', 'http://127.0.0.1:8000')
|
||||
BACKEND_URL = BASE_URL + "/v1/chat/completions"
|
||||
API_KEY = os.getenv('GEMINI_FLOW2API_APIKEY', 'Bearer han1234')
|
||||
if API_KEY is None:
|
||||
raise ValueError('[gemini flow2api] api key not set')
|
||||
MODEL_LANDSCAPE = "gemini-3.0-pro-image-landscape"
|
||||
MODEL_PORTRAIT = "gemini-3.0-pro-image-portrait"
|
||||
|
||||
# 修改: 增加 model 参数,默认为 None
|
||||
async def request_backend_generation(
|
||||
prompt: str,
|
||||
images: list[bytes] = None,
|
||||
model: str = None) -> bytes | None:
|
||||
"""
|
||||
请求后端生成图片。
|
||||
:param prompt: 提示词
|
||||
:param images: 图片二进制列表
|
||||
:param model: 指定模型名称 (可选)
|
||||
:return: 成功返回图片bytes,失败返回None
|
||||
"""
|
||||
# 更新token
|
||||
images = images or []
|
||||
|
||||
# 逻辑: 如果未指定 model,默认使用 Landscape
|
||||
use_model = model if model else MODEL_LANDSCAPE
|
||||
|
||||
# 1. 构造 Payload
|
||||
if images:
|
||||
content_payload = [{"type": "text", "text": prompt}]
|
||||
print(f"[Backend] 正在处理 {len(images)} 张图片输入...")
|
||||
for img_bytes in images:
|
||||
b64_str = base64.b64encode(img_bytes).decode('utf-8')
|
||||
content_payload.append({
|
||||
"type": "image_url",
|
||||
"image_url": {"url": f"data:image/jpeg;base64,{b64_str}"}
|
||||
})
|
||||
else:
|
||||
content_payload = prompt
|
||||
|
||||
payload = {
|
||||
"model": use_model, # 使用选定的模型
|
||||
"messages": [{"role": "user", "content": content_payload}],
|
||||
"stream": True
|
||||
}
|
||||
|
||||
headers = {
|
||||
"Authorization": API_KEY,
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
|
||||
image_url = None
|
||||
print(f"[Backend] Model: {use_model} | 发起请求: {prompt[:20]}...")
|
||||
|
||||
try:
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.post(BACKEND_URL, json=payload, headers=headers, timeout=120) as response:
|
||||
if response.status != 200:
|
||||
err_text = await response.text()
|
||||
content = response.content
|
||||
print(f"[Backend Error] Status {response.status}: {err_text} {content}")
|
||||
raise Exception(f"API Error: {response.status}: {err_text}")
|
||||
|
||||
async for line in response.content:
|
||||
line_str = line.decode('utf-8').strip()
|
||||
if line_str.startswith('{"error'):
|
||||
chunk = json.loads(data_str)
|
||||
delta = chunk.get("choices", [{}])[0].get("delta", {})
|
||||
msg = delta['reasoning_content']
|
||||
if '401' in msg:
|
||||
msg += '\nAccess Token 已失效,需重新配置。'
|
||||
elif '400' in msg:
|
||||
msg += '\n返回内容被拦截。'
|
||||
raise Exception(msg)
|
||||
|
||||
if not line_str or not line_str.startswith('data: '):
|
||||
continue
|
||||
|
||||
data_str = line_str[6:]
|
||||
if data_str == '[DONE]':
|
||||
break
|
||||
|
||||
try:
|
||||
chunk = json.loads(data_str)
|
||||
delta = chunk.get("choices", [{}])[0].get("delta", {})
|
||||
|
||||
# 打印思考过程
|
||||
if "reasoning_content" in delta:
|
||||
print(delta['reasoning_content'], end="", flush=True)
|
||||
|
||||
# 提取内容中的图片链接
|
||||
if "content" in delta:
|
||||
content_text = delta["content"]
|
||||
img_match = re.search(r'!\[.*?\]\((.*?)\)', content_text)
|
||||
if img_match:
|
||||
image_url = img_match.group(1)
|
||||
print(f"\n[Backend] 捕获图片链接: {image_url}")
|
||||
except json.JSONDecodeError:
|
||||
continue
|
||||
|
||||
# 3. 下载生成的图片
|
||||
if image_url:
|
||||
async with session.get(image_url) as img_resp:
|
||||
if img_resp.status == 200:
|
||||
image_bytes = await img_resp.read()
|
||||
return image_bytes
|
||||
else:
|
||||
print(f"[Backend Error] 图片下载失败: {img_resp.status}")
|
||||
except Exception as e:
|
||||
print(f"[Backend Exception] {e}")
|
||||
raise e
|
||||
|
||||
return None
|
||||
|
||||
if __name__ == '__main__':
|
||||
async def main():
|
||||
print("=== AI 绘图接口测试 ===")
|
||||
user_prompt = input("请输入提示词 (例如 '一只猫'): ").strip()
|
||||
if not user_prompt:
|
||||
user_prompt = "A cute cat in the garden"
|
||||
|
||||
print(f"正在请求: {user_prompt}")
|
||||
|
||||
# 这里的 images 传空列表用于测试文生图
|
||||
# 如果想测试图生图,你需要手动读取本地文件:
|
||||
# with open("output_test.jpg", "rb") as f: img_data = f.read()
|
||||
# result = await request_backend_generation(user_prompt, [img_data])
|
||||
|
||||
result = await request_backend_generation(user_prompt)
|
||||
|
||||
if result:
|
||||
filename = "output_test.jpg"
|
||||
with open(filename, "wb") as f:
|
||||
f.write(result)
|
||||
print(f"\n[Success] 图片已保存为 {filename},大小: {len(result)} bytes")
|
||||
else:
|
||||
print("\n[Failed] 生成失败")
|
||||
|
||||
# 运行测试
|
||||
if os.name == 'nt': # Windows 兼容性
|
||||
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
|
||||
asyncio.run(main())
|
||||
@@ -223,7 +223,7 @@ class Database:
|
||||
capsolver_api_key TEXT DEFAULT '',
|
||||
capsolver_base_url TEXT DEFAULT 'https://api.capsolver.com',
|
||||
website_key TEXT DEFAULT '6LdsFiUsAAAAAIjVDZcuLhaHiDn5nnHVXVRQGeMV',
|
||||
page_action TEXT DEFAULT 'FLOW_GENERATION',
|
||||
page_action TEXT DEFAULT 'IMAGE_GENERATION',
|
||||
browser_proxy_enabled BOOLEAN DEFAULT 0,
|
||||
browser_proxy_url TEXT,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
@@ -509,7 +509,8 @@ class Database:
|
||||
capsolver_api_key TEXT DEFAULT '',
|
||||
capsolver_base_url TEXT DEFAULT 'https://api.capsolver.com',
|
||||
website_key TEXT DEFAULT '6LdsFiUsAAAAAIjVDZcuLhaHiDn5nnHVXVRQGeMV',
|
||||
page_action TEXT DEFAULT 'FLOW_GENERATION',
|
||||
page_action TEXT DEFAULT 'IMAGE_GENERATION',
|
||||
|
||||
browser_proxy_enabled BOOLEAN DEFAULT 0,
|
||||
browser_proxy_url TEXT,
|
||||
browser_count INTEGER DEFAULT 1,
|
||||
|
||||
@@ -157,7 +157,7 @@ class CaptchaConfig(BaseModel):
|
||||
capsolver_api_key: str = ""
|
||||
capsolver_base_url: str = "https://api.capsolver.com"
|
||||
website_key: str = "6LdsFiUsAAAAAIjVDZcuLhaHiDn5nnHVXVRQGeMV"
|
||||
page_action: str = "FLOW_GENERATION"
|
||||
page_action: str = "IMAGE_GENERATION"
|
||||
browser_proxy_enabled: bool = False # 浏览器打码是否启用代理
|
||||
browser_proxy_url: Optional[str] = None # 浏览器打码代理URL
|
||||
browser_count: int = 1 # 浏览器打码实例数量
|
||||
|
||||
@@ -251,11 +251,12 @@ class BrowserCaptchaService:
|
||||
debug_logger.log_warning("[BrowserCaptcha] reCAPTCHA 加载超时")
|
||||
return False
|
||||
|
||||
async def _execute_recaptcha_on_tab(self, tab) -> Optional[str]:
|
||||
async def _execute_recaptcha_on_tab(self, tab, action: str = "IMAGE_GENERATION") -> Optional[str]:
|
||||
"""在指定标签页执行 reCAPTCHA 获取 token
|
||||
|
||||
Args:
|
||||
tab: nodriver 标签页对象
|
||||
action: reCAPTCHA action类型 (IMAGE_GENERATION 或 VIDEO_GENERATION)
|
||||
|
||||
Returns:
|
||||
reCAPTCHA token 或 None
|
||||
@@ -272,7 +273,7 @@ class BrowserCaptchaService:
|
||||
|
||||
try {{
|
||||
grecaptcha.enterprise.ready(function() {{
|
||||
grecaptcha.enterprise.execute('{self.website_key}', {{action: 'FLOW_GENERATION'}})
|
||||
grecaptcha.enterprise.execute('{self.website_key}', {{action: '{action}'}})
|
||||
.then(function(token) {{
|
||||
window.{token_var} = token;
|
||||
}})
|
||||
@@ -311,13 +312,16 @@ class BrowserCaptchaService:
|
||||
|
||||
# ========== 主要 API ==========
|
||||
|
||||
async def get_token(self, project_id: str) -> Optional[str]:
|
||||
async def get_token(self, project_id: str, action: str = "IMAGE_GENERATION") -> Optional[str]:
|
||||
"""获取 reCAPTCHA token
|
||||
|
||||
自动常驻模式:如果该 project_id 没有常驻标签页,则自动创建并常驻
|
||||
|
||||
Args:
|
||||
project_id: Flow项目ID
|
||||
action: reCAPTCHA action类型
|
||||
- IMAGE_GENERATION: 图片生成和2K/4K图片放大 (默认)
|
||||
- VIDEO_GENERATION: 视频生成和视频放大
|
||||
|
||||
Returns:
|
||||
reCAPTCHA token字符串,如果获取失败返回None
|
||||
@@ -335,16 +339,16 @@ class BrowserCaptchaService:
|
||||
resident_info = await self._create_resident_tab(project_id)
|
||||
if resident_info is None:
|
||||
debug_logger.log_warning(f"[BrowserCaptcha] 无法为 project_id={project_id} 创建常驻标签页,fallback 到传统模式")
|
||||
return await self._get_token_legacy(project_id)
|
||||
return await self._get_token_legacy(project_id, action)
|
||||
self._resident_tabs[project_id] = resident_info
|
||||
debug_logger.log_info(f"[BrowserCaptcha] ✅ 已为 project_id={project_id} 创建常驻标签页 (当前共 {len(self._resident_tabs)} 个)")
|
||||
|
||||
# 使用常驻标签页生成 token
|
||||
if resident_info and resident_info.recaptcha_ready and resident_info.tab:
|
||||
start_time = time.time()
|
||||
debug_logger.log_info(f"[BrowserCaptcha] 从常驻标签页即时生成 token (project: {project_id})...")
|
||||
debug_logger.log_info(f"[BrowserCaptcha] 从常驻标签页即时生成 token (project: {project_id}, action: {action})...")
|
||||
try:
|
||||
token = await self._execute_recaptcha_on_tab(resident_info.tab)
|
||||
token = await self._execute_recaptcha_on_tab(resident_info.tab, action)
|
||||
duration_ms = (time.time() - start_time) * 1000
|
||||
if token:
|
||||
debug_logger.log_info(f"[BrowserCaptcha] ✅ Token生成成功(耗时 {duration_ms:.0f}ms)")
|
||||
@@ -362,7 +366,7 @@ class BrowserCaptchaService:
|
||||
self._resident_tabs[project_id] = resident_info
|
||||
# 重建后立即尝试生成
|
||||
try:
|
||||
token = await self._execute_recaptcha_on_tab(resident_info.tab)
|
||||
token = await self._execute_recaptcha_on_tab(resident_info.tab, action)
|
||||
if token:
|
||||
debug_logger.log_info(f"[BrowserCaptcha] ✅ 重建后 Token生成成功")
|
||||
return token
|
||||
@@ -371,7 +375,7 @@ class BrowserCaptchaService:
|
||||
|
||||
# 最终 Fallback: 使用传统模式
|
||||
debug_logger.log_warning(f"[BrowserCaptcha] 所有常驻方式失败,fallback 到传统模式 (project: {project_id})")
|
||||
return await self._get_token_legacy(project_id)
|
||||
return await self._get_token_legacy(project_id, action)
|
||||
|
||||
async def _create_resident_tab(self, project_id: str) -> Optional[ResidentTabInfo]:
|
||||
"""为指定 project_id 创建常驻标签页
|
||||
@@ -449,11 +453,12 @@ class BrowserCaptchaService:
|
||||
except Exception as e:
|
||||
debug_logger.log_warning(f"[BrowserCaptcha] 关闭标签页时异常: {e}")
|
||||
|
||||
async def _get_token_legacy(self, project_id: str) -> Optional[str]:
|
||||
async def _get_token_legacy(self, project_id: str, action: str = "IMAGE_GENERATION") -> Optional[str]:
|
||||
"""传统模式获取 reCAPTCHA token(每次创建新标签页)
|
||||
|
||||
Args:
|
||||
project_id: Flow项目ID
|
||||
action: reCAPTCHA action类型 (IMAGE_GENERATION 或 VIDEO_GENERATION)
|
||||
|
||||
Returns:
|
||||
reCAPTCHA token字符串,如果获取失败返回None
|
||||
@@ -491,8 +496,8 @@ class BrowserCaptchaService:
|
||||
return None
|
||||
|
||||
# 执行 reCAPTCHA
|
||||
debug_logger.log_info("[BrowserCaptcha] [Legacy] 执行 reCAPTCHA 验证...")
|
||||
token = await self._execute_recaptcha_on_tab(tab)
|
||||
debug_logger.log_info(f"[BrowserCaptcha] [Legacy] 执行 reCAPTCHA 验证 (action: {action})...")
|
||||
token = await self._execute_recaptcha_on_tab(tab, action)
|
||||
|
||||
duration_ms = (time.time() - start_time) * 1000
|
||||
|
||||
|
||||
@@ -1177,14 +1177,20 @@ class FlowClient:
|
||||
return None, None
|
||||
# API打码服务
|
||||
elif captcha_method in ["yescaptcha", "capmonster", "ezcaptcha", "capsolver"]:
|
||||
token = await self._get_api_captcha_token(captcha_method, project_id)
|
||||
token = await self._get_api_captcha_token(captcha_method, project_id, action)
|
||||
return token, None
|
||||
else:
|
||||
debug_logger.log_info(f"[reCAPTCHA] 未知的打码方式: {captcha_method}")
|
||||
return None, None
|
||||
|
||||
async def _get_api_captcha_token(self, method: str, project_id: str) -> Optional[str]:
|
||||
"""通用API打码服务"""
|
||||
async def _get_api_captcha_token(self, method: str, project_id: str, action: str = "IMAGE_GENERATION") -> Optional[str]:
|
||||
"""通用API打码服务
|
||||
|
||||
Args:
|
||||
method: 打码服务类型
|
||||
project_id: 项目ID
|
||||
action: reCAPTCHA action类型 (IMAGE_GENERATION 或 VIDEO_GENERATION)
|
||||
"""
|
||||
# 获取配置
|
||||
if method == "yescaptcha":
|
||||
client_key = config.yescaptcha_api_key
|
||||
@@ -1212,7 +1218,7 @@ class FlowClient:
|
||||
|
||||
website_key = "6LdsFiUsAAAAAIjVDZcuLhaHiDn5nnHVXVRQGeMV"
|
||||
website_url = f"https://labs.google/fx/tools/flow/project/{project_id}"
|
||||
page_action = "FLOW_GENERATION"
|
||||
page_action = action
|
||||
|
||||
try:
|
||||
async with AsyncSession() as session:
|
||||
|
||||
Reference in New Issue
Block a user