@a9i5k4/dsh-literature 0.2.0 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +83 -61
- package/README_EN.md +159 -0
- package/lib/client.js +13 -12
- package/lib/index.js +38 -8
- package/package.json +2 -1
package/lib/index.js
CHANGED
|
@@ -134,7 +134,12 @@ var init_config = __esm({
|
|
|
134
134
|
entryMode: "auto",
|
|
135
135
|
/** Watched folder: new PDFs here are auto-imported into the built-in library. */
|
|
136
136
|
importDir: "",
|
|
137
|
-
watchImport: false
|
|
137
|
+
watchImport: false,
|
|
138
|
+
/** User-configured custom download sources (mirrors / institutional proxies).
|
|
139
|
+
* Each: { id, label, urlTemplate, headers?, enabled, order }. URL templates
|
|
140
|
+
* may use {doi} {arxiv} {isbn} {title} {url}. These are the user's own
|
|
141
|
+
* configuration — the plugin never ships any such source itself. */
|
|
142
|
+
customSources: []
|
|
138
143
|
};
|
|
139
144
|
cached = null;
|
|
140
145
|
}
|
|
@@ -1184,11 +1189,31 @@ function looksLikePdf(buffer) {
|
|
|
1184
1189
|
if (!buffer || buffer.length < 8) return false;
|
|
1185
1190
|
return buffer.subarray(0, 5).equals(PDF_MAGIC);
|
|
1186
1191
|
}
|
|
1187
|
-
|
|
1192
|
+
function renderSourceTemplate(template, record) {
|
|
1193
|
+
if (!template || !/^https?:\/\//i.test(String(template))) return "";
|
|
1194
|
+
const vars = {
|
|
1195
|
+
doi: record.doi ? encodeURIComponent(record.doi) : "",
|
|
1196
|
+
arxiv: record.arxiv ? encodeURIComponent(record.arxiv) : "",
|
|
1197
|
+
isbn: record.isbn ? encodeURIComponent(record.isbn) : "",
|
|
1198
|
+
title: record.title ? encodeURIComponent(record.title) : "",
|
|
1199
|
+
url: record.url ? encodeURIComponent(record.url) : ""
|
|
1200
|
+
};
|
|
1201
|
+
for (const [k, v] of Object.entries(vars)) {
|
|
1202
|
+
if (!v && String(template).includes(`{${k}}`)) return "";
|
|
1203
|
+
}
|
|
1204
|
+
for (const [k, v] of Object.entries(vars)) {
|
|
1205
|
+
if (!v && String(template).includes(`{${k}}`)) return "";
|
|
1206
|
+
}
|
|
1207
|
+
let out = String(template);
|
|
1208
|
+
for (const [k, v] of Object.entries(vars)) out = out.split(`{${k}}`).join(v);
|
|
1209
|
+
if (out.includes("{") || !/^https?:\/\//i.test(out)) return "";
|
|
1210
|
+
return out;
|
|
1211
|
+
}
|
|
1212
|
+
async function candidates(record, { unpaywallEmail, customSources = [] } = {}) {
|
|
1188
1213
|
const out = [];
|
|
1189
|
-
const push2 = (url, source, kind = "pdf") => {
|
|
1214
|
+
const push2 = (url, source, kind = "pdf", headers) => {
|
|
1190
1215
|
if (!url) return;
|
|
1191
|
-
out.push({ url: String(url), source, kind });
|
|
1216
|
+
out.push({ url: String(url), source, kind, headers });
|
|
1192
1217
|
};
|
|
1193
1218
|
if (record.arxiv) {
|
|
1194
1219
|
const base = String(record.arxiv).replace(/v\d+$/, "");
|
|
@@ -1231,6 +1256,10 @@ async function candidates(record, { unpaywallEmail } = {}) {
|
|
|
1231
1256
|
warn("crossref link lookup failed:", e.message);
|
|
1232
1257
|
}
|
|
1233
1258
|
}
|
|
1259
|
+
for (const src of [...customSources].filter((x) => x && x.enabled).sort((a, b) => (a.order ?? 100) - (b.order ?? 100))) {
|
|
1260
|
+
const url = renderSourceTemplate(src.urlTemplate, record);
|
|
1261
|
+
if (url) push2(url, src.label || src.id || "custom", "pdf", src.headers);
|
|
1262
|
+
}
|
|
1234
1263
|
const seen = /* @__PURE__ */ new Set();
|
|
1235
1264
|
return out.filter((c) => {
|
|
1236
1265
|
const k = c.url.toLowerCase();
|
|
@@ -1256,8 +1285,8 @@ function extractPdfUrlFromHtml(html, baseUrl2) {
|
|
|
1256
1285
|
}
|
|
1257
1286
|
return null;
|
|
1258
1287
|
}
|
|
1259
|
-
async function fetchPdf(record, { timeoutMs = 3e4, unpaywallEmail = "" } = {}) {
|
|
1260
|
-
const list = await candidates(record, { unpaywallEmail });
|
|
1288
|
+
async function fetchPdf(record, { timeoutMs = 3e4, unpaywallEmail = "", customSources = [] } = {}) {
|
|
1289
|
+
const list = await candidates(record, { unpaywallEmail, customSources });
|
|
1261
1290
|
if (!list.length) {
|
|
1262
1291
|
const bookLike = ["book", "bookSection", "report", "thesis"].includes(record.itemType);
|
|
1263
1292
|
throw new PdfFailure(
|
|
@@ -1273,7 +1302,8 @@ async function fetchPdf(record, { timeoutMs = 3e4, unpaywallEmail = "" } = {}) {
|
|
|
1273
1302
|
try {
|
|
1274
1303
|
const { buffer, contentType, finalUrl } = await httpGetBuffer(cand.url, {
|
|
1275
1304
|
timeoutMs,
|
|
1276
|
-
accept: "application/pdf,*/*;q=0.8"
|
|
1305
|
+
accept: "application/pdf,*/*;q=0.8",
|
|
1306
|
+
...cand.headers && typeof cand.headers === "object" ? { headers: cand.headers } : {}
|
|
1277
1307
|
});
|
|
1278
1308
|
if (looksLikePdf(buffer)) {
|
|
1279
1309
|
log(`pdf ok: ${cand.url} (${(buffer.length / 1024).toFixed(0)} KB via ${cand.source})`);
|
|
@@ -1966,7 +1996,7 @@ async function fetchItemPdf(key) {
|
|
|
1966
1996
|
const result = await withRetry(
|
|
1967
1997
|
(attempt) => {
|
|
1968
1998
|
if (attempt > 1) emitTask({ id: task.id, state: "running", progress: 10, message: `\u7B2C ${attempt} \u6B21\u5C1D\u8BD5`, updatedAt: Date.now() });
|
|
1969
|
-
return fetchPdf(current.record, { timeoutMs: config.fetchTimeoutMs, unpaywallEmail: config.unpaywallEmail });
|
|
1999
|
+
return fetchPdf(current.record, { timeoutMs: config.fetchTimeoutMs, unpaywallEmail: config.unpaywallEmail, customSources: config.customSources });
|
|
1970
2000
|
},
|
|
1971
2001
|
{ ...config.retry, label: `fetch ${key}` }
|
|
1972
2002
|
);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@a9i5k4/dsh-literature",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.2",
|
|
4
4
|
"description": "DSH Literature 文献侧窗:在 DeepSeek Harness 侧边栏识别 DOI/arXiv/标题、抓取元数据与全文、写入本地文献库或导出目录,并提供内置 PDF 阅读器(缩放/翻页/目录/搜索/高亮/笔记)。",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
"lib",
|
|
14
14
|
"cordis.patch.yml",
|
|
15
15
|
"README.md",
|
|
16
|
+
"README_EN.md",
|
|
16
17
|
"LICENSE"
|
|
17
18
|
],
|
|
18
19
|
"scripts": {
|