新增 PDF/EPUB/MOBI/AZW 内置阅读器,PDF 分段读取支持超大文件, 批注、读书与画布笔记、封面生成与本地导入。AI 助手支持三种协议、 图像上下文与安全 Markdown 渲染,上下文范围改为 选中/当前页/全文, 页面与全文无需选中文本即可发送,全文会提示可能超出模型限制。 便携版输出目录固定为 PeopleLib-windows-x64,不再随版本号变化, 避免升级后 data/ 被遗留在旧目录。 Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
94 lines
3.5 KiB
JavaScript
94 lines
3.5 KiB
JavaScript
const { fetchJson, clampPage, isRetryable } = require('./http');
|
|
|
|
const PAGE_SIZE = 30;
|
|
const CATALOG_START = '2000-01-01';
|
|
const SNAPSHOT_TTL = 5 * 60 * 1000;
|
|
let catalogSnapshot = null;
|
|
|
|
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}`, { retries: 0 });
|
|
const total = parseInt(j.messages && j.messages[0] && j.messages[0].total, 10) || 0;
|
|
return { total, collection: j.collection || [] };
|
|
} catch (e) {
|
|
last = e;
|
|
// 超时与网络抖动是这里最常见的瞬时故障,必须一并重试
|
|
if (!isRetryable(e) || i === tries - 1) 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 to = dateStr(new Date());
|
|
if (!catalogSnapshot || catalogSnapshot.to !== to || catalogSnapshot.expiresAt <= Date.now()) {
|
|
const first = await fetchWindow(server, CATALOG_START, to, 0);
|
|
catalogSnapshot = { to, total: first.total, expiresAt: Date.now() + SNAPSHOT_TTL };
|
|
}
|
|
const total = catalogSnapshot.total;
|
|
const end = Math.max(0, total - (page - 1) * PAGE_SIZE);
|
|
const cursor = Math.max(0, end - PAGE_SIZE);
|
|
const count = end - cursor;
|
|
if (!count) return { items: [], maxPage: Math.max(1, Math.ceil(total / PAGE_SIZE)), page };
|
|
const { collection } = await fetchWindow(server, CATALOG_START, to, cursor);
|
|
const items = collection.slice(0, count).reverse().map((r) => toItem(r, server));
|
|
return { items, maxPage: Math.max(1, Math.ceil(total / PAGE_SIZE)), 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}` }]
|
|
};
|
|
}
|
|
};
|