feat: use system curl for cmhub image downloads
This commit is contained in:
@@ -403,6 +403,7 @@ data/images/<batch_id>/<slug>/<task_id>_<item_id>_new.<ext> # AI 生成的新
|
||||
2. **若②勾选「生成封面图片(成本较高)」**:接着并发生成图片,线程池大小 = `image_concurrency`,调 `gen_cover(封面提示词, old_cover_path, resolution, jpg_quality)` → 新图存 `data/images/<batch_id>/<slug>/<task_id>_<item_id>_new.jpg`。默认 `backend=cmhub`,调用 `POST /api/v1/generate/image`,拿 `image_url` 后立即安全下载并转本地 JPEG;`backend=direct` 仅内部兼容时使用 `default_image_model`。
|
||||
- T-545 已实现:cmhub 模式下不再直接按用户填写的 `image_concurrency` 全量打到网关;实际生图请求并发 = `min(image_concurrency, 5)`。拿到 `image_url` 后交给独立下载/保存线程池,下载线程数量与实际生图请求并发一致,同样最大 5。这样批量时“下一批生图请求”和“上一批图片下载/保存”可以流水线并行,但不会对 cmhub 生图接口或 `/media/generated/images/*.png` 静态下载打出超过 5 的并发。direct 兼容路径暂不改变。
|
||||
- cmhub 生图连接超时取 `ai.cmhub.connect_timeout`;生图请求和随后 `image_url` 下载的读取等待统一固定 650 秒,读超时不自动重发,避免重复扣点。下载层最多安全重试 3 次,只复用同一个 `image_url`,不重新请求 cmhub 生图;下载总耗时超过 20 秒时写“图片下载较慢”警告。`backend=direct` 兼容路径仍按模型 `timeout_seconds` 或 `resolution_timeouts[resolution]`(512→180/1k→240/2k→360/4k→600)取返回超时。
|
||||
- T-548 已实现:cmhub 图片下载后端新增 `ai.cmhub.download_with_curl`(`auto`/`true`/`false`,默认 `auto`)。Windows 且检测到系统 curl 时优先用 curl 下载,否则或 curl 执行失败时回退 requests;生成、models、balance 仍走共享 requests Session。curl 下载前仍执行公网 URL 校验;URL 写入临时 curl 配置文件并通过 `-K` 传入,避免带 token 的 `image_url` 出现在进程命令行;`use_system_proxy=false` 时 curl 加 `--noproxy "*"`。
|
||||
- cmhub 返回的完整 `image_url` 默认只在内存中临时用于下载,不写入 `tasks` 或 `run_log_events`。本机调试时可设置环境变量 `CMSHOPEE_DEBUG_CMHUB_IMAGE_URL=1`,②本轮可见运行日志会显示脱敏后的 URL 调试行,且该行不持久化到 SQLite。
|
||||
3. **若未勾选生成封面**:标题成功后立即写 `new_title`,`new_cover_path=NULL`,不渲染封面提示词、不调用 `gen_cover()`、不创建本地新封面文件。
|
||||
|
||||
|
||||
+1
-1
@@ -138,7 +138,7 @@
|
||||
| T-545 | ② cmhub 生图和下载并发上限 5 + 独立下载线程池 | T-535, T-533, T-519 | 背景:实测 10 图片并发时,cmhub 后台单条生成小于 200 秒,但本地下载 `/media/generated/images/*.png` 常见 24~185 秒且有连接失败;如果每个生图线程同时负责“等待 cmhub 返回 + 下载 + 转 JPEG + 保存”,下载慢会占住生图线程,后续任务排队。方案:cmhub 模式下实际生图请求并发 = `min(ai.image_concurrency, 5)`;拿到 `image_url` 后把下载/转 JPEG/保存交给独立下载线程池,下载线程数与实际生图请求并发一致、同样最大 5;不新增用户可见配置项,⑤仍只保留「图片并发」。②开始日志必须显示用户设置图片并发、cmhub 实际生图并发和下载并发;分段日志继续记录“cmhub 已返回 image_url / 下载完成 / 本地保存完成”的耗时。下载失败按当前任务失败记录,但不得重新调用 cmhub 生图接口导致重复扣点;`cover_done/generated_done` 必须等下载保存成功并写 DB 后才计数;停止逻辑继续取消未开始项,运行中请求/下载允许自然完成或失败。direct 兼容路径暂不改变。补 `tests/test_ai.py` 覆盖并发上限和下载线程池不阻塞后续生图提交、下载失败不重复扣点;补 `tests/test_gui.py` 覆盖开始日志显示实际并发 | DONE |
|
||||
| T-536 | GUI 按钮圆角全局统一 | T-512, T-513, T-523 | 现象:只有③「开始更新」及少数上色按钮(删除批次/删除账号/未匹配(n))有 `border-radius: 4px`,其余按钮走原生渲染显直角——不一致。根因:圆角是 T-512/T-513 给按钮上色时顺带写进 QSS 的副产品,不是全局形状决策;一旦给 `QPushButton` 设 stylesheet 就放弃原生渲染,才补了 radius/border。方案(**方式 A:全局统一圆角**):① 在 `app/gui/widgets.py` 抽一个**共享按钮基础样式常量/helper**(统一 `border-radius`,如 4px,与卡片 6px 圆角语言协调),并**接管按钮的全部视觉状态**——normal/hover/pressed/disabled/focus 的背景、边框都定义好,避免全局设 QSS 后按钮变扁平方块、丢 hover 反馈;② 在主窗口/app 级用全局 `QPushButton` QSS 应用该基础样式,让**所有按钮共享同一圆角**;③ warning(`startUpdateButton`)与 danger(`_danger_outline_button_style`:删除批次/删除账号/未匹配)按钮改为**只叠加颜色**,复用共享基础样式的圆角/内边距/状态,不再各自重写 radius/border——杜绝“上色=顺带圆角”的隐性耦合;④ 以 Windows 为主目标做一次视觉自测(hover/按下/禁用不劣于原生)。只改 GUI 样式层(`widgets.py` + 主窗口全局 QSS + 各上色按钮引用),不改任何按钮的启用/禁用逻辑、行为、业务流程、DB、Excel、Shopee/CDP。GUI 单测至少断言上色按钮仍带各自语义色且不再各自硬写 radius(改为引用共享样式);圆角外观本身以人工视觉验收为准 | DONE |
|
||||
| T-546 | cmhub 客户端共享 Session + 连接池 + 代理处理(补 T-545 未覆盖的下载慢根因) | T-545, T-526 | 现象:实测生图拿到 `image_url` 后本地下载单条约 191 秒、而同一 URL 用 curl 约 10 秒;日志伴随 `connect_timeout: 连接 cmhub 超时`。T-545 已做「生图并发上限 5 + 独立下载线程池」解决“下载堵住生图槽位”的流水线问题,但**未根治单条下载在并发争抢下变慢**。根因(代码核实):① cmhub 所有 HTTP(`_cmhub_call_once` 的生成/models/balance、`_download_cmhub_image` 的下载)都是**裸 `requests.get`/`requests.request`、无共享 `Session`**,每个任务全新 TCP+TLS,叠加同步生成长连接(单条 60~260s)与 `connect_timeout` 触发的重试,形成连接风暴/连接饥饿;② `requests` 默认 `trust_env=True` 读 `HTTP(S)_PROXY`/`ALL_PROXY`,若系统带慢代理会拖累,而干净窗口的 curl 直连快。方案:① cmhub 所有 HTTP 统一走一个**模块级共享 `requests.Session`**,挂 `HTTPAdapter(pool_connections/pool_maxsize)`,池大小 ≥(实际生图并发 + 下载并发)以免连接不足排队、复用连接减少握手与 connect_timeout;生成与下载共享该 Session(`requests.Session` 跨线程发请求安全,但连接池要够大);② 代理处理——诊断日志脱敏记录 cmhub 请求是否经代理;对 cmhub 请求提供明确策略(可配 `trust_env=False` 或显式 `proxies`),避免误走慢代理,默认可先保持读环境但提供关闭开关;③ 复测口径——Session+连接池到位后单条下载耗时应回落到与 curl 同量级;若在 T-545 的并发上限 5 下仍显著慢,评估把 cmhub 生图默认并发再降(1~2);④ 确认 connect_timeout 重试退避不加剧连接风暴。诊断建议:可先临时把图片并发设 1 复测以区分“并发争抢”与“代理”。边界:只改 `app/ai.py` 的 cmhub HTTP 客户端层(+ 可选 config 代理项)+ 诊断日志 + 相关文档;不改 cmhub 协议、T-545 的并发/下载池语义、生成编排、DB、Excel、CDP/Shopee。测试:`tests/test_ai.py` 覆盖共享 Session 被复用(mock 同一 session 多次调用)、代理配置被尊重、连接池大小设定;现有 cmhub 用例保持绿。落地:`app/ai.py` 加模块级 `_cmhub_session()`(`HTTPAdapter` 池 `CMHUB_HTTP_POOL_SIZE=32`)+ `_apply_cmhub_proxy()`;生成/下载/models/balance 统一走该 Session;`ai.cmhub.use_system_proxy` 默认 `false`(绕过系统代理,公网网关直连),`_cmhub_runtime` 读取并应用、`fetch_cmhub_models/balance` 加同名参数;`test_ai.py` 补 3 项新测试并把原 18 处 `requests` mock 改到共享 Session;243 单测全绿。**待 Windows 实网复测确认下载提速**(troubleshooting 已加「cmhub 图片下载很慢」排障节与代理定位命令) | DONE |
|
||||
| T-548 | cmhub 图片下载改用系统 curl(Win10+ 自带) | T-546, T-538 | 背景:图片下载慢的另一手段——用 curl(用户的快基准)下载。curl 在 **Windows 10+ 自带**(`C:\Windows\System32\curl.exe`),**无需捆绑**(不用项目里 `wget/` 那个 2022 老 wget,其 OpenSSL 太旧且捆绑二进制易被杀软误报);应用底线本就是 Win10+(PySide6/Qt6 不支持 Win7)。**前置说明**:若 T-546 的代理绕过已让 requests 下载回落到 curl 同量级,本任务可作可选/降级;此处按用户要求落地。方案:① **只改图片下载这一步**(`app/ai.py` `_download_cmhub_image`),cmhub 生成/models/balance 仍走 requests 共享 Session。② 后端选择:新增 config `ai.cmhub.download_with_curl`(`auto`/`true`/`false`,默认 `auto`=Windows 且检测到 curl 用 curl,否则回退 requests);`shutil.which("curl")` 或固定 `System32\curl.exe` 检测;**非 Windows / 无 curl / curl 执行失败 → 自动回退现有 requests 下载**(保证 Linux/Mac/CI/无 curl 环境和现有测试不受影响)。③ **安全防护(硬要求)**:a) SSRF 不可绕过——调 curl 前仍执行 `_assert_public_http_url()` 拦内网/回环 IP,用 `subprocess.run([...], shell=False)` 参数列表、不拼 shell;b) token 不泄漏——`image_url` 带 `?token=...`,直接进命令行会出现在进程列表(tasklist)和日志,改用 curl `-K <临时配置文件>`(把 `url = "..."` 写进临时文件传入、用完删)避免 URL 进程列表暴露,日志一律脱敏、不打印完整 URL/命令;c) 绕过代理——按 `use_system_proxy`:false 时加 `--noproxy "*"`(这才是 curl 快的关键,与 T-546 一致),true 时不加。④ 超时/大小:`--connect-timeout <connect>` + `--max-time <read≈650>`;下载到临时文件、读回字节后**仍复用现有 `_save_jpeg` 重编码**;保留 20MB 上限(`--max-filesize` + 落盘后大小复核)。⑤ 重试:沿用现有 `_download_cmhub_image_with_retry`(3 次),只替换内层下载实现;curl 非零退出=失败、走现有重试语义。⑥ 打包:curl 系统自带、不捆绑、不改 spec;`docs/packaging.md` 注明依赖 Win10+ 自带 curl。测试:`tests/test_ai.py` mock `subprocess.run`——断言 curl 参数含 `--noproxy`/超时/`-K` 临时配置(URL 不在 argv)、SSRF 校验先于 curl、curl 失败回退 requests、非 Windows/无 curl 自动回退;现有下载用例保持绿。边界:只改 `_download_cmhub_image` 下载后端 + config 项 + 诊断脱敏 + 打包文档;不改生成/API 调用、`_save_jpeg`、并发/重试语义、DB、Excel、CDP/Shopee | TODO |
|
||||
| T-548 | cmhub 图片下载改用系统 curl(Win10+ 自带) | T-546, T-538 | 背景:图片下载慢的另一手段——用 curl(用户的快基准)下载。curl 在 **Windows 10+ 自带**(`C:\Windows\System32\curl.exe`),**无需捆绑**(不用项目里 `wget/` 那个 2022 老 wget,其 OpenSSL 太旧且捆绑二进制易被杀软误报);应用底线本就是 Win10+(PySide6/Qt6 不支持 Win7)。**前置说明**:若 T-546 的代理绕过已让 requests 下载回落到 curl 同量级,本任务可作可选/降级;此处按用户要求落地。方案:① **只改图片下载这一步**(`app/ai.py` `_download_cmhub_image`),cmhub 生成/models/balance 仍走 requests 共享 Session。② 后端选择:新增 config `ai.cmhub.download_with_curl`(`auto`/`true`/`false`,默认 `auto`=Windows 且检测到 curl 用 curl,否则回退 requests);`shutil.which("curl")` 或固定 `System32\curl.exe` 检测;**非 Windows / 无 curl / curl 执行失败 → 自动回退现有 requests 下载**(保证 Linux/Mac/CI/无 curl 环境和现有测试不受影响)。③ **安全防护(硬要求)**:a) SSRF 不可绕过——调 curl 前仍执行 `_assert_public_http_url()` 拦内网/回环 IP,用 `subprocess.run([...], shell=False)` 参数列表、不拼 shell;b) token 不泄漏——`image_url` 带 `?token=...`,直接进命令行会出现在进程列表(tasklist)和日志,改用 curl `-K <临时配置文件>`(把 `url = "..."` 写进临时文件传入、用完删)避免 URL 进程列表暴露,日志一律脱敏、不打印完整 URL/命令;c) 绕过代理——按 `use_system_proxy`:false 时加 `--noproxy "*"`(这才是 curl 快的关键,与 T-546 一致),true 时不加。④ 超时/大小:`--connect-timeout <connect>` + `--max-time <read≈650>`;下载到临时文件、读回字节后**仍复用现有 `_save_jpeg` 重编码**;保留 20MB 上限(`--max-filesize` + 落盘后大小复核)。⑤ 重试:沿用现有 `_download_cmhub_image_with_retry`(3 次),只替换内层下载实现;curl 非零退出=失败、走现有重试语义。⑥ 打包:curl 系统自带、不捆绑、不改 spec;`docs/packaging.md` 注明依赖 Win10+ 自带 curl。测试:`tests/test_ai.py` mock `subprocess.run`——断言 curl 参数含 `--noproxy`/超时/`-K` 临时配置(URL 不在 argv)、SSRF 校验先于 curl、curl 失败回退 requests、非 Windows/无 curl 自动回退;现有下载用例保持绿。边界:只改 `_download_cmhub_image` 下载后端 + config 项 + 诊断脱敏 + 打包文档;不改生成/API 调用、`_save_jpeg`、并发/重试语义、DB、Excel、CDP/Shopee | DONE |
|
||||
|
||||
## Phase 8 · 工程基础设施后续(`docs/engineering-review.md`)
|
||||
|
||||
|
||||
+2
-2
@@ -33,7 +33,7 @@ ai_config(config=None) -> dict # default_text_model/default_image
|
||||
# title_concurrency/image_concurrency/retry/jpg_quality/
|
||||
# resolution/resolution_timeouts
|
||||
ai_backend(config=None) -> str # 默认 cmhub;direct 仅内部兼容/手工回滚
|
||||
cmhub_config(config=None) -> dict # base_url/title_alias/image_alias/connect_timeout
|
||||
cmhub_config(config=None) -> dict # base_url/title_alias/image_alias/connect_timeout/download_with_curl
|
||||
normalize_cmhub_base_url(base_url) -> str # 规整为 cmhub 网关根:scheme+host(+port)
|
||||
cmhub_request_url(base_url, endpoint) -> str # 先规整 base_url,再拼 /api/v1/...
|
||||
response_timeout(config=None) -> int # = resolution_timeouts[resolution](返回超时,随分辨率)
|
||||
@@ -313,7 +313,7 @@ fetch_cmhub_models(base_url, api_key, connect_timeout=10, read_timeout=30) -> li
|
||||
- `api_type=chat/auto` 走 OpenAI-compatible chat JSON;`api_type=images_edits` 走 multipart form。
|
||||
- direct 连接超时参考模型 `connect_timeout_seconds`;**返回超时 = 模型 `timeout_seconds` 或 `appconfig.response_timeout()`(随分辨率:512/1k/2k/4k → 180/240/360/600)**。cmhub 使用 `requests timeout=(connect, read)`,connect 来自 `ai.cmhub.connect_timeout`;cmhub 生文读取等待固定 600s,cmhub 生图请求和图片下载读取等待固定 650s,且生图读超时不自动重发。
|
||||
- 并发数/重试/分辨率/jpg 质量来自 `appconfig.ai_config()`;标题/图片并发会被夹到 1..5,失败重试次数会被夹到 0..10,兼容旧配置中的超限值;Key 本地明文存储,但不入日志、不导出。cmhub 响应的 `points_cost/points_balance/call_id` 不改变返回值,通过 `on_event` metadata 事件上报,GUI 余额/计费展示留给 T-528。
|
||||
- 标题快、图片慢:分两段、各用各自并发数;失败按 `retry` 重试,仍失败记 error 不阻塞其余。cmhub 生图使用内部实际并发 `min(image_concurrency, 5)`,拿到 `image_url` 后交给独立下载/保存线程池,下载线程数与实际生图并发一致且最大 5;下载失败最多安全重试 3 次,只复用同一个 `image_url`,不会重新调用 cmhub 生图接口;下载总耗时超过 20 秒时写“图片下载较慢”警告;direct 兼容路径暂保持原 `image_concurrency` 语义。
|
||||
- 标题快、图片慢:分两段、各用各自并发数;失败按 `retry` 重试,仍失败记 error 不阻塞其余。cmhub 生图使用内部实际并发 `min(image_concurrency, 5)`,拿到 `image_url` 后交给独立下载/保存线程池,下载线程数与实际生图并发一致且最大 5;T-548 后图片下载后端由 `ai.cmhub.download_with_curl` 控制,默认 `auto`:Windows 且检测到系统 curl 时优先用 curl 下载,否则回退 requests;curl 失败也会自动回退 requests。下载前仍执行公网 URL 校验,curl 使用 `-K` 临时配置文件传入 URL,不把带 token 的 `image_url` 放进命令行参数;`use_system_proxy=false` 时 curl 加 `--noproxy "*"`。下载失败最多安全重试 3 次,只复用同一个 `image_url`,不会重新调用 cmhub 生图接口;下载总耗时超过 20 秒时写“图片下载较慢”警告;direct 兼容路径暂保持原 `image_concurrency` 语义。
|
||||
- 调试 cmhub 图片 URL:默认不在日志或 DB 中记录完整 `image_url`。若本机调试需要查看,启动程序前设置环境变量 `CMSHOPEE_DEBUG_CMHUB_IMAGE_URL=1`,②本轮可见运行日志会显示脱敏后的“cmhub 图片 URL”,但该调试行不写入 SQLite `run_log_events`。
|
||||
- 调用有成本与失败可能:超时、限流、内容安全拒绝都要返回明确错误。
|
||||
- 生成结果**直接进入 ③ 更新候选**;③ 点击「开始更新」后弹窗批量确认,确认后提交线上。本地留档 + 回写 Excel 供追溯。
|
||||
|
||||
@@ -98,6 +98,7 @@
|
||||
- **响应**:拿 `image_url` → **新增一步下载**该图字节(cmhub 自家对象存储公网 URL)→ 交给现有 `_save_jpeg(image_bytes, out_path, resolution, quality)` 落盘。下载 helper 必须校验 URL scheme 只允许 `http/https`,拒绝内网/回环/本机地址,并校验域名解析后的 IP 仍不属于内网/回环/本机地址,设置超时和大小上限;生成后**立即下载**(对象存储 URL 可能有有效期)。`points_cost`/`points_balance`/`call_id` 同样通过事件回调传播,不改变 `gen_cover` 返回值。
|
||||
- **超时(关键)**:生图同步且慢。当前 cmshopee 的 cmhub 生图请求和随后 `image_url` 下载读取等待统一固定 650 秒,不再按分辨率变化,绝不用 30s/60s 调生图——否则客户端超时但服务端仍在算并扣点(见 §4.4 幂等)。
|
||||
- **并发(T-545 已实现)**:最近实测 `/media/generated/images/*.png` 下载链路在 10 并发下明显慢且有连接失败。cmhub 模式下采用内置保护:实际生图请求并发 = `min(ai.image_concurrency, 5)`;下载/保存使用独立线程池,线程数与实际生图请求并发一致,同样最大 5;不新增用户可见配置项。运行日志必须同时显示用户设置和实际并发,避免用户误解设置 10 就会对 cmhub 打 10 并发。下载失败记为该任务失败,不得重新调用生图接口导致重复扣点;读超时仍按 §4.4 的非幂等规则处理。
|
||||
- **下载后端(T-548 已实现)**:cmhub 生成/models/balance 仍走共享 requests Session;仅 `image_url` 图片下载可按 `ai.cmhub.download_with_curl` 选择系统 curl。默认 `auto` 在 Windows 且检测到系统 curl 时优先 curl,非 Windows、无 curl 或 curl 失败自动回退 requests。curl 调用前仍做公网 URL 校验,用 `-K` 临时配置文件传 URL,避免 token 出现在进程命令行;`use_system_proxy=false` 时加 `--noproxy "*"`。
|
||||
|
||||
### 4.4 错误映射与重试策略
|
||||
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -194,6 +194,8 @@ T-544 第一版目标是**启动时检查是否必须升级**,但仍不做自
|
||||
|
||||
`cmshopee.spec` 不声明任何 `datas`;`scripts/build_exe.ps1` 会在打包后检查 `dist\cmshopee\`,如果发现上述路径会直接失败。
|
||||
|
||||
T-548 后 cmhub 图片下载可在 Windows 上优先调用系统自带 curl。curl 在 Windows 10+ 通常位于 `C:\Windows\System32\curl.exe`,发布包不捆绑 curl、wget 或其它下载二进制;如果系统无 curl 或 curl 执行失败,程序会自动回退到内置 requests 下载。
|
||||
|
||||
## 五、首次运行与本地数据位置
|
||||
|
||||
T-538 后,打包版不再把全进程工作目录切到 `cmshopee.exe` 所在目录;路径由 `app/appconfig.py` 按显式数据根解析。
|
||||
|
||||
Reference in New Issue
Block a user