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
@@ -0,0 +1,84 @@
|
||||
const { fetchJson, clampPage } = require('./http');
|
||||
|
||||
const PAGE_SIZE = 30;
|
||||
|
||||
function toItem(r, server) {
|
||||
const authors = String(r.authors || '').split(';').map((s) => s.trim()).filter(Boolean).slice(0, 3).join(', ');
|
||||
return {
|
||||
postId: r.doi,
|
||||
title: r.title || '(无标题)',
|
||||
cover: '',
|
||||
date: r.date || '',
|
||||
url: `https://www.${server}.org/content/${r.doi}v${r.version || 1}`,
|
||||
subtitle: [authors, r.category].filter(Boolean).join(' · '),
|
||||
_server: server
|
||||
};
|
||||
}
|
||||
|
||||
function dateStr(d) { return d.toISOString().slice(0, 10); }
|
||||
|
||||
async function fetchWindow(server, from, to, cursor, tries = 3) {
|
||||
let last;
|
||||
for (let i = 0; i < tries; i++) {
|
||||
try {
|
||||
const j = await fetchJson(`https://api.biorxiv.org/details/${server}/${from}/${to}/${cursor}`);
|
||||
const total = parseInt(j.messages && j.messages[0] && j.messages[0].total, 10) || 0;
|
||||
return { total, collection: j.collection || [] };
|
||||
} catch (e) {
|
||||
last = e;
|
||||
if (!/504|502|503/.test(e.message)) throw e;
|
||||
await new Promise((r) => setTimeout(r, 1500 * (i + 1)));
|
||||
}
|
||||
}
|
||||
throw last;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
id: 'biorxiv',
|
||||
name: 'bioRxiv 预印本',
|
||||
supportsSearch: false,
|
||||
|
||||
async list(page) {
|
||||
page = clampPage(page);
|
||||
const server = 'biorxiv';
|
||||
const end = new Date();
|
||||
const daysPerPage = 3;
|
||||
const startIdx = (page - 1) * daysPerPage;
|
||||
const from = new Date(end.getTime() - (startIdx + daysPerPage) * 864e5);
|
||||
const to = new Date(end.getTime() - startIdx * 864e5);
|
||||
const { collection } = await fetchWindow(server, dateStr(from), dateStr(to), 0);
|
||||
const items = collection.slice(0, PAGE_SIZE).map((r) => toItem(r, server));
|
||||
return { items, maxPage: 1000, page };
|
||||
},
|
||||
|
||||
async search() {
|
||||
throw new Error('bioRxiv 暂不支持搜索,请翻页浏览');
|
||||
},
|
||||
|
||||
async detail(postId) {
|
||||
const j = await fetchJson(`https://api.biorxiv.org/details/biorxiv/${postId}`);
|
||||
const c = (j.collection || [])[0];
|
||||
if (!c) throw new Error('未找到该预印本');
|
||||
return {
|
||||
postId,
|
||||
title: c.title,
|
||||
cover: '',
|
||||
authors: String(c.authors || '').split(';').map((s) => s.trim()).filter(Boolean),
|
||||
date: c.date || '',
|
||||
tags: [c.category ? `分类:${c.category}` : '', c.license ? `许可:${c.license}` : ''].filter(Boolean),
|
||||
brief: c.abstract || '',
|
||||
url: `https://www.biorxiv.org/content/${c.doi}v${c.version || 1}`,
|
||||
links: [{ name: 'bioRxiv 页', url: `https://www.biorxiv.org/content/${c.doi}v${c.version || 1}` }]
|
||||
};
|
||||
},
|
||||
|
||||
async download(postId) {
|
||||
const j = await fetchJson(`https://api.biorxiv.org/details/biorxiv/${postId}`);
|
||||
const c = (j.collection || [])[0];
|
||||
const v = (c && c.version) || 1;
|
||||
return {
|
||||
files: [{ name: `${String(postId).replace(/\//g, '_')}.pdf`, link: `https://www.biorxiv.org/content/${postId}v${v}.full.pdf`, format: 'PDF' }],
|
||||
links: [{ name: 'bioRxiv 页', url: `https://www.biorxiv.org/content/${postId}v${v}` }]
|
||||
};
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user