Files
eh-downloader/frontend/index.html
T
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

205 lines
7.9 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8" />
<title>EH Downloader</title>
<link rel="stylesheet" href="css/style.css" />
</head>
<body>
<div class="app">
<header class="bar">
<div class="brand">
<span class="logo">🐳</span>
<div>
<h1>EH Downloader</h1>
<p class="sub">E-Hentai 画廊批量下载 · 2026 版协议 · Wails</p>
</div>
</div>
<div class="actions">
<button id="btn-import" class="btn">导入链接</button>
<button id="btn-start" class="btn primary">开始下载</button>
<button id="btn-stop" class="btn danger" disabled>停止</button>
<button id="btn-clear" class="btn" title="只清除已完成的任务列表项,已下载的文件不会删除">清除已完成</button>
<button id="btn-delsel" class="btn danger" disabled title="删除勾选的任务。已下载的文件不会删除。">删除选中</button>
<button id="btn-clearall" class="btn danger" title="清空整个任务列表。本地已下载的文件不会删除。">删除全部</button>
<button id="btn-settings" class="btn icon" title="设置">⚙</button>
</div>
</header>
<div class="stats" id="stats">
<div class="stat"><b id="s-total">0</b><span>全部</span></div>
<div class="stat queued"><b id="s-queued">0</b><span>等待</span></div>
<div class="stat running"><b id="s-running">0</b><span>进行</span></div>
<div class="stat done"><b id="s-done">0</b><span>完成</span></div>
<div class="stat failed"><b id="s-failed">0</b><span>失败</span></div>
<div class="stat progress-wrap">
<div class="pbar"><i id="s-bar"></i></div>
<span id="s-text">—</span>
</div>
</div>
<main class="main">
<table class="tasks" id="tasks">
<thead>
<tr>
<th class="c-sel"><input type="checkbox" id="sel-all" /></th>
<th class="c-gid">GID</th>
<th class="c-title">标题</th>
<th class="c-prog">进度</th>
<th class="c-stat">状态</th>
<th class="c-act">操作</th>
</tr>
</thead>
<tbody id="tbody"></tbody>
</table>
<div class="empty" id="empty">
<div class="empty-inner">
<p class="big">还没有任务</p>
<p>点「导入链接」,粘贴一个 txt 文件或直接粘 <code>https://e-hentai.org/g/123456/abcdef1234/</code></p>
<p class="dim">一行一个画廊,也支持竖线/逗号分隔。一行粘多个也行。</p>
</div>
</div>
</main>
<section class="logpane" id="logpane">
<div class="log-head" id="log-head">
<span class="caret">▸</span>
<span>日志</span>
<span class="log-count" id="log-count"></span>
<div class="spacer"></div>
<button class="mini" id="btn-clearlog">清空</button>
<button class="mini" id="btn-togglelog">展开</button>
</div>
<div class="log-body" id="log-body"></div>
</section>
</div>
<!-- 导入 -->
<div class="modal" id="modal-import" hidden>
<div class="sheet">
<h2>导入画廊链接</h2>
<p class="hint">支持完整链接 <code>https://e-hentai.org/g/{gid}/{token}/</code>(推荐),或裸 gid。每行一个。</p>
<textarea id="import-text" placeholder="https://e-hentai.org/g/4239250/58703925db/&#10;https://e-hentai.org/g/4239249/b325120778/&#10;4239248"></textarea>
<div class="row">
<input type="file" id="import-file" accept=".txt,text/plain" />
</div>
<div class="sheet-foot">
<span class="msg" id="import-msg"></span>
<button class="btn" id="btn-import-cancel">取消</button>
<button class="btn primary" id="btn-import-ok">导入</button>
</div>
</div>
</div>
<!-- 通用确认 -->
<div class="modal" id="modal-confirm" hidden>
<div class="sheet narrow">
<h2 id="cf-title">确认</h2>
<p class="hint" id="cf-body"></p>
<div class="sheet-foot">
<span class="msg" id="cf-msg"></span>
<button class="btn" id="cf-cancel">取消</button>
<button class="btn danger" id="cf-ok">确认</button>
</div>
</div>
</div>
<!-- 设置 -->
<div class="modal" id="modal-settings" hidden>
<div class="sheet">
<h2>设置</h2>
<label class="field">
<span>下载目录</span>
<div class="row">
<input type="text" id="set-dir" />
<button class="btn" id="btn-pickdir">选择…</button>
</div>
</label>
<label class="field">
<span>目录结构模板</span>
<div class="row">
<input type="text" id="set-dirtpl" placeholder="{title}" />
</div>
<div class="presets" id="dir-presets"></div>
<small id="dir-preview"></small>
<small>占位符:<code>{title} {gid} {artist} {group} {series} {category} {lang} {tag1} {tags:3}</code><br>
换模板不会重下已完成的图 —— 引擎会先去老的「标题目录」里找。</small>
</label>
<label class="field">
<span>代理模式</span>
<select id="set-proxymode">
<option value="off">不使用代理(直连)</option>
<option value="http">HTTP / HTTPS 代理</option>
<option value="socks5">SOCKS5 代理</option>
</select>
</label>
<label class="field">
<span>代理地址</span>
<input type="text" id="set-proxy" placeholder="http://127.0.0.1:7890 或 socks5://127.0.0.1:1080" />
<small>EH 在国内直连基本不通,通常需要走本机代理。Sparkle/mihomo 默认端口是 7890。</small>
</label>
<label class="field">
<span>下载分辨率</span>
<select id="set-xres">
<option value="auto">站点默认(压缩 webp)</option>
<option value="org">原始分辨率(xres=org)</option>
<option value="1280">长边限制 1280</option>
<option value="1920">长边限制 1920</option>
</select>
<small>EH 浏览器里给的是压缩过的 webp;「原始分辨率」会改写 URL 里的 xres=org,但并非所有画廊都支持。</small>
</label>
<label class="field">
<span>画廊并发:<b id="set-gconc-v">5</b> 个</span>
<input type="range" id="set-gconc" min="1" max="20" step="1" value="5" />
<small>同时处理几个画廊。</small>
</label>
<label class="field">
<span>单画廊页并发:<b id="set-pconc-v">8</b> 张</span>
<input type="range" id="set-pconc" min="1" max="20" step="1" value="8" />
<small>每个画廊内部同时下几张图。<br>
总并发 = 画廊并发 × 页并发,默认 <b id="set-max">40</b> 张同时下载。</small>
</label>
<label class="field">
<span>请求间隔:<b id="set-interval-v">0</b> 秒</span>
<input type="range" id="set-interval" min="0" max="50" step="1" value="0" />
<small>所有请求之间的最小间隔(全局生效,含 /s/ 解析与图片下载)。0 = 不限流。<br>
高并发下如果频繁遇到 429,可以调到 1~2 秒。</small>
</label>
<label class="field">
<span>请求超时:<b id="set-to-v">30</b> 秒</span>
<input type="range" id="set-to" min="10" max="120" step="5" value="30" />
</label>
<label class="field">
<span>油猴推送端口</span>
<input type="text" id="set-bridgeport" placeholder="37777" />
<small>本地桥口端口,供油猴脚本推送画廊用。改这里的话,油猴脚本里的 PORT 也要同步改。<br>
启动日志里有「本地桥口已就绪 http://127.0.0.1:端口」那行,可用来确认实际生效端口。</small>
</label>
<div class="sheet-foot">
<span class="msg" id="set-msg"></span>
<button class="btn" id="btn-settings-cancel">取消</button>
<button class="btn primary" id="btn-settings-ok">保存</button>
</div>
</div>
</div>
<script src="wailsjs/go/main/App.js"></script>
<script src="wailsjs/runtime/runtime.js"></script>
<script src="js/api.js"></script>
<script src="js/renderer.js"></script>
</body>
</html>