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:
lofyer
2026-07-25 14:51:10 +08:00
co-authored by factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
commit 1a1288ce18
33 changed files with 4640 additions and 0 deletions
+145
View File
@@ -0,0 +1,145 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; img-src https: http: data: file:; style-src 'self' 'unsafe-inline';" />
<title>PeopleLib 文献库</title>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<div class="titlebar">
<div class="titlebar-left">
<span class="brand">PeopleLib <span class="brand-sub">开放文献库</span></span>
</div>
<nav class="tabs">
<button class="tab active" data-tab="library">我的书库</button>
<button class="tab" data-tab="browse">检索</button>
</nav>
<div class="titlebar-spacer"></div>
<div class="titlebar-controls">
<button class="win-btn tab" data-tab="settings" title="设置">&#9881;</button>
<button id="minBtn" class="win-btn" title="最小化">&#9472;</button>
<button id="maxBtn" class="win-btn" title="最大化">&#9633;</button>
<button id="closeBtn" class="win-btn win-close" title="关闭">&#10005;</button>
</div>
</div>
<main id="main">
<!-- 我的书库 -->
<section id="libraryTab" class="tab-panel">
<div class="toolbar">
<span id="libStatus" class="status-bar"></span>
<div class="spacer"></div>
<button id="addLocalBtn" class="tb-btn">+ 添加本地文件</button>
</div>
<div id="libGrid" class="grid"></div>
</section>
<!-- 检索 -->
<section id="browseTab" class="tab-panel hidden">
<div id="browseGridView">
<div class="browse-head">
<div class="toolbar">
<select id="sourceSelect" class="source-select"></select>
<div class="search-inline">
<input id="searchInput" type="text" placeholder="搜索标题 / 作者 / 关键词..." />
<button id="searchBtn" class="tb-btn">搜索</button>
<button id="clearSearchBtn" class="tb-btn ghost hidden">✕ 清除</button>
</div>
</div>
<div id="statusBar" class="status-bar"></div>
</div>
<div id="grid" class="grid"></div>
<div id="pager" class="pager hidden">
<button id="prevBtn" class="page-btn">← 上一页</button>
<span id="pageInfo" class="page-info"></span>
<button id="nextBtn" class="page-btn">下一页 →</button>
<span class="page-jump">
跳转到
<input id="jumpInput" type="number" min="1" class="jump-input" />
<button id="jumpBtn" class="page-btn">跳转</button>
</span>
</div>
</div>
<div id="detailView" class="hidden">
<button id="backBtn" class="back-btn" aria-label="返回">← 返回</button>
<div id="detailContent"></div>
</div>
</section>
<!-- 设置 -->
<section id="settingsTab" class="tab-panel hidden">
<div class="settings-page">
<h2 class="settings-title">设置</h2>
<div class="settings-group">
<div class="settings-item settings-item-block">
<div class="settings-item-info">
<div class="settings-item-label">数据源管理</div>
<div class="settings-item-desc">选择在检索页显示的数据源</div>
</div>
<div id="sourceList" class="source-list"></div>
</div>
</div>
<div class="settings-group">
<div class="settings-item">
<div class="settings-item-info">
<div class="settings-item-label">书库默认排序</div>
<div class="settings-item-desc">控制书库条目的排列顺序</div>
</div>
<select id="sortSelect" class="source-select">
<option value="added">添加时间</option>
<option value="title">标题</option>
<option value="author">作者</option>
</select>
</div>
</div>
<div class="settings-group">
<div class="settings-item">
<div class="settings-item-info">
<div class="settings-item-label">网络代理</div>
<div class="settings-item-desc">对所有数据源与下载统一生效;访问 LibGen / Z-Library 通常需要代理(留空表示直连)</div>
</div>
<input id="proxyInput" type="text" placeholder="留空表示直连,例如 http://localhost:7897" style="width:220px;padding:6px;background:#222;border:1px solid #444;color:#eee;border-radius:4px;" />
<button id="proxySaveBtn" class="tb-btn">保存</button>
</div>
</div>
<div class="settings-group">
<div class="settings-item">
<div class="settings-item-info">
<div class="settings-item-label">Z-Library 账号</div>
<div class="settings-item-desc" id="zlibStatus">未登录</div>
</div>
<button id="zlibLoginBtn" class="tb-btn">登录</button>
<button id="zlibLogoutBtn" class="tb-btn ghost hidden">退出</button>
</div>
</div>
<div class="settings-group">
<div class="settings-item">
<div class="settings-item-info">
<div class="settings-item-label">关于</div>
<div class="settings-item-desc">PeopleLib <span id="appVersion">-</span> · 开放获取文献与公版图书客户端</div>
</div>
</div>
</div>
</div>
</section>
</main>
<!-- 通用弹窗 -->
<div id="modal" class="modal hidden">
<div class="modal-box">
<div id="modalTitle" class="modal-title"></div>
<div id="modalBody" class="modal-body"></div>
<div class="modal-actions">
<button id="modalCancel" class="page-btn">取消</button>
<button id="modalOk" class="tb-btn">确定</button>
</div>
</div>
</div>
<script src="util.js"></script>
<script src="views/browse.js"></script>
<script src="views/library.js"></script>
<script src="app.js"></script>
</body>
</html>