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 次请求
This commit is contained in:
+32
-5
@@ -134,17 +134,28 @@ async function loadSettings() {
|
||||
$('set-dir').value = settings.downloadRoot || '';
|
||||
$('set-proxy').value = settings.proxy || '';
|
||||
$('set-proxymode').value = settings.proxyMode || 'off';
|
||||
$('set-conc').value = settings.concurrency || 3;
|
||||
$('set-conc-v').textContent = settings.concurrency || 3;
|
||||
$('set-gconc').value = settings.galleryConcurrency || 5;
|
||||
$('set-gconc-v').textContent = settings.galleryConcurrency || 5;
|
||||
$('set-pconc').value = settings.pageConcurrency || 8;
|
||||
$('set-pconc-v').textContent = settings.pageConcurrency || 8;
|
||||
$('set-interval').value = Math.round((settings.requestIntervalMs || 0) / 1000);
|
||||
$('set-interval-v').textContent = Math.round((settings.requestIntervalMs || 0) / 1000);
|
||||
$('set-to').value = Math.round((settings.timeout || 30000) / 1000);
|
||||
$('set-to-v').textContent = Math.round((settings.timeout || 30000) / 1000);
|
||||
$('set-xres').value = String(settings.xres || 'auto');
|
||||
$('set-bridgeport').value = String(settings.bridgePort || 37777);
|
||||
$('set-dirtpl').value = settings.dirTemplate || '{title}';
|
||||
updateMaxConcurrent();
|
||||
renderPresets();
|
||||
refreshPreview();
|
||||
}
|
||||
|
||||
function updateMaxConcurrent() {
|
||||
const g = parseInt($('set-gconc').value, 10) || 5;
|
||||
const p = parseInt($('set-pconc').value, 10) || 8;
|
||||
$('set-max').textContent = g * p;
|
||||
}
|
||||
|
||||
const DIR_PRESETS = [
|
||||
['{title}', '仅标题'],
|
||||
['{artist}/{title}', '按作者'],
|
||||
@@ -208,7 +219,15 @@ function bind() {
|
||||
if (d) $('set-dir').value = d;
|
||||
};
|
||||
|
||||
$('set-conc').oninput = (e) => { $('set-conc-v').textContent = e.target.value; };
|
||||
$('set-gconc').oninput = (e) => {
|
||||
$('set-gconc-v').textContent = e.target.value;
|
||||
updateMaxConcurrent();
|
||||
};
|
||||
$('set-pconc').oninput = (e) => {
|
||||
$('set-pconc-v').textContent = e.target.value;
|
||||
updateMaxConcurrent();
|
||||
};
|
||||
$('set-interval').oninput = (e) => { $('set-interval-v').textContent = e.target.value; };
|
||||
$('set-to').oninput = (e) => { $('set-to-v').textContent = e.target.value; };
|
||||
$('set-dirtpl').oninput = () => { renderPresets(); refreshPreview(); };
|
||||
|
||||
@@ -293,7 +312,9 @@ async function saveSettings() {
|
||||
proxy: $('set-proxy').value.trim(),
|
||||
proxyMode: $('set-proxymode').value,
|
||||
xres: $('set-xres').value,
|
||||
concurrency: parseInt($('set-conc').value, 10),
|
||||
galleryConcurrency: parseInt($('set-gconc').value, 10),
|
||||
pageConcurrency: parseInt($('set-pconc').value, 10),
|
||||
requestIntervalMs: parseInt($('set-interval').value, 10) * 1000,
|
||||
timeout: parseInt($('set-to').value, 10) * 1000,
|
||||
bridgePort: parseInt($('set-bridgeport').value, 10) || 37777,
|
||||
dirTemplate: $('set-dirtpl').value.trim() || '{title}',
|
||||
@@ -301,7 +322,13 @@ async function saveSettings() {
|
||||
await window.eh.setSettings(patch);
|
||||
$('set-msg').textContent = '';
|
||||
closeModal('modal-settings');
|
||||
pushLog({ level: 'ok', msg: '设置已保存', at: Date.now() });
|
||||
const max = patch.galleryConcurrency * patch.pageConcurrency;
|
||||
pushLog({
|
||||
level: 'ok',
|
||||
msg: `设置已保存 · 画廊并发 ${patch.galleryConcurrency} × 页并发 ${patch.pageConcurrency}(最多 ${max} 张同时下载)` +
|
||||
(patch.requestIntervalMs ? ` · 请求间隔 ${patch.requestIntervalMs / 1000} 秒` : ''),
|
||||
at: Date.now(),
|
||||
});
|
||||
}
|
||||
|
||||
async function doStart() {
|
||||
|
||||
Reference in New Issue
Block a user