// ==UserScript== // @name 粉笔 Keynote 下载器 // @namespace https://www.fenbi.com/ // @version 1.0.0 // @description 进入粉笔课程回放页后自动捕获 keynote 文件并下载(原生浏览器下载,支持 IDM/Aria 劫持) // @author You // @match https://www.fenbi.com/spa/pwa/class/* // @match https://*.fenbi.com/spa/pwa/class/* // @grant unsafeWindow // @run-at document-start // ==/UserScript== (function () { 'use strict'; var win = (typeof unsafeWindow !== 'undefined') ? unsafeWindow : window; var captured = false; var keynoteUrl = null; function triggerDownload(url) { var hash = extractHash(url); var filename = hash ? hash + '.pdf' : 'keynote.pdf'; var a = document.createElement('a'); a.href = url; a.download = filename; a.style.display = 'none'; document.body.appendChild(a); a.click(); document.body.removeChild(a); console.log('[粉笔Keynote下载器] 已触发下载: ' + filename); } function extractHash(url) { var m = url.match(/keynotes\/([a-f0-9]+)/i); return m ? m[1] : null; } function onKeynoteFound(url) { if (captured) return; captured = true; keynoteUrl = url; console.log('[粉笔Keynote下载器] 捕获到 keynote URL: ' + url); setTimeout(function () { triggerDownload(url); showNotification('已下载 keynote: ' + (extractHash(url) || 'unknown') + '.pdf'); }, 2000); } function showNotification(msg) { var div = document.createElement('div'); div.textContent = msg; div.style.cssText = 'position:fixed;top:20px;right:20px;z-index:99999;' + 'background:#4CAF50;color:white;padding:12px 20px;border-radius:8px;' + 'font-size:14px;font-family:sans-serif;box-shadow:0 4px 12px rgba(0,0,0,0.3);' + 'transition:opacity 0.5s;opacity:1;'; document.body.appendChild(div); setTimeout(function () { div.style.opacity = '0'; }, 4000); setTimeout(function () { if (div.parentNode) div.parentNode.removeChild(div); }, 5000); } function installHooks() { var origOpen = win.XMLHttpRequest.prototype.open; win.XMLHttpRequest.prototype.open = function (method, url) { if (url && typeof url === 'string' && url.indexOf('keynoteali') !== -1 || (url && typeof url === 'string' && url.indexOf('keynotes/') !== -1 && url.indexOf('fbstatic') !== -1)) { onKeynoteFound(url); } return origOpen.apply(this, arguments); }; if (win.fetch) { var origFetch = win.fetch; win.fetch = function () { var url = arguments[0]; if (typeof url === 'string' && (url.indexOf('keynoteali') !== -1 || url.indexOf('keynotes/') !== -1)) { onKeynoteFound(url); } else if (url && url.url && typeof url.url === 'string' && url.url.indexOf('keynoteali') !== -1) { onKeynoteFound(url.url); } return origFetch.apply(this, arguments); }; } } function scanPerformanceEntries() { if (captured) return; try { var resources = win.performance.getEntriesByType('resource'); for (var i = 0; i < resources.length; i++) { var name = resources[i].name; if (name.indexOf('keynoteali') !== -1 || (name.indexOf('keynotes/') !== -1 && name.indexOf('fbstatic') !== -1)) { onKeynoteFound(name); return; } } } catch (e) {} } var observer = null; function startObserver() { if (typeof win.PerformanceObserver === 'undefined') return; try { observer = new win.PerformanceObserver(function (list) { var entries = list.getEntries(); for (var i = 0; i < entries.length; i++) { var name = entries[i].name; if (name.indexOf('keynoteali') !== -1 || (name.indexOf('keynotes/') !== -1 && name.indexOf('fbstatic') !== -1)) { onKeynoteFound(name); } } }); observer.observe({ entryTypes: ['resource'] }); } catch (e) {} } installHooks(); startObserver(); var scanCount = 0; var scanTimer = setInterval(function () { scanCount++; scanPerformanceEntries(); if (captured || scanCount > 30) { clearInterval(scanTimer); } }, 1000); console.log('[粉笔Keynote下载器] 脚本已加载,等待捕获 keynote URL...'); })();