// ==UserScript==
// @name MRM Downloader
// @namespace https://nyt92.eu.org
// @version 2025-3-20
// @description Download video and bulk images from myreadingmanga manga/doujin page.
// @author nyt92
// @match https://myreadingmanga.info/*
// @exclude https://myreadingmanga.info/about/
// @exclude https://myreadingmanga.info/whats-that-book/
// @exclude https://myreadingmanga.info/upload
// @exclude https://myreadingmanga.info/popular/*
// @exclude https://myreadingmanga.info/video/*
// @exclude https://myreadingmanga.info/cats/*
// @exclude https://myreadingmanga.info/pairing/*
// @exclude https://myreadingmanga.info/group/*
// @exclude https://myreadingmanga.info/privacy-policy/
// @exclude https://myreadingmanga.info/dmca-notice/
// @exclude https://myreadingmanga.info/contact/
// @exclude https://myreadingmanga.info/terms-service/
// @exclude https://myreadingmanga.info/sitemap/
// @exclude https://myreadingmanga.info/my-bookmark/
// @exclude https://myreadingmanga.info/tag/*
// @exclude https://myreadingmanga.info/genre/*
// @exclude https://myreadingmanga.info/status/*
// @exclude https://myreadingmanga.info/lang/*
// @exclude https://myreadingmanga.info/yaoi-manga/*
// @exclude https://myreadingmanga.info/manhwa/*
// @connect myreadingmanga.info
// @connect i1.myreadingmanga.info
// @connect i2.myreadingmanga.info
// @connect i3.myreadingmanga.info
// @connect i4.myreadingmanga.info
// @connect i5.myreadingmanga.info
// @connect i6.myreadingmanga.info
// @supportURL https://github.com/NYT92/mrm-downloader
// @icon https://www.google.com/s2/favicons?sz=64&domain=myreadingmanga.info
// @require https://cdnjs.cloudflare.com/ajax/libs/jszip/3.7.1/jszip.min.js
// @require https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/2.0.5/FileSaver.min.js
// @grant GM_xmlhttpRequest
// @license GPLv3
// ==/UserScript==
// THIS USERSCRIPT IS LICENSE UNDER THE GPLv3 License
(function () {
("use strict");
const excludedPaths = [
"/about/",
"/upload/",
"/whats-that-book/",
"/popular/",
"/video/",
"/cats/",
"/pairing/",
"/group/",
"/privacy-policy/",
"/dmca-notice/",
"/contact/",
"/terms-service/",
"/sitemap/",
"/my-bookmark/",
"/tag/",
"/genre/",
"/status/",
"/lang/",
"/yaoi-manga/",
"/manhwa/",
];
const currentPath = window.location.pathname;
if (excludedPaths.some((path) => currentPath.startsWith(path))) {
return;
}
const wpadminbar = document.querySelector("#wpadminbar");
if (wpadminbar) {
wpadminbar.remove();
document.documentElement.setAttribute("style", "margin-top: 0px !important;");
}
const style = document.createElement("style");
style.textContent = `
.mrm-dl-btn {
background-color: #ffab23;
border-radius: 5px;
display: inline-block;
cursor: pointer;
color: black;
font-size: 16px;
font-weight: bold;
padding: 16px 32px;
text-decoration: none;
}
.mrm-dl-btn:hover {
background-color:rgb(196, 120, 24);
}
.mrm-dl-btn:active {
position: relative;
top: 1px;
}
`;
document.head.appendChild(style);
function saveCookies() {
const cookies = prompt("Please paste your cookies here:");
if (cookies) {
localStorage.setItem("mrm_cookies", cookies);
alert("Cookies saved!");
window.location.reload();
}
}
const cookiesBtn = document.createElement("button");
cookiesBtn.setAttribute("class", "mrm-dl-btn");
cookiesBtn.id = "saveCookiesBtn";
cookiesBtn.textContent = "Load 🍪";
cookiesBtn.style.cssText =
"position: fixed; bottom: 10px; right: 10px; z-index: 9999;";
document.body.appendChild(cookiesBtn);
cookiesBtn.addEventListener("click", saveCookies);
const title =
document
.querySelector(".entry-header h1.entry-title")
?.textContent.trim() || "Untitled";
const imageDlBtn = document.createElement("button");
imageDlBtn.setAttribute("class", "mrm-dl-btn");
imageDlBtn.id = "downloadImagesBtn";
imageDlBtn.textContent = "Download Images (.zip)";
imageDlBtn.style.cssText =
"position: fixed; top: 10px; right: 10px; z-index: 9999;";
const videoDlBtn = document.createElement("button");
videoDlBtn.setAttribute("class", "mrm-dl-btn");
videoDlBtn.id = "downloadVideoBtn";
videoDlBtn.textContent = "Download Video";
videoDlBtn.style.cssText =
"position: fixed; top: 10px; right: 10px; z-index: 9999;";
const progressBar = document.createElement("div");
progressBar.id = "downloadProgress";
progressBar.style.cssText =
"position: fixed; top:120px; right: 10px; width: 235px; right: 10px; height: 20px; background-color: #f0f0f0; display: none; z-index: 9999;";
const progressInner = document.createElement("div");
progressInner.style.cssText =
"width: 0%; height: 100%; background-color: #4CAF50; transition: width 0.5s;";
progressBar.appendChild(progressInner);
const progressText = document.createElement("div");
progressText.style.cssText =
"position: fixed; top: 145px; right: 10px; z-index: 9999; display: none;";
progressText.textContent = "Preparing download...";
const checkVidinTag = Array.from(
document.querySelectorAll(".entry-categories a")
).map((tag) => tag.textContent.trim().toLowerCase());
const hasVideo = document.querySelector("#MRM_video") !== null;
const hasYouTube =
document.querySelector("iframe[src*='youtube.com']") !== null;
const isHomePage = document.querySelector(".content-archive") !== null;
const imageSelectors = [
".img-myreadingmanga",
".img-myreadingmanga img",
".entry-content img",
".separator img",
"img[decoding='async']",
];
let hasImages = false;
for (const selector of imageSelectors) {
if (document.querySelectorAll(selector).length > 0) {
hasImages = true;
break;
}
}
if (
excludedPaths.some((path) => currentPath.startsWith(path)) ||
hasYouTube ||
isHomePage
) {
return;
}
if (checkVidinTag.includes("video") && hasVideo) {
document.body.appendChild(videoDlBtn);
} else if (!checkVidinTag.includes("video") && hasImages) {
document.body.appendChild(imageDlBtn);
} else {
return;
}
document.body.appendChild(progressBar);
document.body.appendChild(progressText);
console.log(
"Info: Cookies are required for this script to work due to browser limitations and Cloudflare protection. See https://github.com/NYT92/mrm-downloader/tree/main?tab=readme-ov-file#using-the-script for more information."
);
const savedCookies = localStorage.getItem("mrm_cookies");
const lastAlertTime = localStorage.getItem("mrm_last_alert");
const ONE_WEEK = 7 * 24 * 60 * 60 * 1000;
if (!savedCookies) {
const currentTime = Date.now();
if (!lastAlertTime || currentTime - parseInt(lastAlertTime) > ONE_WEEK) {
alert(
"Please set the cookies first before downloading images. See https://github.com/NYT92/mrm-downloader/tree/main?tab=readme-ov-file#using-the-script for more information. This alert will not show again until week has passed."
);
localStorage.setItem("mrm_last_alert", currentTime.toString());
}
imageDlBtn.style.display = "none";
videoDlBtn.style.display = "none";
return;
}
const cookiesValue = savedCookies || "";
imageDlBtn.addEventListener("click", function () {
imageDlBtn.disabled = true;
imageDlBtn.textContent = "Downloading...";
const imageSelectors = [
".img-myreadingmanga",
".img-myreadingmanga img",
".entry-content img",
".separator img",
"img[decoding='async']",
];
let imageSources = [];
for (const selector of imageSelectors) {
const images = document.querySelectorAll(selector);
if (images.length > 0) {
imageSources = Array.from(images)
.map((img) => img.src || img.dataset.src)
.filter(Boolean);
break;
}
}
if (imageSources.length === 0) {
alert(
"No images found on this page. Check the console for debugging information."
);
imageDlBtn.disabled = false;
imageDlBtn.textContent = "Download Images (.zip)";
return;
}
const pageElement = document.querySelector(".post-page-numbers.current");
const page = pageElement ? pageElement.textContent.trim() : "1";
const zip = new JSZip();
progressBar.style.display = "block";
progressText.style.display = "block";
progressInner.style.width = "0%";
function getExtensionFromMimeType(mimeType) {
const mimeToExt = {
"image/jpeg": "jpg",
"image/png": "png",
"image/gif": "gif",
"image/webp": "webp",
"image/jpg": "jpg",
"text/html": "html",
};
return mimeToExt[mimeType.toLowerCase()];
}
function addImageToZip(src, index) {
return new Promise((resolve, reject) => {
progressText.textContent = `Downloading image ${index + 1} of ${
imageSources.length
}...`;
GM_xmlhttpRequest({
method: "GET",
url: src,
headers: {
Cookie: cookiesValue,
},
responseType: "arraybuffer",
onload: function (response) {
try {
const arrayBuffer = response.response;
const byteArray = new Uint8Array(arrayBuffer);
let mimeType = "image/jpeg";
try {
const contentTypeMatch = response.responseHeaders.match(
/Content-Type:\s*(\S+)/i
);
if (contentTypeMatch && contentTypeMatch[1]) {
mimeType = contentTypeMatch[1];
}
} catch (headerError) {
console.warn(
`Could not parse Content-Type header for ${src}:`,
headerError
);
}
const blob = new Blob([byteArray], { type: mimeType });
if (blob.type.includes("text/html")) {
alert(
"The script have detected the Cloudflare is blocking the page. You need to re-enter the cookies info due to Cloudflare resetting the cookies or the cookies is expired. See https://github.com/NYT92/mrm-downloader/tree/main?tab=readme-ov-file#using-the-script for more information."
);
reject(new Error("Invalid cookies"));
window.location.reload();
} else {
const ext = getExtensionFromMimeType(blob.type);
const fileName = `image_${index + 1}.${ext}`;
zip.file(fileName, blob, { binary: true });
console.log(
`Added ${fileName} to ZIP (${blob.size} bytes, type: ${blob.type})`
);
const progress = ((index + 1) / imageSources.length) * 100;
progressInner.style.width = `${progress}%`;
resolve();
}
} catch (error) {
console.error(`Error processing ${src}:`, error);
reject(error);
}
},
onerror: function (error) {
console.error(`Error fetching ${src}:`, error);
reject(error);
},
});
});
}
Promise.all(imageSources.map(addImageToZip))
.then(() => {
progressText.textContent = "Creating ZIP file...";
return zip.generateAsync({ type: "blob" });
})
.then(function (content) {
const safeTitle = title.replace(/[^a-z0-9]/gi, "_").toLowerCase();
const fileName = `${safeTitle}_ch${page}.zip`;
saveAs(content, fileName);
console.log("ZIP file saved successfully");
progressBar.style.display = "none";
progressText.style.display = "none";
imageDlBtn.disabled = false;
imageDlBtn.textContent = "Download Images (.zip)";
})
.catch((error) => {
console.error("Error creating ZIP file:", error);
alert(
"An error occurred while creating the ZIP file. Please check the console for details."
);
progressBar.style.display = "none";
progressText.style.display = "none";
imageDlBtn.disabled = false;
imageDlBtn.textContent = "Download Images (.zip)";
});
});
videoDlBtn.addEventListener("click", function () {
videoDlBtn.disabled = true;
videoDlBtn.textContent = "Downloading...";
const videoElement = document.querySelector("#MRM_video > video > source");
if (!videoElement) {
alert("No video found on this page.");
videoDlBtn.disabled = false;
videoDlBtn.textContent = "Download Video";
return;
}
const videoSrc = videoElement.src;
if (!videoSrc) {
alert("Unable to find video source.");
videoDlBtn.disabled = false;
videoDlBtn.textContent = "Download Video";
return;
}
progressBar.style.display = "block";
progressText.style.display = "block";
progressText.textContent = "Starting video download...";
progressInner.style.width = "0%";
GM_xmlhttpRequest({
method: "GET",
url: videoSrc,
headers: {
Cookie: cookiesValue,
},
responseType: "arraybuffer",
onprogress: function (progress) {
if (progress.lengthComputable) {
const percentComplete = (progress.loaded / progress.total) * 100;
progressInner.style.width = percentComplete + "%";
const downloadedMB = (progress.loaded / (1024 * 1024)).toFixed(2);
const totalMB = (progress.total / (1024 * 1024)).toFixed(2);
console.log(`Downloaded: ${downloadedMB}MB / ${totalMB}MB`);
progressText.textContent = `Downloaded: ${downloadedMB}MB / ${totalMB}MB`;
}
},
onload: function (response) {
const blob = new Blob([response.response], { type: "video/mp4" });
if (new Blob([response.response]).type.includes("text/html")) {
alert(
"The script have detected the Cloudflare is blocking the page. You need to re-enter the cookies info due to Cloudflare resetting the cookies or the cookies is expired. See https://github.com/NYT92/mrm-downloader/tree/main?tab=readme-ov-file#using-the-script for more information."
);
window.location.reload();
} else {
const safeTitle = title.replace(/[^a-z0-9]/gi, "_").toLowerCase();
const fileName = `${safeTitle}.mp4`;
saveAs(blob, fileName);
console.log("Video downloaded successfully");
progressBar.style.display = "none";
progressText.style.display = "none";
videoDlBtn.disabled = false;
videoDlBtn.textContent = "Download Video";
}
},
onerror: function (error) {
console.error("Error downloading video:", error);
alert(
"An error occurred while downloading the video. Please check the console for details."
);
progressBar.style.display = "none";
progressText.style.display = "none";
videoDlBtn.disabled = false;
videoDlBtn.textContent = "Download Video";
},
});
});
})();