Oxford Academic导出PDF

自动获取并合并PDF

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==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:分段下载PDF
                dlBtn.innerText="30% 下载PDF";
                count=0;
                for(let chapter of parsedCatagory.chapters){
                    if(chapter.child==false){
                        //异步跨域下载PDF
                        function 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){
                        //合并PDF
                        const 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/[email protected]/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/[email protected]/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;
        }
    }
})();