// ==UserScript==
// @name 国家中小学智慧教育平台教材PDF电子课本链接与下载工具
// @namespace https://greasyfork.org/zh-CN/scripts/466598
// @version 1.2.5
// @description 教材列表页与预览页添加了PDF按钮,免登录查看或下载电子课本与课外书籍,可批量下载
// @match *://basic.smartedu.cn/*
// @match *://www.zxx.edu.cn/*
// @grant none
// @downloadURL none
// ==/UserScript==
(function() {
'use strict';
let link;
const pattern = /contentId=([a-zA-Z0-9_-]+)/; // 匹配 contentId 的正则表达式
const contentId = window.location.href.match(pattern)?.[1]; // 获取 contentId 的数值
link = `https://r3-ndr.ykt.cbern.com.cn/edu_product/esp/assets_document/${contentId}.pkg/pdf.pdf`;
// window.location.href = link; // 直接重定向到该网址,经测试可用
const maxTimeToCheck = 5000; // 最多检查 5000 毫秒,即 5 秒
let elapsedTime = 0;
function checkConfirmButtons() {
const confirmBtns = document.querySelector(".fish-modal-confirm-btns");
if (confirmBtns) {
// 去除阻碍
document.querySelector(".fish-modal-root").remove(); // 去除遮罩
const body = document.querySelector("html > body"); // 去除 body 属性
body.removeAttribute("class");
body.removeAttribute("style");
// 添加阅读器
const name = document.querySelectorAll(".fish-breadcrumb-link")[2]?.textContent;
const indexModuleWrapperECeCo = document.querySelector(".index-module_wrapper_ECeCo");
if (indexModuleWrapperECeCo) {
indexModuleWrapperECeCo.innerHTML = `
`;
}
// 全屏切换
const fullscreenBtn = document.querySelector(".tool-btn.fullscreen");
const courseDocument = document.querySelector(".course-document");
const htmlElement = document.querySelector("html");
fullscreenBtn.addEventListener("click", () => {
if (courseDocument.classList.contains("full-screen")) { // 当前处于"全屏状态",切换为"非全屏状态"
courseDocument.classList.remove("full-screen");
htmlElement.style.overflow = "";
} else { // 当前处于"非全屏状态",切换为"全屏状态"
courseDocument.classList.add("full-screen");
htmlElement.style.overflow = "hidden";
}
});
// 重定向至登录页
const toLogin = [
document.querySelector(".fish-dropdown-trigger.index-module_assessment-btn_6imdF"),
document.querySelector(".index-module_like-wrap_NbyLe"),
document.querySelector(".index-module_suggestion-wrap_s\\+Ii\\+")
];
toLogin.forEach(element => {
element.addEventListener("click", () => {
window.open("https://auth.smartedu.cn/uias/login/");
});
});
clearInterval(intervalId1); // 清除定时器
} else {
elapsedTime += 100;
if (elapsedTime >= maxTimeToCheck) {
clearInterval(intervalId1); // 清除定时器,停止检查
}
}
}
const intervalId1 = setInterval(checkConfirmButtons, 100); // 每隔 100 毫秒检查一次 fish-modal-confirm-btns 元素是否存在
function checkIndexModule() {
console.log("生成链接: " + link);
fetch(link)
.then(response => {
if (response.ok) {
console.log("生成链接有效"); // 有效链接
} else {
console.log("生成链接无效"); // 无效链接
link = `https://r3-ndr.ykt.cbern.com.cn/edu_product/esp/assets/${contentId}.pkg/pdf.pdf`;
}
})
.catch(() => {
console.log("检测生成链接发生错误"); // 网络错误或请求被拒绝等
});
const container = document.querySelector(".index-module_extra_tUQog"); // 找到要添加按钮的容器元素
if (container && !document.querySelector(".Btns")) {
const div = document.createElement("div");
div.className = "Btns";
div.innerHTML = `📓 查看PDF📓 下载PDF`;
container.appendChild(div); // 将按钮添加到网页中
const element1 = document.querySelector("div.Btns > a.link");
if (element1) {
element1.addEventListener("mouseover", function() {
this.innerHTML = "📘 查看PDF";
this.style.color = "#226dec"; // 鼠标移入时修改元素的样式
});
element1.addEventListener("mouseout", function() {
this.innerHTML = "📓 查看PDF";
this.style.color = "#888"; // 鼠标移出时恢复原来的样式
});
element1.addEventListener("mousedown", function() {
this.innerHTML = "📘 查看PDF";
this.style.color = "#226dec"; // 鼠标按下时修改元素的样式
});
}
const element2 = document.querySelector("div.Btns > a.download");
if (element2) {
element2.addEventListener("click", function(event) { // 点击下载
var fileUrl = link;
var xhr = new XMLHttpRequest();
xhr.open("GET", fileUrl);
xhr.responseType = "blob";
xhr.onloadstart = function() { // 初始化进度
element2.innerText = "0%";
};
xhr.onprogress = function(event) {
if (event.lengthComputable) {
var progress = Math.round((event.loaded / event.total) * 100);
element2.innerText = "📓 下载中 (" + progress + "%)";
}
};
xhr.onload = function() {
if (xhr.status === 200) {
var blob = xhr.response;
var downloadLink = document.createElement("a");
downloadLink.href = URL.createObjectURL(blob);
downloadLink.download = document.querySelector(".index-module_title_bnE9V").innerText; // 文件名
downloadLink.dispatchEvent(new MouseEvent("click"));
element2.innerText = "📓 下载PDF"; // 下载完成后恢复按钮文字
}
};
xhr.send(); // 发送请求
});
}
clearInterval(intervalId2); // 清除定时器
} else {
elapsedTime += 100;
if (elapsedTime >= maxTimeToCheck) {
clearInterval(intervalId2); // 清除定时器,停止检查
}
}
}
const intervalId2 = setInterval(checkIndexModule, 100); // 每隔 100 毫秒检查一次 index-module_extra_tUQog 元素是否存在
const originalFetch = window.fetch; // 纠正课后阅读、备课和自学页面链接
window.fetch = function (url, options) {
return originalFetch(url, options).then(function(response) {
if (response.ok && url.endsWith(".pdf")) {
let modifiedURL = url.replace("-private", "");
console.log("Fetched PDF (modified valid URL): " + modifiedURL);
const element1 = document.querySelector("a.link"); // 按钮 element1
if (element1) {
element1.href = modifiedURL; // 修改 element1 的链接
}
link = modifiedURL; // 修改 element2 的链接
const prepContainer = document.querySelector(".index-module_resource-info__fav_3xNjm"); // 备课页面添加按钮
if (prepContainer && !document.querySelector(".index-module_resource-info__fav_3xNjm > div > .link")) {
const div = document.createElement("div");
div.setAttribute("style", "display: flex; align-items: center;");
div.innerHTML = `📓 查看PDF`; // 待纠正时更新链接
prepContainer.appendChild(div);
}
const syncClassSuggestion = document.getElementsByClassName("index-module_suggestion-wrap_s+Ii+ suggestion")[0]; // 自学页面添加按钮
if (syncClassSuggestion && !document.querySelector("div > .link")) {
const div = document.createElement("div");
div.setAttribute("style", "display: flex; align-items: center; margin-left: 30px");
div.innerHTML = `📓 查看PDF`; // 待纠正时更新链接
syncClassSuggestion.insertAdjacentElement("afterend", div);
}
}
return response;
});
};
function checkIndexModules() {
const old_added_PDF_btns = document.querySelectorAll(".PDF-btns");
if (old_added_PDF_btns) {
old_added_PDF_btns.forEach(element => {
element.remove();
});
}
const item = document.querySelectorAll("li.index-module_item_GfOnF");
if (item) {
const img = document.querySelectorAll("div.index-module_cover_DGT6P > img");
let pdfArray = []; // 声明PDF空数组
for (let i = 0; i < img.length; i++) {
if (img[i]) {
const src = img[i].getAttribute("src");
let pdf;
if (src.includes("esp/assets/")) {
let extractedText = src.match(/assets\/(.*?)\./)[1];
pdf = `https://r3-ndr.ykt.cbern.com.cn/edu_product/esp/assets/${extractedText}.pkg/pdf.pdf`;
} else if (src.includes("esp/assets_document/")) {
let extractedText = src.match(/assets_document\/(.*?)\./)[1];
pdf = `https://r3-ndr.ykt.cbern.com.cn/edu_product/esp/assets_document/${extractedText}.pkg/pdf.pdf`;
} else if (src.includes("65/document/")) {
let extractedText = src.match(/document\/(.*?)\/image/)[1];
pdf = `https://v3.ykt.cbern.com.cn/65/document/${extractedText}/pdf.pdf`;
} else {
console.log("未识别链接");
}
if (pdf) {
pdfArray.push(pdf); // 将 pdf 添加到数组中
console.log(`pdf${i + 1}: ${pdf}`);
}
clearInterval(intervalId3); // 查找到 img,清除定时器停止检查
}
}
const content = document.querySelectorAll("div.index-module_content_KmLzG");
for (let i = 0; i < content.length; i++) {
if (content[i]) {
content[i].setAttribute("style", "width: 500px;"); // 统一 content 宽度
}
}
const contentName = document.querySelectorAll("div.index-module_line_LgJAC");
const added_PDF_btns = document.querySelectorAll("li.index-module_item_GfOnF > div.PDF-btns");
for (let i = 0; i < item.length; i++) {
if (item[i] && !added_PDF_btns[i] && pdfArray[i]) {
const PDF_btns = document.createElement("div"); // 创建 PDF_btns
PDF_btns.setAttribute("class", "PDF-btns");
PDF_btns.setAttribute("style", "width: 300px;");
PDF_btns.innerHTML = `
查看PDF
下载PDF
`;
item[i].appendChild(PDF_btns); // 将 PDF_btns 添加到父元素中
PDF_btns.addEventListener("click", function(event) { // 停止 PDF_btns 的点击事件向更上一层元素传播
event.stopPropagation();
});
const element2 = document.querySelectorAll("div.PDF-btns > a.fish-btn.fish-btn-primary")[i];
if (element2) {
element2.addEventListener("click", function(event) { // 点击下载
var fileUrl = pdfArray[i];
var xhr = new XMLHttpRequest();
xhr.open("GET", fileUrl);
xhr.responseType = "blob";
xhr.onloadstart = function() { // 初始化进度
element2.innerText = "下载中0%";
};
xhr.onprogress = function(event) {
if (event.lengthComputable) {
var progress = Math.round((event.loaded / event.total) * 100);
element2.innerText = "下载中 (" + progress + "%)";
}
};
xhr.onload = function() {
if (xhr.status === 200) {
var blob = xhr.response;
var downloadLink = document.createElement("a");
downloadLink.href = URL.createObjectURL(blob);
downloadLink.download = contentName[i].querySelector("span").getAttribute("title"); // 文件名
downloadLink.dispatchEvent(new MouseEvent("click"));
element2.innerText = "下载PDF"; // 下载完成后恢复按钮文字
}
};
xhr.send(); // 发送请求
});
}
}
}
} else {
elapsedTime += 100;
if (elapsedTime >= maxTimeToCheck) {
clearInterval(intervalId3); // 清除定时器,停止检查
}
}
}
let intervalId3 = setInterval(checkIndexModules, 100); // 每隔 100 毫秒检查一次
function doItAgain() {
clearInterval(intervalId3); // 防止多次启动定时器
elapsedTime = 0; // 逝去时间归零
intervalId3 = setInterval(checkIndexModules, 100); // 创建新的定时器以待调用
}
const observer = new MutationObserver(function(mutations) { // 创建一个MutationObserver
mutations.forEach(function(mutation) {
const target = mutation.target; // 获取mutation的目标元素
if (!target.closest("li.index-module_item_GfOnF")) { // 检查是否是要忽略的元素或其后代元素
console.log("DOM树已变化,已排除li.index-module_item_GfOnF及其后代");
doItAgain(); // 执行新的定时器
}
});
});
const config = { // 配置需要观察的内容
childList: true, // 监听子节点的变化
subtree: true // 监听所有后代节点的变化
};
observer.observe(document.body, config); // 将观察器绑定到根节点上
})();