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();