Greasy Fork is available in English.
自动获取并合并PDF
- // ==UserScript==// @name Oxford Academic导出PDF// @namespace https://qinlili.bid// @version 1.0.0// @description 自动获取并合并PDF// @author 琴梨梨// @license WTFPL// @match https://academic.oup.com/edited-volume/*// @icon https://oup.silverchair-cdn.com/UI/app/img/v-638282418223920402/apple-touch-icon.png// @run-at document-idle// @require https://lib.baomitu.com/pdf-lib/1.17.1/pdf-lib.min.js#sha512-z8IYLHO8bTgFqj+yrPyIJnzBDf7DDhWwiEsk4sY+Oe6J2M+WQequeGS7qioI5vT6rXgVRb4K1UVQC5ER7MKzKQ==// @grant GM_xmlhttpRequest// @grant GM_registerMenuCommand// ==/UserScript==(async function() {//CODENAME:Carboxylate'use strict';const config={//带目录导出,需要依赖闭源的PSPDFKIT,目前暂不可用,有待进一步开发writeOutline:false,//去除账号标识,开发中removeAccountInfo:false};GM.registerMenuCommand("原神,启动!", () => {location.href="https://ys.mihoyo.com/";});const dlFile = (link, name) => {let eleLink = document.createElement('a');eleLink.download = name;eleLink.style.display = 'none';eleLink.href = link;document.body.appendChild(eleLink);eleLink.click();document.body.removeChild(eleLink);};const loadScriptAsync = link => {return new Promise(resolve => {let script = document.createElement("script");script.src = link;script.onload = resolve;document.body.appendChild(script);});};switch(location.host){case("academic.oup.com"):{//文档详情页console.log("关注笙歌喵~关注帅比笙歌超可#OvO谢谢喵~");console.log("Carboxylate 1.0.0");const titleDiv=document.getElementsByClassName("book-bottom-section__title-wrap")[0];let dlBtn=document.createElement("button");dlBtn.innerText="下载全部PDF";titleDiv.appendChild(dlBtn);dlBtn.onclick=async ()=>{dlBtn.onclick=null;//阶段1:建立目录结构dlBtn.innerText="0% 分析目录结构";const catagory=document.getElementsByClassName("bookToc")[0];let parsedCatagory={title:document.getElementsByClassName("book-info__title")[0].innerText.trim(),author:document.getElementsByClassName("book-info__author-link")[0].innerText.trim(),chapters:[]};const parseChapter=(ele,depth)=>{if(ele.getElementsByClassName("collections-child-list").length==0||ele.getElementsByClassName("collections-child-list")[0].children.length==0){//顶级章节,直接处理[].forEach.call(ele.children,sub=>{if(sub.className=="tocLink"&&sub.tagName=="A"){//获取标题和链接parsedCatagory.chapters.push({title:(sub.getElementsByClassName("tocLink-label")[0]?sub.getElementsByClassName("tocLink-label")[0].innerText:"")+" "+sub.getElementsByClassName("tocLink-title")[0].innerText,link:sub.href,depth:depth,child:false,debug:sub});}})}else{//读取子章节parsedCatagory.chapters.push({title:(ele.getElementsByClassName("tocLink-label")[0]?ele.getElementsByClassName("tocLink-label")[0].innerText:"")+" "+ele.getElementsByClassName("tocLink-title")[0].innerText,link:null,depth:depth,child:true,debug:ele});//递归遍历[].forEach.call(ele.getElementsByTagName("ul")[0].getElementsByTagName("li"),ele=>{parseChapter(ele,depth+1);});}}[].forEach.call(catagory.children,ele=>{if(ele.tagName=="LI"){parseChapter(ele,0);}});console.log(parsedCatagory);//阶段2:解析PDF地址dlBtn.innerText="5% 解析PDF地址";let count=0;for(let chapter of parsedCatagory.chapters){if(chapter.child==false){//可下载的章节,需要解析地址let chapterRequest=await fetch(chapter.link, {"referrer": location.href,"method": "GET","mode": "cors","credentials": "include"});let chapterPage=await chapterRequest.text();const parser = new DOMParser();const chapterDoc = parser.parseFromString(chapterPage, "text/html");chapter.pdflink=chapterDoc.getElementsByClassName("at-pdfLink")[0].href;};count++;dlBtn.innerText=(5+25*count/parsedCatagory.chapters.length).toFixed(2)+"% 解析PDF地址["+count+"/"+parsedCatagory.chapters.length+"]";};console.log(parsedCatagory);//阶段3:分段下载PDFdlBtn.innerText="30% 下载PDF";count=0;for(let chapter of parsedCatagory.chapters){if(chapter.child==false){//异步跨域下载PDFfunction asyncFetch(){return new Promise(resolve => {let pic=GM_xmlhttpRequest({method: "GET", url: chapter.pdflink, responseType: "blob", onload: (res) => {console.log(res.response);chapter.pdffile=res.response;resolve();}})});}await asyncFetch();};count++;dlBtn.innerText=(30+60*count/parsedCatagory.chapters.length).toFixed(2)+"% 下载PDF["+count+"/"+parsedCatagory.chapters.length+"]";};console.log(parsedCatagory);//阶段4:合并PDF导出dlBtn.innerText="90% 合并PDF";const pdfDoc = await PDFLib.PDFDocument.create();count=0;pdfDoc.setTitle(parsedCatagory.title);pdfDoc.setAuthor(parsedCatagory.author);pdfDoc.setCreator("Carboxylate By QINLILI");pdfDoc.setCreationDate(new Date());pdfDoc.setModificationDate(new Date());//合并PDF并记录页码for(let chapter of parsedCatagory.chapters){if(chapter.child==false){//合并PDFconst pdfObj=await PDFLib.PDFDocument.load(await chapter.pdffile.arrayBuffer());const copiedPages = await pdfDoc.copyPages(pdfObj, pdfObj.getPageIndices());copiedPages.forEach((page) => pdfDoc.addPage(page));chapter.pages=copiedPages.length;};count++;dlBtn.innerText=(90+6*count/parsedCatagory.chapters.length).toFixed(2)+"% 合并PDF["+count+"/"+parsedCatagory.chapters.length+"]";};console.log(parsedCatagory);//导出文件dlBtn.innerText="96% 写入目录";const mergedPdfFile = await pdfDoc.save();if(config.writeOutline){await loadScriptAsync("https://cdn.jsdelivr.net/npm/pspdfkit@2023.4.0/dist/pspdfkit.min.js");let foo=document.createElement("div");foo.className="foo";foo.style.width="100vw";foo.style.height="100vh";foo.style.display="none";document.body.appendChild(foo);let instance=await PSPDFKit.load({baseUrl:"https://cdn.jsdelivr.net/npm/pspdfkit@2023.4.0/dist/",document: mergedPdfFile.buffer,container:'.foo'});for(let chapter of parsedCatagory.chapters){const bookmark = new PSPDFKit.Bookmark({name: chapter.title,action: new PSPDFKit.Actions.GoToAction({ pageIndex: count })});await instance.create(bookmark);if(chapter.child==false){//小标题累计页码count+=chapter.pages;};};dlBtn.innerText="98% 导出文件 带目录导出耗时极长,请保持耐心";const documentBuffer = await instance.exportPDF();const pdfFile=new Blob([documentBuffer]);dlFile(URL.createObjectURL(pdfFile),parsedCatagory.title+".pdf")}else{dlBtn.innerText="98% 导出文件";const pdfFile=new Blob([mergedPdfFile]);dlFile(URL.createObjectURL(pdfFile),parsedCatagory.title+".pdf")};dlBtn.innerText="100% 下载成功";};break;};default:{break;}}})();