feat: PeopleLib 开放文献客户端,集成 Z-Library 与 LibGen 等多源检索
Electron 桌面客户端,聚合多个开放获取文献源的搜索、详情与下载。 新增数据源: - Z-Library:邮箱登录(凭据本地存储),会话失效自动重登 - LibGen:适配新版 libgen.ac 前端(旧版 search.php 镜像已全部下线) - Memory of the World、Sci-Hub、Anna's Archive 基础设施: - mirror.js:镜像故障转移,支持串行优先与并发竞速两种策略, 失效镜像 5 分钟冷却后自动重试,避免站点恢复后被永久跳过 - http.js:统一 15 秒请求超时,防止单个卡死镜像拖垮整次搜索 - settings.js:全局代理配置持久化,经 Electron net.fetch 生效于所有请求 Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
This commit is contained in:
co-authored by
factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
commit
1a1288ce18
+102
@@ -0,0 +1,102 @@
|
||||
$('minBtn').onclick = () => window.api.minimize();
|
||||
$('maxBtn').onclick = () => window.api.maximize();
|
||||
$('closeBtn').onclick = () => window.api.close();
|
||||
|
||||
let currentTab = 'library';
|
||||
|
||||
function switchTab(tab) {
|
||||
currentTab = tab;
|
||||
document.querySelectorAll('.tab').forEach((t) => t.classList.toggle('active', t.dataset.tab === tab));
|
||||
$('libraryTab').classList.toggle('hidden', tab !== 'library');
|
||||
$('browseTab').classList.toggle('hidden', tab !== 'browse');
|
||||
$('settingsTab').classList.toggle('hidden', tab !== 'settings');
|
||||
if (tab === 'library') Library.refresh(true);
|
||||
}
|
||||
|
||||
document.querySelectorAll('.tab').forEach((t) => {
|
||||
t.onclick = () => switchTab(t.dataset.tab);
|
||||
});
|
||||
|
||||
Browse.init();
|
||||
Library.init();
|
||||
|
||||
const sortSelect = $('sortSelect');
|
||||
sortSelect.value = Library.getSortMode();
|
||||
sortSelect.onchange = () => Library.setSortMode(sortSelect.value);
|
||||
|
||||
async function initSourceManager() {
|
||||
const listEl = $('sourceList');
|
||||
const res = await window.api.sources.list();
|
||||
const all = res.ok ? res.data : [];
|
||||
const enabled = getEnabledSources();
|
||||
listEl.innerHTML = all.map((s) => {
|
||||
const checked = enabled ? enabled.includes(s.id) : true;
|
||||
return `
|
||||
<label class="source-row">
|
||||
<input type="checkbox" data-id="${escapeHtml(s.id)}" ${checked ? 'checked' : ''} />
|
||||
<span>${escapeHtml(s.name)}</span>
|
||||
</label>`;
|
||||
}).join('');
|
||||
listEl.querySelectorAll('input[type="checkbox"]').forEach((cb) => {
|
||||
cb.onchange = () => {
|
||||
const ids = Array.from(listEl.querySelectorAll('input[type="checkbox"]:checked'))
|
||||
.map((el) => el.dataset.id);
|
||||
setEnabledSources(ids);
|
||||
Browse.reloadSources();
|
||||
};
|
||||
});
|
||||
}
|
||||
initSourceManager();
|
||||
|
||||
async function refreshZlibStatus() {
|
||||
const r = await window.api.zlib.hasCreds();
|
||||
const logged = r.ok && r.data;
|
||||
$('zlibStatus').textContent = logged ? '已配置(凭据保存在本地)' : '未登录';
|
||||
$('zlibLoginBtn').textContent = logged ? '重新登录' : '登录';
|
||||
$('zlibLogoutBtn').classList.toggle('hidden', !logged);
|
||||
}
|
||||
|
||||
$('zlibLoginBtn').onclick = async () => {
|
||||
const r = await openModal('Z-Library 登录', `
|
||||
<p style="margin-bottom:8px;">使用 Z-Library 账号登录(保存在本地 userData 目录)</p>
|
||||
<div style="display:flex;flex-direction:column;gap:8px;">
|
||||
<input id="zlibEmail" type="email" placeholder="邮箱" style="padding:8px;background:#222;border:1px solid #444;color:#eee;border-radius:4px;" />
|
||||
<input id="zlibPassword" type="password" placeholder="密码" style="padding:8px;background:#222;border:1px solid #444;color:#eee;border-radius:4px;" />
|
||||
<div id="zlibErr" style="color:#f66;font-size:12px;min-height:16px;"></div>
|
||||
</div>
|
||||
`, async () => {
|
||||
const email = $('zlibEmail').value.trim();
|
||||
const password = $('zlibPassword').value;
|
||||
if (!email || !password) { $('zlibErr').textContent = '请输入邮箱和密码'; return false; }
|
||||
$('zlibErr').textContent = '登录中...';
|
||||
const res = await window.api.zlib.login(email, password);
|
||||
if (!res.ok) { $('zlibErr').textContent = res.error || '登录失败'; return false; }
|
||||
if (res.data && res.data.ok === false) { $('zlibErr').textContent = res.data.error || '登录失败'; return false; }
|
||||
return true;
|
||||
});
|
||||
if (r) refreshZlibStatus();
|
||||
};
|
||||
|
||||
$('zlibLogoutBtn').onclick = async () => {
|
||||
const ok = await confirmModal('退出 Z-Library', '确定要清除本地保存的 Z-Library 凭据吗?');
|
||||
if (ok) { await window.api.zlib.logout(); refreshZlibStatus(); }
|
||||
};
|
||||
|
||||
refreshZlibStatus();
|
||||
|
||||
async function refreshProxy() {
|
||||
const r = await window.api.proxy.get();
|
||||
if (r.ok) $('proxyInput').value = r.data || '';
|
||||
}
|
||||
$('proxySaveBtn').onclick = async () => {
|
||||
await window.api.proxy.set($('proxyInput').value.trim());
|
||||
$('proxySaveBtn').textContent = '已保存 ✓';
|
||||
setTimeout(() => { $('proxySaveBtn').textContent = '保存'; }, 1500);
|
||||
};
|
||||
refreshProxy();
|
||||
|
||||
window.api.getVersion().then((r) => {
|
||||
if (r && r.ok) $('appVersion').textContent = 'v' + r.data;
|
||||
});
|
||||
|
||||
switchTab('library');
|
||||
Reference in New Issue
Block a user