// ==UserScript== // @name THU PDF Downloader - 清华大学图书馆/PDF.js 强制下载 // @namespace http://tampermonkey.net/ // @version 2.5 // @description 专门针对清华大学图书馆、学位论文等 PDF.js 访问限制场景。直接从浏览器内存中提取 PDF 原始流并下载,支持页面标题重命名。 // @author 王一航 // @match *://newetds.lib.tsinghua.edu.cn/* // @match *://*/* // @grant none // @license MIT // @downloadURL https://update.greasyfork.icu/scripts/565801/THU%20PDF%20Downloader%20-%20%E6%B8%85%E5%8D%8E%E5%A4%A7%E5%AD%A6%E5%9B%BE%E4%B9%A6%E9%A6%86PDFjs%20%E5%BC%BA%E5%88%B6%E4%B8%8B%E8%BD%BD.user.js // @updateURL https://update.greasyfork.icu/scripts/565801/THU%20PDF%20Downloader%20-%20%E6%B8%85%E5%8D%8E%E5%A4%A7%E5%AD%A6%E5%9B%BE%E4%B9%A6%E9%A6%86PDFjs%20%E5%BC%BA%E5%88%B6%E4%B8%8B%E8%BD%BD.meta.js // ==/UserScript== (function() { 'use strict'; // === UI 配置 === const CONFIG = { mainColor: '#660874', // 清华紫 hoverColor: '#8c259d', textColor: '#ffffff', position: { bottom: '20px', right: '20px' }, zIndex: '2147483647' }; // 定义按钮创建逻辑(封装成函数,稍后调用) function initButton(app) { // 防止重复添加 if (document.getElementById('thu-pdf-downloader-container')) return; const container = document.createElement('div'); container.id = 'thu-pdf-downloader-container'; // 注入样式 const style = document.createElement('style'); style.innerHTML = ` #thu-pdf-downloader-container { position: fixed; bottom: ${CONFIG.position.bottom}; right: ${CONFIG.position.right}; z-index: ${CONFIG.zIndex}; font-family: sans-serif; } #thu-pdf-btn { display: flex; align-items: center; justify-content: center; background-color: ${CONFIG.mainColor}; color: ${CONFIG.textColor}; border: none; border-radius: 4px; padding: 8px 16px; font-size: 14px; font-weight: 600; cursor: pointer; box-shadow: 0 2px 5px rgba(0,0,0,0.2); transition: all 0.2s ease; user-select: none; } #thu-pdf-btn:hover { background-color: ${CONFIG.hoverColor}; transform: translateY(-1px); } #thu-pdf-btn svg { width: 16px; height: 16px; margin-right: 8px; fill: currentColor; } `; document.head.appendChild(style); const btn = document.createElement('div'); btn.id = 'thu-pdf-btn'; btn.innerHTML = ` 下载 PDF `; btn.title = "检测到 PDF.js 实例,点击强制下载"; // 点击事件 btn.onclick = async () => { const updateState = (text, bg) => { btn.querySelector('span').innerText = text; if(bg) btn.style.backgroundColor = bg; }; updateState('处理中...', '#555'); try { // 直接使用传入的 app 实例,不再递归查找,确保 100% 准确 const data = await app.pdfDocument.getData(); const blob = new Blob([data], { type: 'application/pdf' }); const url = URL.createObjectURL(blob); const rawTitle = document.title || 'downloaded_document'; const safeTitle = rawTitle.replace(/[\\/:*?"<>|]/g, '_').trim(); const a = document.createElement('a'); a.href = url; a.download = `${safeTitle}.pdf`; document.body.appendChild(a); a.click(); setTimeout(() => { document.body.removeChild(a); URL.revokeObjectURL(url); updateState('成功!', '#28a745'); setTimeout(() => updateState('下载 PDF', CONFIG.mainColor), 2000); }, 100); } catch (err) { console.error("[THU-PDF] Error:", err); alert("下载出错,请检查控制台"); updateState('出错', '#dc3545'); } }; container.appendChild(btn); document.body.appendChild(container); console.log("[THU-PDF] 按钮已注入 (针对当前 Context)"); } // === 核心修改:轮询检测机制 === // 不再默认显示按钮,而是查找是否存在 PDFViewerApplication const checkInterval = setInterval(() => { // 1. 检测全局对象是否存在 if (window.PDFViewerApplication && window.PDFViewerApplication.pdfDocument) { clearInterval(checkInterval); initButton(window.PDFViewerApplication); } }, 1000); // 每秒检查一次 // 10秒后停止检测,节省性能 (可根据网络情况调整) setTimeout(() => clearInterval(checkInterval), 15000); })();