您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
带图形化界面的小说下载器,自动把当前页最多链接列表作为目录页,下载全部链接正文,简单直观。
当前为
// ==UserScript== // @name 随手小说下载 // @namespace ythong // @version 0.4.0.7 // @description 带图形化界面的小说下载器,自动把当前页最多链接列表作为目录页,下载全部链接正文,简单直观。 // @author ythong // @match http://*/* // @match https://*/* // @require https://greasyfork.org/scripts/450829-numberdigit/code/numberDigit.js?version=1090310 // @require https://greasyfork.org/scripts/450948-reader/code/Reader.js?version=1091837 // @require https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.32.0/codemirror.min.js // @require https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.32.0/mode/javascript/javascript.js // @resource CodeMirrorminCss https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.32.0/codemirror.min.css // @grant GM_registerMenuCommand // @grant GM_xmlhttpRequest // @grant GM_addStyle // @grant GM_listValues // @grant GM_setValue // @grant GM_getValue // @grant GM_deleteValue // @grant GM_getResourceText // @license MIT License // ==/UserScript== (function () { 'use strict'; var nextPageReg = /下一?[页頁张張章]/i; var nextListReg = /下一?[页頁]/i; var setting = {}, tempSetting = {}, customListFunc, customItemFunc, customTextFunc; var cm; //setting输入对象 var CodeMirrorSize = [293, 121]; const SETTING_KEYS = ["listSelector", "textSelector", "jammerSelector", "customItemFunc", "customTextFunc"]; var chapters = []; var chaptersIndex = []; var root; var MaxThread = 10; // 同时下载数量 var maxNextCount = 100, //最大下一页数量,总不可能无限吧 nextCountLimit = 10, //下一页限制,超过会提示 continueDownload = false; //超过下一页限制,是否继续直到到达下一页限制 var downloadedCount = 0, downloadedErr = 0, downloadedNo = 0, downloadedExceedNext = 0; var downloadIndex; var reader; //#region List function getElementThisSelector(ele) { var tag = ele.tagName.toLocaleLowerCase(); // if (ele.id) { // return '#' + getRightId(ele.id); // } else { var className = (typeof ele.className == 'string') ? ele.className.trim() : ""; return tag + (className ? '.' + className.replace(/\s+/g, ".") : ""); // } } function getAncestorWithMostSimilarDescendant(ele, tag) { var selector = tag; var maxEle, maxSelector, count = 0; while (true) { ele = ele.parentElement; if (!ele || ele.tagName == 'HTML') break; var aa = ele.querySelectorAll(':scope>' + selector); if (aa.length > count) { count = aa.length; maxEle = ele; maxSelector = selector; } selector = getElementThisSelector(ele) + ">" + selector; } return [maxEle, maxSelector, count]; } function getAncestorWithMostSimilarTag(doc,tag) { var eTags = [].slice.apply(doc.querySelectorAll(tag)); var mostAncestor, mostSelector, mostCount = 0; var descendants; while (eTags.length > 0) { var ele = eTags.shift(); var [ancestor, selector, count] = getAncestorWithMostSimilarDescendant(ele, tag); descendants = [].slice.apply(ancestor.querySelectorAll(':scope>' + selector)); eTags = eTags.filter(item => descendants.indexOf(item) === -1); if (count > mostCount) { [mostAncestor, mostSelector, mostCount] = [ancestor, selector, count]; } } return [mostAncestor, mostSelector, mostCount]; } function getNextList(doc) { let eles = doc.querySelectorAll("a"), nextPage = null; for (let ele of eles) { if (nextListReg.test(ele.innerText) && ele.href.indexOf("javascript") == -1) return ele; } eles = doc.querySelectorAll("span") for (let ele of eles) { if (nextListReg.test(ele.innerText)) return ele; } } function getRestList(doc){ function showComplate(){ alert("获取剩余目录结束"); } function waitGetList(doc){ var count=0; setTimeout(function(){ if(count>10) return showComplate() count+=1; var addCount=getList(doc); if(addCount){ getRestList(doc); } setTimeout(arguments.callee, 200); },200); } if(!doc)return showComplate(); var nextList=getNextList(doc); if(!nextList)return showComplate(); if(nextList.href && nextList.href.indexOf("javascript") == -1){ download(nextList.href,0,waitGetList) } else { nextList.click(); waitGetList(doc); } } function getList(doc) { var preChapterNo = 0; var preHrefNo = 0; function getChapterNo(s) { var found = s.match(/\d+|[零一壹二贰两三叁四肆五伍六陆七柒八捌九玖十拾百佰千仟万亿]+/g); if (found) { var n = parseInt(found[0]); if (isNaN(n)) { n = numberDigit(found[0]); if (n != -1) return n; } else return n; } return preChapterNo; } function getHrefNo(s) { var found = s.match(/\d+/g); return found ? parseInt(found.pop()) : preHrefNo; } function addToChapters(elements) { var count=0; for (var e of elements) { if (customItemFunc) customItemFunc(doc, e); if (!e.href || e.href.indexOf("javascript") != -1) continue; //if(isHide(window,e))continue; if (getIndexOfObjectArray(chapters, "href", e.href) != -1) continue; count+=1; var length = chapters.push({ href: e.href, title: e.text, chapterNo: getChapterNo(e.text), hrefNo: getHrefNo(e.href), text: "", //正文 nextCount: 0, //本章的下一页数量 }); preChapterNo = chapters[length - 1].chapterNo; preHrefNo = chapters[length - 1].hrefNo; chaptersIndex.push(length - 1); } return count } setting = getSetting(); if (typeof setting != 'object') return; if (!setting.listSelector) { var [mostAncestor, mostSelector, mostCount] = getAncestorWithMostSimilarTag(doc,"a:not([href*='javascript'])"); var eListSelector = getElementSelector(mostAncestor, doc) + '>' + mostSelector; setting.listSelector = eListSelector; displaySetting(); } var count=0; if (customListFunc) { var customResult = customListFunc(doc, setting.listSelector); if (customResult[0] instanceof Document) { [doc, setting.listSelector] = customResult; } else { count=addToChapters(customResult); if(count)createTr(); return } } var listSelector=(setting.listSelector||'').trim().replace(/\n/,','); count=addToChapters(doc.querySelectorAll(listSelector)); if(count)createTr(); return count; } function createTr() { var tableBody = root.querySelector("table tbody"); tableBody.innerHTML = ""; for (var i of chaptersIndex) { var tr = document.createElement("tr"); chapters[i].tr = tr var td = document.createElement("td"); td.className = "serial"; tr.appendChild(td); td = document.createElement("td"); var a = document.createElement("a"); a.text = chapters[i].title; a.href = chapters[i].href; a.target="_blank"; td.title = `章节号${chapters[i].chapterNo},网址号${chapters[i].hrefNo}`; td.appendChild(a); tr.appendChild(td); td = document.createElement("td"); var button = document.createElement("button"); button.className = 'getText'; button.textContent = '正'; button.addEventListener("click", getText); td.appendChild(button); button = document.createElement("button"); button.className = 'deleteRow'; button.textContent = '─'; button.addEventListener("click", deleteRow); td.appendChild(button); var span = document.createElement("span"); td.appendChild(span); tr.appendChild(td); tableBody.appendChild(tr); showState(i); } } //getList function sortList(value) { var length = chapters.length; if (!chaptersIndex) { chaptersIndex = new Array(length); for (let i = 0; i < length; i++) { chaptersIndex[i] = i; } } switch (value) { case "原始升序": for (let i = 0; i < length; i++) { chaptersIndex[i] = i; } break; case "原始降序": for (let i = 0; i < length; i++) { chaptersIndex[i] = length - i - 1; } break; case "章节号升序": chaptersIndex.sort((a, b) => chapters[a].chapterNo - chapters[b].chapterNo); break; case "章节号降序": chaptersIndex.sort((a, b) => chapters[b].chapterNo - chapters[a].chapterNo); break; case "网址升序": chaptersIndex.sort((a, b) => chapters[a].hrefNo - chapters[b].hrefNo); break; case "网址降序": chaptersIndex.sort((a, b) => chapters[b].hrefNo - chapters[a].hrefNo); break; } createTr(); } //#endregion List //#region helper function getElementSelector(element, doc) { function getRightId(id) { var firstCode = id.charCodeAt(0); if (firstCode >= 48 && firstCode <= 57) return "\\3" + id[0] + " " + id.substr(1, id.length); else return id; } let domPath = []; var e = element; while (e.nodeName.toLowerCase() !== "html") { var tag = e.tagName.toLocaleLowerCase(); if (e.id) { domPath.unshift('#' + getRightId(e.id)); break; } else if (tag == "body") { domPath.unshift(tag); } else { var index = 0; var isOneTag = true; var isOneClass = e.classList.length > 0; for (var i = 0; i < e.parentNode.childElementCount; i++) { if (e.parentNode.children[i] == e) { index = i; } else if (e.parentNode.children[i].tagName == e.tagName) { isOneTag = false; if (e.classList.length > 0 && e.parentNode.children[i].classList.toString() == e.classList.toString()) { isOneClass = false; } } } if (isOneTag) { domPath.unshift(tag); } else if (isOneClass) { var className = e.className.trim(); domPath.unshift(tag + (className ? '.' + className.replace(/\s+/g, ".") : "")); } else { domPath.unshift(tag + ':nth-child(' + (index + 1) + ')'); } } var selector = domPath.toString().replace(/,/g, '>'); var eles = doc.querySelectorAll(selector); if (eles.length == 1 && eles[0] == element) break; e = e.parentNode; } return domPath.toString().replace(/,/g, '>'); } //getElementSelector //#endregion helper //#region Text function getContentElement(doc) { //参考“怠惰小说下载器” function getEffectiveText(text) { return text.replace(/\s+/g, ''); } var largestContent, contents = doc.querySelectorAll("span,div,article,section,p,td"), largestNum = 0; for (let content of contents) { var curNum = 0; for (let item of content.childNodes) { if (item.nodeType == 3) { if (!/^\s*$/.test(item.data)) curNum += getEffectiveText(item.data).length; } else if (/^(I|A|STRONG|B|FONT|P|DL|DD|H\d)$/.test(item.tagName)) { //有这些子节点 curNum += getEffectiveText(item.innerText).length; } } if (curNum > largestNum) { largestNum = curNum; largestContent = content } } //console.log(largestContent, largestNum, getEffectiveText(largestContent.innerText)); return largestContent; } function getElementText(element) { //参考“怠惰小说下载器” let result = ""; for (let childNode of element.childNodes) { if (childNode.innerHTML) { childNode.innerHTML = childNode.innerHTML.replace(/<\s*br\s*>/gi, "\r\n").replace(/\n+/gi, "\n").replace(/\r+/gi, "\r"); } if (childNode.nodeType == 1 && !/^(I|A|STRONG|B|FONT)$/.test(childNode.tagName)) result += "\r\n"; if (childNode.textContent) { var text = childNode.textContent; text = text.replace(/[\u00A0\u2002\u2003\u2005\u200C\u200D]/g, ''); result += text.trim().replace(/ +/g, " ").replace(/([^\r]|^)\n([^\r]|$)/gi, "$1\r\n$2"); } } return result; } function download(href, index, callback) { if (typeof index == 'undefined') return; if (index < 0 || index >= chapters.length) return; if (href == null) { href = chapters[index].href; chapters[index].text = ''; chapters[index].state = ''; chapters[index].nextCount = 0; } let requestBody = { method: 'GET', url: href, headers: { referer: href, "Content-Type": "text/html;charset=" + document.charset, }, timeout: 15000, overrideMimeType: "text/html;charset=" + document.charset, onload: function (result) { var doc = getDoc(result.responseText); doc.href = href; //记下当前页面的网址,自己生成的没有网址。 deleteSomeTag(doc, 'script'); deleteSomeTag(doc, 'style'); deleteElementBySelector(doc, '*[style*="display:none"]'); deleteElementBySelector(doc, '#float_favorite'); deleteHideElement(doc); callback(doc, index); }, onerror: function () { console.warn("error:", href); callback(null, index); }, ontimeout: function () { console.warn("timeout: ", href); callback(null, index); } }; GM_xmlhttpRequest(requestBody); } //getDocByHref function deleteHideElement(doc) { if (!doc.defaultView) return; //直接下载网页的没有doc.defaultView var elements = doc.querySelectorAll("span,div,ul,li") //var elements = doc.querySelectorAll("li") for (var i = elements.length - 1; i >= 0; i--) { var ele = elements[i]; var thisStyle = doc.defaultView.getComputedStyle(ele); if (thisStyle && (thisStyle.display == "none" || (ele.tagName == "SPAN" && thisStyle.fontSize == "0px"))) ele.remove(); } } function deleteElementBySelector(doc, selector) { var elements = doc.querySelectorAll(selector); for (var i = elements.length - 1; i >= 0; i--) { elements[i].remove(); } } function deleteSomeTag(doc, tag) { var elements = doc.getElementsByTagName(tag); for (var i = elements.length - 1; i >= 0; i--) { elements[i].remove(); } } function getDoc(str) { var doc = null; try { doc = document.implementation.createHTMLDocument(''); doc.documentElement.innerHTML = str; } catch (e) { console.log('parse error'); } return doc; } //getDoc function getNextPage(doc) { let eles = doc.querySelectorAll("a"); for (let ele of eles) { if (nextPageReg.test(ele.innerText) && ele.href.indexOf("javascript") == -1) return ele; } } //获得正文,如果有下一页网址不在章节网址,继续获取,并返回Next function getTextFromDoc(doc, index, callback) { function addTexttoChapter(text) { chapters[index].text += ((doc.href == chapters[index].href) ? "" : `>>${doc.title}\n`) + text + '\n'; } if (doc) { var nextPage = getNextPage(doc); var nextPagehref = nextPage ? nextPage.href : ""; var jammerSelector=(setting.jammerSelector||'').trim().replace(/\n/,','); if (jammerSelector) deleteElementBySelector(doc, jammerSelector); var content; if (!setting.textSelector) { content = getContentElement(doc); var sSelector = getElementSelector(content, doc); setting.textSelector = sSelector; displaySetting(); } if (customTextFunc) { var customResult = customTextFunc(doc, setting.textSelector); if (Array.isArray(customResult)) { [doc, setting.textSelector] = customResult; } else if (typeof customResult == 'string') { addTexttoChapter(customResult); return 'OK'; } else { return 'No' } } for (var selector of setting.textSelector.trim().split('\n')) { var eText = doc.querySelector(selector); if (!eText) { return 'No'; } addTexttoChapter(getElementText(eText)); } if (nextPagehref) { var href2 = nextPagehref.slice(0, 6) == 'https:' ? 'http:' + nextPagehref.slice(6) : 'https' + nextPagehref.slice(5); if (nextPagehref == document.location.href || href2 == document.location.href) return 'OK'; // 如果a元素的href为空,返回的是目录页的地址 if (nextPagehref == doc.href || href2 == doc.href) return 'OK'; if (getIndexOfObjectArray(chapters, "href", nextPagehref) == -1 && getIndexOfObjectArray(chapters, "href", href2) == -1) { if (chapters[index].nextCount > nextCountLimit - 1) { //第一个下一页为0 if (continueDownload || confirm(`目录“${chapters[index].title}”的下一页数量超过最大值${nextCountLimit},你要让以后的下一页继续吗?\n继续可能会下载太多链接,请谨慎继续!`)) { continueDownload = true; } else { return '>N'; } } if (chapters[index].nextCount > maxNextCount - 1) return '>>N' download(nextPagehref, index, callback); return 'Next'; } else return 'OK'; } else return 'OK'; } else { return 'Er'; } } //getTextFromDoc function showChapterText(index) { function showOrDownload(index) { if (isDownloaded(index)) { showChapterText(index) } else { download(null, index, getTextCallback) } } reader.setReader(chapters[index].text, chapters[index].title, (index - 1 >= 0 && index - 1 < chapters.length) ? '<' : '', () => { showOrDownload(index - 1) }, (index + 1 >= 0 && index + 1 < chapters.length) ? '>' : '', () => { showOrDownload(index + 1) }); } function showState(index) { var span = chapters[index].tr.querySelector("td>span"); span.textContent = (chapters[index].state || '') + ((chapters[index].state != 'OK' && chapters[index].text) ? "+" : "") + (chapters[index].nextCount || ''); } function getTextCallback(doc, index) { var state = getTextFromDoc(doc, index, getTextCallback); switch (state) { case 'No': if (confirm(`${chapters[index].text ? "后续页" : ""}找不到正文选择器指定的元素,清空正文选择器重新获取。`)) { delete setting.textSelector; displaySetting(); } break; case 'Er': alert(`${chapters[index].href}下载出错`); break; case 'Next': chapters[index].nextCount += 1; return; } chapters[index].state = state; showState(index); if (chapters[index].text) showChapterText(index); } var getText = e => { setting = getSetting(); if (typeof setting != 'object') return; var tr = e.target.parentElement.parentElement; var index = getIndexOfObjectArray(chapters, "tr", tr); if (isDownloaded(index)) showChapterText(index); else download(null, index, getTextCallback); } function saveAllText() { var allText = '', a; for (var i of chaptersIndex) { allText += '\n##' + chapters[i].title + '\n' + chapters[i].text; } var blob = new Blob(['#' + document.title + '\n', document.location.href + '\n', allText], { type: "text/plain;charset=utf-8", endings: "native" }); var filename = document.title.replace(/[/\\?%*:|"<>.]/g, '-') + '.txt'; downloadFile(blob, filename); } function getAllTextCallback(doc, index) { var state = getTextFromDoc(doc, index, getAllTextCallback); switch (state) { case 'No': downloadedNo += 1; break; case 'Er': downloadedErr += 1; break; case '>N': case '>>N': downloadedExceedNext += 1; break; case 'Next': chapters[index].nextCount += 1; return; } chapters[index].state = state; showState(index); download(null, chaptersIndex[downloadIndex], getAllTextCallback); downloadIndex = getNextUndownloadIndex(downloadIndex + 1); downloadedCount += 1; root.querySelector('#downloadNumbers').textContent = `${chapters.length}/${downloadedCount}/${downloadedNo}/${downloadedErr}/${downloadedExceedNext}`; if (downloadedCount >= chapters.length) { if (downloadedNo == 0 && downloadedErr == 0) { saveAllText(); } else { if (confirm(`${downloadedNo}个找不到正文元素,${downloadedErr}个下载失败。\n是否保存已下载的文本。`)) { saveAllText(); } } } } //getAllTextCallback function isDownloaded(index) { var state = chapters[index].state; return state == 'OK' } function getNextUndownloadIndex(index) { while (index < chaptersIndex.length && isDownloaded(chaptersIndex[index])) { downloadedCount += 1; index += 1; } return index; } function getAllText() { if (chapters.length < 1) alert("没有目录,请先获取目录再下载全部文本。"); downloadedCount = 0; //已下载数量 downloadedErr = 0; //下载失败数量 downloadedNo = 0; //下载章节找不到选择器对应元素的数量 downloadIndex = 0; //当前待下载序号 downloadIndex = getNextUndownloadIndex(downloadIndex); if (downloadIndex >= chapters.length) saveAllText(); //已经获取全部文本 else { for (var i = 0; i < MaxThread; i++) { download(null, chaptersIndex[downloadIndex], getAllTextCallback); downloadIndex = getNextUndownloadIndex(downloadIndex + 1); } } } var deleteRow = e => { var tr = e.target.parentElement.parentElement; var index = getIndexOfObjectArray(chapters, "tr", tr); tr.remove(); chapters.splice(index, 1); chaptersIndex.splice(chaptersIndex.indexOf(index), 1); for (let i = 0; i < chaptersIndex.length; i++) { if (chaptersIndex[i] > index) chaptersIndex[i] -= 1; } }; function getIndexOfObjectArray(objectArray, key, value) { for (let i = 0; i < objectArray.length; i++) { if (objectArray[i][key] == value) return i; } return -1; } function displaySetting() { // eSetting.value=toTplString(JSON.stringify(setting, null, 2)); cm.setValue(settingToString(setting)); setSaveSiteSettingClass(); } function getSetting(isSetCunstomFun = true) { var result = {}, key = '', value = ''; var lines = cm.getValue().split('\n'); for (let line of lines) { if (!line.trim()) continue; if (line.slice(0, 2) == '$$') { if (key && value) result[key] = value.trim(); key = line.slice(2); value = ''; if (SETTING_KEYS.indexOf(key) == -1) { return alert(`键值${key}不合法`); } } else { value += (value ? '\n' : '') + line; } } if (key && value) result[key] = value.trim(); if (isSetCunstomFun) { customListFunc = setting.customListFunc ? Function("doc", "selector", setting.customListFunc + ";return [doc,selector];") : null; customItemFunc = setting.customItemFunc ? Function("doc", "item", setting.customItemFunc + ";return [doc,item];") : null; customTextFunc = setting.customTextFunc ? Function("doc", "selector", setting.customTextFunc + ";return [doc,selector];") : null; } return result; } function settingToString(setting) { var result = ''; for (var key in setting) { result += '$$' + key + '\n' result += setting[key] + '\n'; } return result; } function saveSiteSetting() { setting = getSetting(); if (typeof setting != 'object') return; if (isSameObject(setting, {})) { GM_deleteValue(location.host); alert("已删除该网站配置"); } else { GM_setValue(location.host, setting); alert("已保存该网站配置"); } setSaveSiteSettingClass(); } function isSameObject(object1, object2) { if (!object1 || !object2) return false; var ss1 = Object.entries(object1).toString(); var ss2 = Object.entries(object2).toString(); return ss1 === ss2; } function setRestListClass(){ var nextList=getNextList(document); root.querySelector("#getRestList").disabled=!nextList; } function setSaveSiteSettingClass() { tempSetting = getSetting(false); if (typeof tempSetting == 'object') { var gmSetting = GM_getValue(location.host); root.querySelector("#saveSiteSetting").className = isSameObject(tempSetting, gmSetting) ? "disabled" : "" } setRestListClass(); //设置剩余目录按钮可用性 } function enableCodeMirrow() { var CodeMirrorminCss = GM_getResourceText("CodeMirrorminCss") var style = document.createElement('style'); style.innerHTML = CodeMirrorminCss; root.appendChild(style); var ele = root.getElementById("setting"); cm = CodeMirror.fromTextArea(ele, { matchBrackets: true, mode: "javascript", }); cm.setSize(CodeMirrorSize[0], CodeMirrorSize[1]); cm.on('blur', function () { setSaveSiteSettingClass(); }); document.ytheditor = cm; window.ytheditor = cm; ytheditor = cm SETTING_KEYS.forEach(words => { //CodeMirror.resolveMode("javascript").keywords[words] = true; }); } function downloadFile(blob, fileName) { var link = document.createElement('a'); link.href = window.URL.createObjectURL(blob); link.download = fileName; link.click(); link.remove(); window.URL.revokeObjectURL(link.href); } function addSiteSettings(siteSettings) { for (var name in siteSettings) { var setting = siteSettings[name]; var tmpSetting = {}; for (var key of SETTING_KEYS) { if (setting.hasOwnProperty(key)) tmpSetting[key] = setting[key]; } if (!isSameObject(tmpSetting, {})) GM_setValue(name, tmpSetting); } } function addDiv() { GM_addStyle(` #ythList{ position:fixed; right:0px; z-index: 99999999999; background-color: #ccc; top: 0; } `); var shadowCss = ` <style> textarea{ white-space: nowrap; width: 288px; height: 121px; font-size: 12px; } button:hover{ background-color: rgb(93 187 93); } .titleList{ overflow-y:scroll; overflow-x:hidden; padding-right: 2px; flex-grow: 1; margin-bottom: 10px; } button.main{ margin: 5px 0px 0px; } .capsule{ width: fit-content; border-radius: 10px; color: #000; padding: 0px 7px 2px 7px; border-width: 1px; border-style: solid; } #remainOK{ transform: scale(0.8); } table th { text-align: center; position: sticky; top: 0; background-color: #aaa; box-shadow: 0 -1px #000000; } table th, td { border: 1px solid black; word-wrap: break-word; } table td:nth-child(3){ vertical-align:text-top; } table { border-collapse: collapse; border-spacing: 0; table-layout: fixed; width:30.5em; font-size: inherit; } table button { font-size: 10px; border-radius: 50%; width: 16px; height: 16px; border-style: solid; color: #444; background-color: #f7f7f7; padding: 1px 0px; border-width: 1px; } table button.getText { } table button.deleteRow { } table th:nth-child(2),td:nth-child(2) { width:20em; } table th:nth-child(1),td:nth-child(1) { width:3em; } table tbody{ counter-reset:sectioncounter; } table td.serial:before{ content:counter(sectioncounter); counter-increment:sectioncounter; } #downloadNumbers{ float:right; } svg { height: 18px; } svg path{ fill:#404040; } #saveSiteSetting.disabled svg path{ fill:#cccccc; } #saveSiteSetting.disabled{ cursor: none; pointer-events:none; background-color: revert; } button:disabled{ background-color: revert; } select,select option{ float: right; transform: scale(0.8); } div.CodeMirror{ resize: both; float: right; min-width: 288px; min-height: 60px; } #SiteSettingButtons{ float: left; padding: 0px 4px 0px 0px; display: flex; flex-direction: column; } #SiteSettingButtons button{ width: 20px; height: 24px; border-radius: 14%; padding: 2px 0px 2px 0px; border: 1px solid #333; /* background-color: #d2d2d2;*/ } #container{ display: flex; flex-flow: column; align-content: space-between; padding: 5px; font-size: 10px; font-family: -apple-system,BlinkMacSystemFont,Helvetica Neue,PingFang SC,Microsoft YaHei,Source Han Sans SC,Noto Sans CJK SC,WenQuanYi Micro Hei,sans-serif; line-height: normal; text-align: left; } </style>`; var div = document.createElement("div"); div.id = "ythList"; root = div.attachShadow({ mode: 'open' }); var html = shadowCss; var importSvg = '<svg t="1662536357307" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2569"><path d="M667.733333 864H170.666667c-6.4 0-10.666667-4.266667-10.666667-10.666667V170.666667c0-6.4 4.266667-10.666667 10.666667-10.666667h309.333333V320c0 40.533333 34.133333 74.666667 74.666667 74.666667h160v38.4c0 17.066667 14.933333 32 32 32s32-14.933333 32-32V298.666667c0-8.533333-4.266667-17.066667-8.533334-23.466667l-170.666666-170.666667c-6.4-6.4-14.933333-8.533333-23.466667-8.533333H170.666667C130.133333 96 96 130.133333 96 170.666667v682.666666c0 40.533333 34.133333 74.666667 74.666667 74.666667h497.066666c17.066667 0 32-14.933333 32-32s-14.933333-32-32-32z m46.933334-550.4v17.066667H554.666667c-6.4 0-10.666667-4.266667-10.666667-10.666667V160h19.2l151.466667 153.6z" p-id="2570"></path><path d="M853.333333 597.333333H599.466667l51.2-51.2c12.8-12.8 12.8-32 0-44.8-12.8-12.8-32-12.8-44.8 0l-106.666667 106.666667c-12.8 12.8-12.8 32 0 44.8l106.666667 106.666667c6.4 6.4 14.933333 8.533333 23.466666 8.533333s17.066667-2.133333 23.466667-8.533333c12.8-12.8 12.8-32 0-44.8L599.466667 661.333333H853.333333c17.066667 0 32-14.933333 32-32S870.4 597.333333 853.333333 597.333333z" p-id="2571"></path></svg>'; var exportSvg = '<svg t="1662536484558" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3489"><path d="M582.4 864H170.666667c-6.4 0-10.666667-4.266667-10.666667-10.666667V170.666667c0-6.4 4.266667-10.666667 10.666667-10.666667h309.333333V320c0 40.533333 34.133333 74.666667 74.666667 74.666667h160v38.4c0 17.066667 14.933333 32 32 32s32-14.933333 32-32V298.666667c0-8.533333-4.266667-17.066667-8.533334-23.466667l-170.666666-170.666667c-6.4-6.4-14.933333-8.533333-23.466667-8.533333H170.666667C130.133333 96 96 130.133333 96 170.666667v682.666666c0 40.533333 34.133333 74.666667 74.666667 74.666667h411.733333c17.066667 0 32-14.933333 32-32s-14.933333-32-32-32z m132.266667-550.4v17.066667H554.666667c-6.4 0-10.666667-4.266667-10.666667-10.666667V160h19.2l151.466667 153.6z" p-id="3490"></path><path d="M866.133333 669.866667l-106.666666-106.666667c-12.8-12.8-32-12.8-44.8 0s-12.8 32 0 44.8l51.2 51.2H512c-17.066667 0-32 14.933333-32 32S494.933333 725.333333 512 725.333333h253.866667l-51.2 51.2c-12.8 12.8-12.8 32 0 44.8 6.4 6.4 14.933333 8.533333 23.466666 8.533334s17.066667-2.133333 23.466667-8.533334l106.666667-106.666666c8.533333-10.666667 8.533333-32-2.133334-44.8z" p-id="3491"></path></svg>'; var saveSvg = '<svg t="1662536828344" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2606"><path d="M906.666667 298.666667L725.333333 117.333333c-14.933333-14.933333-32-21.333333-53.333333-21.333333H170.666667C130.133333 96 96 130.133333 96 170.666667v682.666666c0 40.533333 34.133333 74.666667 74.666667 74.666667h682.666666c40.533333 0 74.666667-34.133333 74.666667-74.666667V349.866667c0-19.2-8.533333-38.4-21.333333-51.2zM652.8 864H371.2V648.533333h281.6v215.466667z m211.2-10.666667c0 6.4-4.266667 10.666667-10.666667 10.666667h-140.8V618.666667c0-17.066667-12.8-29.866667-29.866666-29.866667H341.333333c-17.066667 0-29.866667 12.8-29.866666 29.866667v245.333333H170.666667c-6.4 0-10.666667-4.266667-10.666667-10.666667V170.666667c0-6.4 4.266667-10.666667 10.666667-10.666667h140.8V320c0 17.066667 12.8 29.866667 29.866666 29.866667h277.333334c17.066667 0 29.866667-12.8 29.866666-29.866667s-12.8-29.866667-29.866666-29.866667H371.2V160h302.933333c2.133333 0 6.4 2.133333 8.533334 2.133333l179.2 179.2c2.133333 2.133333 2.133333 4.266667 2.133333 8.533334V853.333333z" p-id="2607"></path></svg>'; var clearAllSvg = '<svg t="1662608639253" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1287"><path d="M672 256c16.953 0 32.987 6.696 45.145 18.855C729.304 287.013 736 303.047 736 320v512c0 16.953-6.696 32.987-18.855 45.145S688.953 896 672 896H224c-16.954 0-32.986-6.696-45.145-18.855S160 848.953 160 832V320c0-16.953 6.696-32.987 18.855-45.145C191.014 262.696 207.046 256 224 256h448m0-64H224c-70.4 0-128 57.6-128 128v512c0 70.4 57.6 128 128 128h448c70.4 0 128-57.6 128-128V320c0-70.4-57.6-128-128-128z" p-id="1288"></path><path d="M800 64H352v64h448c35.2 0 64 28.8 64 64v576h64V192c0-70.4-57.6-128-128-128z" p-id="1289"></path><path d="M598.765 425.236c-12.445-12.445-32.81-12.445-45.255 0L448 530.745l-105.51-105.51c-12.445-12.445-32.81-12.445-45.255 0s-12.445 32.81 0 45.255L402.745 576l-105.51 105.51c-12.445 12.445-12.445 32.81 0 45.255s32.81 12.445 45.255 0L448 621.255l105.51 105.51c12.445 12.445 32.81 12.445 45.255 0s12.445-32.81 0-45.255L493.255 576l105.51-105.51c12.445-12.445 12.445-32.809 0-45.254z" p-id="1290"></path></svg>'; var shrinkSvg = '<svg t="1662608958322" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2606"><path d="M313.6 358.4H177.066667c-17.066667 0-32 14.933333-32 32s14.933333 32 32 32h213.333333c4.266667 0 8.533333 0 10.666667-2.133333 8.533333-4.266667 14.933333-8.533333 17.066666-17.066667 2.133333-4.266667 2.133333-8.533333 2.133334-10.666667v-213.333333c0-17.066667-14.933333-32-32-32s-32 14.933333-32 32v136.533333L172.8 125.866667c-12.8-12.8-32-12.8-44.8 0-12.8 12.8-12.8 32 0 44.8l185.6 187.733333zM695.466667 650.666667H832c17.066667 0 32-14.933333 32-32s-14.933333-32-32-32H618.666667c-4.266667 0-8.533333 0-10.666667 2.133333-8.533333 4.266667-14.933333 8.533333-17.066667 17.066667-2.133333 4.266667-2.133333 8.533333-2.133333 10.666666v213.333334c0 17.066667 14.933333 32 32 32s32-14.933333 32-32v-136.533334l200.533333 200.533334c6.4 6.4 14.933333 8.533333 23.466667 8.533333s17.066667-2.133333 23.466667-8.533333c12.8-12.8 12.8-32 0-44.8l-204.8-198.4zM435.2 605.866667c-4.266667-8.533333-8.533333-14.933333-17.066667-17.066667-4.266667-2.133333-8.533333-2.133333-10.666666-2.133333H192c-17.066667 0-32 14.933333-32 32s14.933333 32 32 32h136.533333L128 851.2c-12.8 12.8-12.8 32 0 44.8 6.4 6.4 14.933333 8.533333 23.466667 8.533333s17.066667-2.133333 23.466666-8.533333l200.533334-200.533333V832c0 17.066667 14.933333 32 32 32s32-14.933333 32-32V618.666667c-2.133333-4.266667-2.133333-8.533333-4.266667-12.8zM603.733333 403.2c4.266667 8.533333 8.533333 14.933333 17.066667 17.066667 4.266667 2.133333 8.533333 2.133333 10.666667 2.133333h213.333333c17.066667 0 32-14.933333 32-32s-14.933333-32-32-32h-136.533333L896 170.666667c12.8-12.8 12.8-32 0-44.8-12.8-12.8-32-12.8-44.8 0l-187.733333 187.733333V177.066667c0-17.066667-14.933333-32-32-32s-32 14.933333-32 32v213.333333c2.133333 4.266667 2.133333 8.533333 4.266666 12.8z" p-id="2607"></path></svg>'; var expandSvg = '<svg t="1662609025513" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2745"><path d="M149.333333 394.666667c17.066667 0 32-14.933333 32-32v-136.533334l187.733334 187.733334c6.4 6.4 14.933333 8.533333 23.466666 8.533333s17.066667-2.133333 23.466667-8.533333c12.8-12.8 12.8-32 0-44.8l-187.733333-187.733334H362.666667c17.066667 0 32-14.933333 32-32s-14.933333-32-32-32H149.333333c-4.266667 0-8.533333 0-10.666666 2.133334-8.533333 4.266667-14.933333 10.666667-19.2 17.066666-2.133333 4.266667-2.133333 8.533333-2.133334 12.8v213.333334c0 17.066667 14.933333 32 32 32zM874.666667 629.333333c-17.066667 0-32 14.933333-32 32v136.533334L642.133333 597.333333c-12.8-12.8-32-12.8-44.8 0s-12.8 32 0 44.8l200.533334 200.533334H661.333333c-17.066667 0-32 14.933333-32 32s14.933333 32 32 32h213.333334c4.266667 0 8.533333 0 10.666666-2.133334 8.533333-4.266667 14.933333-8.533333 17.066667-17.066666 2.133333-4.266667 2.133333-8.533333 2.133333-10.666667V661.333333c2.133333-17.066667-12.8-32-29.866666-32zM381.866667 595.2l-200.533334 200.533333V661.333333c0-17.066667-14.933333-32-32-32s-32 14.933333-32 32v213.333334c0 4.266667 0 8.533333 2.133334 10.666666 4.266667 8.533333 8.533333 14.933333 17.066666 17.066667 4.266667 2.133333 8.533333 2.133333 10.666667 2.133333h213.333333c17.066667 0 32-14.933333 32-32s-14.933333-32-32-32h-136.533333l200.533333-200.533333c12.8-12.8 12.8-32 0-44.8s-29.866667-10.666667-42.666666 0zM904.533333 138.666667c0-2.133333 0-2.133333 0 0-4.266667-8.533333-10.666667-14.933333-17.066666-17.066667-4.266667-2.133333-8.533333-2.133333-10.666667-2.133333H661.333333c-17.066667 0-32 14.933333-32 32s14.933333 32 32 32h136.533334l-187.733334 187.733333c-12.8 12.8-12.8 32 0 44.8 6.4 6.4 14.933333 8.533333 23.466667 8.533333s17.066667-2.133333 23.466667-8.533333l187.733333-187.733333V362.666667c0 17.066667 14.933333 32 32 32s32-14.933333 32-32V149.333333c-2.133333-4.266667-2.133333-8.533333-4.266667-10.666666z" p-id="2746"></path></svg>'; html += ` <div id="container"> <div id="settingWarpper"> <div id="SiteSettingButtons"> <button id="saveSiteSetting" class="disabled" title="保存当前网站配置信息">${saveSvg}</button> <button id="resize"></button> <button id="importSiteSetting" title="导入多个网站配置信息">${importSvg}</button> <button id="exportSiteSetting" title="导出现有全部网站配置信息">${exportSvg}</button> <button id="clearAllSiteSetting" title="删除现有全部网站配置信息">${clearAllSvg}</button> </div> <textarea id="setting" placeholder="网站配置项,JSON格式"></textarea> </div> <div id="commandBar"> <button id="getList" class="capsule main" title="获取当前页包含的目录列表">获取目录</button> <button id="getRestList" class="capsule main" title="根据目录页面的下一页链接,连续获取接下去的剩余目录列表">剩余目录</button> <button id="clearList" class="capsule main" title="全部删除已经提取的目录列表">清空目录</button> <button id="getAllText" class="capsule main" title="根据目录顺序下载全文并保存文本">下载全文</button></div> <div id="toggleBar"> <span id="toggle">︿</span> <span id="downloadNumbers" title="章节/下载/无内容数量/错误/下页超标"></span> </div> <div class="titleList"> <table><thead><tr> <th>序号</th> <th class="title">标题 <select id="sort"> <option>原始升序</option> <option>原始降序</option> <option>章节号升序</option> <option>章节号降序</option> <option>网址升序</option> <option>网址降序</option> </select></th> <th><button id="DeleteNoText" class="capsule">删空正文</button></th> </tr></thead><tbody></tbody></table> </div> </div> <input type="file" id="inputFiles" accept=".txt" style="display:none">`; root.innerHTML = html; document.body.appendChild(div); reader = (typeof Reader == 'function') ? new Reader(document, "ReaderAttached") : null; root.querySelector("#sort").addEventListener("change", (e) => { sortList(e.target.value); }); enableCodeMirrow(); setting = GM_getValue(location.host); if (!setting) setting = {}; displaySetting(); var eGetList = root.querySelector("#getList"); eGetList.onclick = () => { getList(document); }; eGetList.click(); root.querySelector("#getRestList").addEventListener("click", () => { getRestList(document); }); // setSaveSiteSettingClass(); var resize = root.querySelector("#resize"); resize.addEventListener("click", (e) => { var ele = resize; if (ele.title == '放大') { ele.title = '缩小'; ele.innerHTML = shrinkSvg; cm.setSize(CodeMirrorSize[0] * 2, CodeMirrorSize[1] * 2); } else { ele.title = '放大'; ele.innerHTML = expandSvg; cm.setSize(CodeMirrorSize[0], CodeMirrorSize[1]); } }); resize.click(); var inputFiles = root.querySelector("#inputFiles"); inputFiles.addEventListener("change", (e) => { var file = e.target.files[0]; if (!file) return; var reader = new FileReader(); reader.readAsText(file); reader.onload = function (e) { var siteSettings = JSON.parse(this.result); addSiteSettings(siteSettings); } }); root.querySelector("#importSiteSetting").addEventListener("click", (e) => { inputFiles.click(); }); root.querySelector("#exportSiteSetting").addEventListener("click", (e) => { var names = GM_listValues(); var siteSettings = {}; for (var name of names) { if (name == 'ReaderStyle') continue; siteSettings[name] = GM_getValue(name); } var str = JSON.stringify(siteSettings, null, 2); var blob = new Blob([str], { type: "text/plain;charset=utf-8", endings: "native" }); downloadFile(blob, "AllSiteSetting.txt") }); root.querySelector("#clearAllSiteSetting").addEventListener("click", (e) => { var names = GM_listValues(); for (var name of names) { if (name == 'ReaderStyle') continue; GM_deleteValue(name); } alert("已删除现有全部网站配置信息"); }); root.querySelector("#saveSiteSetting").addEventListener("click", (e) => { saveSiteSetting(); }); var container = root.querySelector("#container") container.style.height = '100vh'; root.querySelector("#toggle").addEventListener("click", (e) => { if (e.target.textContent == '﹀') { e.target.textContent = '︿' e.target.parentElement.nextElementSibling.style.display = '' container.style.height = '100vh'; } else { e.target.textContent = '﹀' e.target.parentElement.nextElementSibling.style.display = 'none' container.style.height = ''; } }); root.querySelector("#DeleteNoText").onclick = () => { for (let i = chapters.length - 1; i >= 0; i--) { if (!chapters[i].text) { chapters.splice(i, 1); } } chaptersIndex = null; sortList(root.querySelector("#sort").value); }; root.querySelector("#clearList").onclick = () => { root.querySelector("table tbody").innerHTML = ""; chapters = []; chaptersIndex = []; }; root.querySelector("#getAllText").onclick = () => { setting = getSetting(); if (typeof setting == 'object') getAllText(); }; } GM_registerMenuCommand("开始", addDiv); })(); //addDiv