您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Script used to download manga R18 on a domain of nhentai
当前为
// ==UserScript== // @name n210-dl // @namespace https://gist.github.com/hiroshil // @version 0.0.1 // @description Script used to download manga R18 on a domain of nhentai // @license MIT // @author hiroshil // @source https://gist.github.com/hiroshil/86723bc557efa88931cf6b135e42a2b2 // @match http*://nhentai.to/g/* // @icon https://www.google.com/s2/favicons?sz=64&domain=nhentai.to // ==/UserScript== /* jshint esversion: 8 */ //https://stackoverflow.com/questions/5525071/how-to-wait-until-an-element-exists //https://stackoverflow.com/questions/30008114/how-do-i-promisify-native-xhr //https://stackoverflow.com/questions/5582574/how-to-check-if-a-string-contains-text-from-an-array-of-substrings-in-javascript function waitForElm(selector) { return new Promise(resolve => { if (document.querySelectorAll(selector).length) { return resolve(document.querySelectorAll(selector)); } const observer = new MutationObserver(mutations => { if (document.querySelectorAll(selector).length) { resolve(document.querySelectorAll(selector)); observer.disconnect(); } }); observer.observe(document.body, { childList: true, subtree: true }); }); } function waitForXHRRequest(url,method,responseType) { return new Promise( (resolve, reject) => { var res = new XMLHttpRequest(); res.responseType = responseType; res.open(method, url, true); res.send(); res.onload = () => { if (res.status >= 200 && res.status < 400) { resolve(res.response); } else { reject(false); } } }); } function sleeper(ms) { return function(x) { return new Promise(resolve => setTimeout(() => resolve(x), ms)); }; } function parseStr(text) { /* Checks if the string matches the format "number-number" with the second number larger. Args: text: The string to check. Returns: Two numbers if the string matches the format and the second number is larger, False otherwise. */ const pattern = /^\d+-\d+$/; // Matches "number-number" format const match = pattern.exec(text); if (match) { const [num1, num2] = match[0].split("-"); // Split into two numbers if (parseInt(num1) < parseInt(num2)){ // Check if second number is larger return [parseInt(num1), parseInt(num2)] } } return false; } async function downloadChapter(id=null,filename=null, callback=(e)=>{}, ret = 3, skipCr = true){ console.log(id); const dlBtn = document.querySelector("#dlzip"); if (!(dlBtn.classList.contains("btn-disabled"))) { dlBtn.classList.add("btn-disabled"); var s_p = 1; var e_p = gallery.num_pages; let pt = prompt("Please enter the number of pages you want to download (Pattern: first page-last page. Example: 15-30)", "Click Continue to download all"); const pg = parseStr(pt) if (pg) { s_p = pg[0]; e_p = pg[1]; } if (pt != null) { const srcUrl = document.querySelector("#cover > a > img").src; const media_url_ = new URL(srcUrl).origin; const ext = srcUrl.split(".").at(-1); const zipFileWriter = new zip.BlobWriter(); const zipWriter = new zip.ZipWriter(zipFileWriter); for (let i = s_p; i <= e_p; i++) { const fname = i.toString() + "." + ext; const src = media_url_ + "/galleries/" + id + "/"+ fname; console.log((i+1).toString() + "/" + e_p.toString() + ": downloading "+src); //debug var blob; do { blob = await waitForXHRRequest(src,'GET','blob'); ret -= 1; if (!blob && !ret) { alert("Error while downloading file: " + src) } else { await sleeper(200); } } while (!blob && ret); if (blob.type.includes("image")){ const blobReader = new zip.BlobReader(blob); await zipWriter.add(fname, blobReader); } } await zipWriter.close(); const zipFileBlob = await zipFileWriter.getData(); const anchor = document.createElement("a"); const clickEvent = new MouseEvent("click"); anchor.href = window.URL.createObjectURL(zipFileBlob); anchor.download = filename; anchor.dispatchEvent(clickEvent); } } callback(dlBtn); } function setupButton(jNode) { const btnDiv = document.querySelector(".buttons"); // Get the last child of the div with class "buttons" jNode.forEach(function(dlElement) { const clonedElement = dlElement.cloneNode(true); // Clone the element with all its content clonedElement.id = "dlzip"; clonedElement.classList.remove("btn-disabled"); clonedElement.querySelector(".top").textContent = "Click to download as zip"; btnDiv.appendChild(clonedElement); // Append the cloned element to the last child clonedElement.addEventListener("click", (e)=>{ downloadChapter(gallery.media_id, gallery.title.japanese + ".zip", (el)=>{ el.classList.remove("btn-disabled"); }); }); }); } async function main(){ var xhr = await waitForXHRRequest("https://raw.githubusercontent.com/gildas-lormeau/zip.js/v2.7.17/dist/zip.js","GET",undefined); eval(xhr); const btn = await waitForElm('#download'); setupButton(btn); } main();