您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Adds "Download All Chapters" button to Royal Road fictions
当前为
// ==UserScript== // @name Royal Road Download Button // @license MIT // @namespace rtonne // @match https://www.royalroad.com/fiction/* // @exclude https://www.royalroad.com/fiction/*/chapter/* // @grant none // @version 3.1 // @author Rtonne // @description Adds "Download All Chapters" button to Royal Road fictions // @require https://cdn.jsdelivr.net/npm/[email protected] // @require https://cdn.jsdelivr.net/npm/[email protected] // @run-at document-end // ==/UserScript== const customStyle = '<style>.portlet-body p,p{margin-top:0}body{background:#181818;font-family:Open Sans, open-sans, Helvetica Neue, Helvetica, Roboto, Arial, sans-serif;line-height:1.42857143;font-size:16px;margin:0}.font-white{color:#fff !important}.portlet{background:#131313;border:1px solid hsla(0, 0%, 100%, 0.1);color:hsla(0, 0%, 100%, 0.8);padding:1em 20px 0;margin:10px 0;display:flex;flex-direction:column}.author-note-portlet{background:#393939;color:hsla(0, 0%, 100%, 0.8);border:0;padding:0 10px 10px;margin:0 0 1em}.portlet-title{border-bottom:0;margin-bottom:10px;min-height:41px;padding:0;margin-left:15px}.caption{padding:16px 0 2px;display:inline-block;float:left;font-size:18px;line-height:18px}.uppercase{text-transform:uppercase !important}.bold{font-weight:700 !important}a{color:#337ab7;text-shadow:none;text-decoration:none}.portlet-body{padding:10px 15px}p{margin-bottom:1em}.col-md-5{min-height:1px;background:#2a3642;margin-left:-15px;margin-right:-15px;padding:10px}.text-center{text-align:center}.container{margin-left:auto;margin-right:auto;padding-left:15px;padding-right:15px;width:"100%"}.col-md-5 > *,.col-md-5 > * > *{font-weight:300;margin:10px 0}table{background:#004b7a;border:none;border-collapse:separate;border-spacing:2px;box-shadow:1px 1px 1px rgba(0, 0, 0, 0.75);margin:10px auto;width:90%}table td{background:rgba(0, 0, 0, 0.1);border:1px solid hsla(0, 0%, 100%, 0.25) !important;color:#ccc;margin:3px;padding:5px}.spoiler,.spoiler-new{max-height:20px;padding-top:100px;overflow-y:scroll;border:1px solid hsla(0, 0%, 100%, 0.5)}.spoiler-new:before,.spoiler:before{content:"Spoiler ahead:"}.btn-primary{box-shadow:none;outline:none;line-height:1.44;background-color:#337ab7;color:#fff;padding:6px 0;text-align:center;display:inline-block;font-size:14px;font-weight:400;border:1px solid #2e6da4}.btn-primary[disabled]{cursor:not-allowed;opacity:0.65}.col-xs-12{width:100%}.col-xs-6{width:50%;position:relative;float:left}.visible-xs,.visible-xs-block{display:none}.col-xs-4{width:33.33333333%;margin:0}.row{display:flex;margin-bottom:1em}@media (min-width: 1200px){.container{width:1170px}.col-lg-3{width:25%}.col-lg-offset-6{margin-left:50%}}@media (min-width: 992px){.container{width:970px}.col-md-4{width:33.33333333%}.col-md-offset-4{margin-left:33.33333333%}}</style>'; const button = document.createElement("a"); button.className = "button-icon-large"; const buttonStyle = getComputedStyle(document.querySelector("a.button-icon-large")); const progressBar = document.createElement("div"); progressBar.style.position = "absolute"; progressBar.style.top = `calc(${buttonStyle.height} - ${buttonStyle.borderBottomWidth})`; progressBar.style.right = "0"; progressBar.style.height = buttonStyle.borderBottomWidth; progressBar.style.background = getComputedStyle(document.querySelector("a.btn-primary")).backgroundColor; progressBar.style.width = "0"; progressBar.className = "RRScraperProgressBar"; button.appendChild(progressBar); const i = document.createElement("i"); i.className = "fa fa-download"; button.appendChild(i); const span = document.createElement("span"); span.innerText = "Download All Chapters"; span.className = "center"; button.appendChild(span); const defaultButtonRows = document.querySelectorAll("div.row.reduced-gutter"); defaultButtonRows.forEach((defaultButtonRow) => { const buttonClone = button.cloneNode(true); buttonClone.onclick = () => { download(); }; defaultButtonRow.insertAdjacentElement("afterend", buttonClone); }); async function download() { document.querySelectorAll("div.RRScraperProgressBar").forEach(element => { element.style.width = "100%"; }); const parser = new DOMParser(); const zip = new JSZip(); const urlSplit = window.location.href.split("/"); const fictionName = urlSplit[urlSplit.length - 1]; var newHtml = await fetch(window.location.href, {credentials: 'omit'}) .then(response => response.text()) .then(text => parser.parseFromString(text, "text/html")); const chapterUrls = [...newHtml.querySelectorAll("tr.chapter-row")].map(element => { return element.getAttribute("data-url"); }); const chapterCount = chapterUrls.length; const chapterCountLength = chapterCount.toString().length; const fillZeros = "0".repeat(chapterCountLength); // timeoutLoop for the progress bar to work let index = 0; async function timeoutLoop() { let chapterUrl = chapterUrls[index]; newHtml = await fetch("https://www.royalroad.com" + chapterUrl, {credentials: 'omit'}) .then(response => response.text()) .then(text => parser.parseFromString(text, "text/html")); let chapterHeader = newHtml.querySelector("div.fic-header > div > div.col-lg-6"); chapterHeader.querySelectorAll("a").forEach(element => { element.setAttribute("href", `https://www.royalroad.com${element.getAttribute("href")}`); }); let chapter = customStyle + '\n<div class="container">' + chapterHeader.outerHTML; chapter += '\n<div class="portlet">'; [...newHtml.querySelector("div.chapter-content").parentNode.children].forEach(element => { if (element.classList.contains("chapter-content") || element.classList.contains("author-note-portlet")) { chapter += "\n" + element.outerHTML; } else if (element.classList.contains("nav-buttons") || element.classList.contains("margin-bottom-10")) { element.querySelectorAll('a').forEach(element2 => { if (element2.innerText.includes("Index")) { element2.setAttribute("href", "."); return; } let adjFilledIndex = ""; if (element2.innerText.includes("Previous")) { adjFilledIndex = (fillZeros + index).slice(chapterCountLength * -1); } else if (element2.innerText.includes("Next")) { adjFilledIndex = (fillZeros + (index + 2)).slice(chapterCountLength * -1); } let adjChapterUrlSplit = element2.getAttribute("href").split("/"); let adjChapterName = adjChapterUrlSplit[adjChapterUrlSplit.length - 1]; element2.setAttribute("href", `${adjFilledIndex}_${adjChapterName}.html`); }) chapter += "\n" + element.outerHTML; } }) chapter += '\n</div></div>'; let chapterUrlSplit = chapterUrl.split("/"); let chapterName = chapterUrlSplit[chapterUrlSplit.length - 1]; let filledIndex = (fillZeros + (index + 1)).slice(chapterCountLength * -1); zip.file(`${fictionName}/${filledIndex}_${chapterName}.html`, chapter); document.querySelectorAll("div.RRScraperProgressBar").forEach(element => { element.style.width = `${((chapterCount - index - 1) / chapterCount) * 100}%`; }); if (++index < chapterCount) { setTimeout(timeoutLoop, 0); } else { zip.generateAsync({ type: "blob", compression: "DEFLATE", compressionOptions: { level: 9 } }).then((blob) => { saveAs(blob, fictionName + ".zip"); }); } } setTimeout(timeoutLoop, 0); }
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址