打开刺猬猫章节页面时自动保存文章到本地, 支持##章节。
// ==UserScript== // @name 刺猬猫章节自动下载 // @namespace https://github.com/NateScarlet/Scripts/tree/master/user-script // @description 打开刺猬猫章节页面时自动保存文章到本地, 支持##章节。 // @grant none // @include https://www.ciweimao.com/chapter/* // @run-at document-idle // @version 2023.12.05+fbf1a78c // ==/UserScript== "use strict"; (() => { var __async = (__this, __arguments, generator) => { return new Promise((resolve, reject) => { var fulfilled = (value) => { try { step(generator.next(value)); } catch (e) { reject(e); } }; var rejected = (value) => { try { step(generator.throw(value)); } catch (e) { reject(e); } }; var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected); step((generator = generator.apply(__this, __arguments)).next()); }); }; // src/utils/elementRootText.ts function elementRootText(element) { let ret = ""; for (const i of element.childNodes) { if (i.nodeType === i.TEXT_NODE) { ret += i.nodeValue; } } return ret.trim(); } // src/utils/canvasToMarkdown.ts function canvasToMarkdown(canvas, alt = "", title = "") { return `} "${title}")`; } // src/utils/isCanvasTainted.ts function isCanvasTainted(canvas) { try { canvas.getContext("2d").getImageData(0, 0, 1, 1); return false; } catch (err) { return err instanceof DOMException && err.name === "SecurityError"; } } // src/utils/imageToCanvas.ts function imageToCanvas(_0) { return __async(this, arguments, function* (img, { background } = {}) { const canvas = document.createElement("canvas"); canvas.width = img.naturalWidth; canvas.height = img.naturalHeight; const ctx = canvas.getContext("2d"); if (background) { ctx.fillStyle = background; ctx.fillRect(0, 0, canvas.width, canvas.height); } ctx.drawImage(img, 0, 0); if (img.src && img.crossOrigin !== "anonymous" && isCanvasTainted(canvas)) { const corsImage = new Image(); corsImage.crossOrigin = "anonymous"; corsImage.src = img.src; yield corsImage.decode(); return imageToCanvas(corsImage, { background }); } return canvas; }); } // src/utils/imageToMarkdown.ts function imageToMarkdown(_0) { return __async(this, arguments, function* (img, { background } = {}) { return canvasToMarkdown( yield imageToCanvas(img, { background }), img.alt, img.title ); }); } // src/utils/loadImage.ts function loadImage(url) { return __async(this, null, function* () { const img = new Image(); img.src = url; img.alt = url; yield img.decode(); return img; }); } // src/utils/sleep.ts function sleep(duration) { return __async(this, null, function* () { return new Promise((resolve) => { setTimeout(resolve, duration); }); }); } // src/ciweimao.com/download.user.ts var __name__ = "刺猬猫章节自动下载"; (function() { return __async(this, null, function* () { const chapter = document.querySelector("#J_BookCnt .chapter").firstChild.textContent; let lines = []; let startTime = Date.now(); while (lines.length === 0 && Date.now() - startTime < 6e4) { yield sleep(1e3); for (const i of document.querySelectorAll("#J_BookImage")) { const url = i.style["background-image"].match( /(?:url\(")?(.+)(?:"\))?/ )[1]; const line = yield imageToMarkdown(yield loadImage(url)); lines.push(line); } for (const i of document.querySelectorAll("#J_BookRead p.chapter")) { const line = elementRootText(i); lines.push(line); for (const img of i.querySelectorAll("img")) { yield img.decode(); lines.push(yield imageToMarkdown(img)); } } for (const i of document.querySelectorAll("p.author_say")) { const line = elementRootText(i); lines.push(` ${line}`); for (const img of i.querySelectorAll("img")) { yield img.decode(); lines.push(yield imageToMarkdown(img)); } } lines = lines.filter((i) => i.length > 0); } console.log(`${__name__}: 获取到 ${lines.length} 行`); const file = new Blob([`# ${chapter} `, lines.join("\n\n") + "\n"], { type: "text/markdown" }); const anchor = document.createElement("a"); anchor.href = URL.createObjectURL(file); anchor.download = `${location.pathname.split("/").slice(-1)[0]} ${document.title}.md`; anchor.style["display"] = "none"; document.body.append(anchor); anchor.click(); setTimeout(() => { document.body.removeChild(anchor); URL.revokeObjectURL(anchor.href); }, 0); }); })(); })();