Greasy Fork is available in English.
适用于【合肥工业大学图书馆-纸电一体化读者服务##】,对文档截图,合并为PDF。
// ==UserScript== // @name 合肥工业大学图书馆PDF下载 // @namespace http://tampermonkey.net/ // @version 0.0.2 // @description 适用于【合肥工业大学图书馆-纸电一体化读者服务##】,对文档截图,合并为PDF。 // @author [email protected] // @match https://hfut.zhitongda.cn/opac/readBook // @require https://greasyfork.org/scripts/445312-wk-full-cli/code/wk-full-cli.user.js // @icon https://hfut.zhitongda.cn/opac/favicon.ico // @grant none // @run-at document-idle // @license GPL-3.0-only // ==/UserScript== // import { utils } from "../utils"; (function() { "use strict"; // ##网址 // https://hfut.zhitongda.cn/opac/reads // 全局常量 const API = "getPdfUrl"; // 取得 PDF 数据的全局函数名称 const DL_BTN = 1; // 按钮序号,此按钮用于下载 PDF const utils = wk$; // 全局变量 let iframe = null; /** * 输出 [info] 级别日志到控制台 * @param {...any} args */ function print(...args) { console.info("[wk]:", ...args); } /** * 将错误定位转为可读的字符串 * @param {Error} err * @returns {string} */ function get_stack(err) { let stack = `${err.stack}`; const matches = stack.matchAll(/at .+?( [(].+[)])/g); for (const group of matches) { stack = stack.replace(group[1], ""); } return stack.trim(); } /** * @param {(event: PointerEvent) => Promise<void>} btn_fn * @returns {(event: PointerEvent) => Promise<void>} */ function wrap_btn_fn(btn_fn) { async function inner(event) { const btn = event.target; const text = btn.textContent; btn.disabled = true; try { await btn_fn(event); } catch(err) { utils.raise( `发生错误,请在评论区反馈,或联系 [email protected]\n` + `错误原因:\n` + `${err}\n` + `发生位置:\n` + `${get_stack(err)}` ); } btn.textContent = text; btn.disabled = false; } return inner; } /** * 初始化进度计数器 */ function init_counter() { let _counter = 0; let _text = "未初始化进度:{}"; /** * 设置进度文本,必须正好包含一对大括号,用于填充 counter * @param {string} text */ function set_text(text) { const [left, right] = [_text.indexOf("{}"), _text.lastIndexOf("{}")]; if (left === -1) { throw new Error(`进度文本中必须包含一对大括号`); } if (left !== right) { throw new Error(`进度文本中能且仅能包含一对大括号`); } _text = text; } /** * counter + 1,且输出进度 */ function count() { _counter += 1; const progress = _text.replace("{}", `${_counter}`); try { print("<进度>", progress); utils.btn(DL_BTN).textContent = progress; } catch (err) { console.error(err); } } /** * 重置 counter */ function reset_counter() { _counter = 0; _text = "未初始化进度:{}"; } return { set_text, count, reset_counter }; } const { set_text, count, reset_counter } = init_counter(); /** * iframe 内单元素选择器 * @param {string} selectors * @returns {HTMLElement | undefined} */ function iframe$(selectors) { if (iframe instanceof HTMLIFrameElement) { return iframe.contentDocument.querySelector(selectors); } const _iframe = document.querySelector("iframe"); if (_iframe) { if (!iframe) { iframe = _iframe; } return iframe.contentDocument.querySelector(selectors); } } /** * 请求第 pn 页 PDF * @param {number} pn * @returns {Promise<undefined | Uint8Array>} */ async function get_data_by_pn(pn) { const r###lt = await window[API](pn); count(); // 更新进度 if (!r###lt.data) { print(`第 ${pn} 页请求得到空响应`); return; } if (!r###lt.success) { print(`第 ${pn} 页请求失败`); return; } return utils.b64_to_bytes(r###lt.data); } /** * 下载并返回全部 PDF 数据 * @param {number} max_pn * @returns {Promise<Array<Uint8Array>>} */ async function fetch_pdfs(max_pn) { // 准备请求 reset_counter(); set_text(`已下载 {}/${max_pn} 页`); print(`最大页码:${max_pn}`); print("开始下载 PDF"); const tasks = []; // 请求数据 for (let i = 0; i < max_pn; i++) { tasks[i] = get_data_by_pn(i); } // 完成请求完成 const pdfs = await utils.gather(tasks); reset_counter(); print("全部 PDF 下载完成"); return pdfs; } /** * 请求并下载 PDF * @returns {Promise<void>} */ async function make_pdf() { // 确认继续 if (!confirm("开始下载后会导致文档预览消失,是否继续?")) { return; } // 环境检测 if (!window[API] || !(window[API] instanceof Function)) { utils.raise(`window.${API} 不存在,无法请求 PDF 数据`); } const title = iframe$("#bookName")?.title || iframe$("title")?.textContent || "电子书"; const max_pn = parseInt(iframe$("#pageNumber").max); // const max_pn = 2; if (max_pn < 1) { utils.raise(`不正常的最大页码:${max_pn}`); } // 移除PDF预览 iframe.remove(); const pdfs = await fetch_pdfs(max_pn); let size = pdfs.map(bytes => bytes.length).reduce((sum, len) => sum + len); size = size / #### / ####; // 下载原始数据 alert(`即将保存原始数据集(${size.toFixed(0)} MB),请用【pdfs-merger】转换为 PDF`); const type = "application/octet-stream"; const blob = new Blob(pdfs, { type }); utils.save(`${title}.dat`, blob, type); } make_pdf = wrap_btn_fn(make_pdf); /** * 合肥工业大学图书馆-纸电一体化读者服务##-文档下载策略 */ function zhitongda() { utils.create_btns(); utils.onclick(make_pdf, DL_BTN, "下载PDF"); } zhitongda(); /** * 更新日志 * --- * (v0.0.1) [2023-08-17] * - 发布初版 * --- * (v0.0.2) [2023-08-18] * - 移除了网页上合成PDF的功能,现在只能下载数据集(原先的合成PDF功能有BUG) */ })();