Files
toor 0447875b6d feat: 双层并发 + 全局限流 + 整轮扫尾 + 画廊目录清单
原实现只能「多画廊并行,每个画廊串行下」,单张大画廊下得很慢。
按用户多年使用习惯(5 画廊 × 每画廊 8 张)改造为双层并发。

- 双层并发:画廊并发(默认5) × 页并发(默认8) = 最多 40 张同时下载,
  两个值都在设置面板可调(1-20)。页级 worker 池 + mutex 保护计数。
- 全局限流:对齐参考实现的 nextRequestTime 算法,限所有请求
  (含 /s/ 解析与图片本体),默认 0 秒关闭。
- 整轮扫尾:跑完后自动重扫失败页 1 轮(同样走完整 4 次退避重试),
  仍失败则停下并标记 partial,换线路后手动点开始即可续传。
- 画廊目录清单 .ehdl.json:记录 gid/token/总页数/已完成页,
  即使 state.json 丢失也能算出「应该多少张、还差多少张」。

顺带修复:
- Start() 双重加锁导致死锁(引擎完全无法启动)
- clampInt 越界时返回默认值而非边界值(设 30 会静默变 8)
- 直链正则写死 hath.network,CDN 换域名会全军覆没 -> 加图片 URL 兜底
- ReconcileTask 会误报「有改动」

测试从 37 增至 62,其中并发/限流/扫尾用 httptest 模拟 EH 实测:
- 页并发峰值 = 8、画廊并发峰值 = 5(完全生效)
- 12 次请求限流 80ms 实测 966ms(期望 880ms)
- 断点续传 4 张已存在时只发 5 次请求
2026-10-09 00:11:38 +08:00

57 lines
2.0 KiB
JavaScript

// Wails API 适配层
//
// 前端业务代码只用 window.eh.*,这里把它接到 Wails 的 Go bindings 上。
// 这样以后即便换框架,也只要改这一个文件。
const go = () => window.go && window.go.main && window.go.main.App;
function requireGo() {
const g = go();
if (!g) throw new Error('Wails 后端未就绪');
return g;
}
window.eh = {
// ---- 设置 ----
getSettings: async () => requireGo().GetSettings(),
setSettings: async (patch) => requireGo().SetSettings(patch),
previewDir: async (tpl) => requireGo().PreviewDir(tpl),
placeholders: async () => requireGo().Placeholders(),
// ---- 任务 ----
listTasks: async () => requireGo().ListTasks(),
addTasks: async (text) => requireGo().AddTasks(text),
removeTask: async (t) => requireGo().RemoveTask(t.gid, t.token || ''),
clearFinished: async () => requireGo().ClearFinished(),
countDone: async () => requireGo().CountDone(),
clearAll: async (onlySelected) => requireGo().ClearAll(!!onlySelected),
requeueFailed: async () => requireGo().RequeueFailed(),
selectAll: async (gids) => requireGo().SelectAll(gids),
// ---- 引擎 ----
status: async () => requireGo().Status(),
engineInfo: async () => requireGo().EngineInfo(),
start: async (payload) => {
const gids = (payload && payload.onlyGids) || [];
return requireGo().Start(gids);
},
stop: async () => requireGo().Stop(),
// ---- 扫描下载目录(用 .ehdl.json 清单补全丢失的任务信息)----
scanDownloadDir: async () => requireGo().ScanDownloadDir(),
// ---- 系统 ----
pickDir: async () => requireGo().PickDir(),
exportFailed: async (gid) => {
const path = await requireGo().ExportFailed(gid);
return path ? { ok: true, filePath: path } : { ok: false };
},
openPath: async (p) => requireGo().OpenPath(p),
showItem: async (p) => requireGo().ShowItem(p),
// ---- 事件 ----
onTaskUpdate: (cb) => window.runtime.EventsOn('task:update', cb),
onLog: (cb) => window.runtime.EventsOn('log', cb),
onDone: (cb) => window.runtime.EventsOn('engine:done', cb),
};