你需要先安装一款用户样式管理器扩展(如 Stylus )后才能安装此样式。
你需要先安装一款用户样式管理器扩展(如 Stylus )后才能安装此样式。
你需要先安装一款用户样式管理器扩展(如 Stylus )后才能安装此样式。
你需要先安装一款用户样式管理器扩展后才能安装此样式。
你需要先安装一款用户样式管理器扩展后才能安装此样式。
你需要先安装一款用户样式管理器扩展后才能安装此样式。
(我已安装用户样式管理器,立即安装用户样式!)
换行
// ==UserScript== // @name MRM Downloader // @namespace https://nyt92.eu.org // @version 2025-1-27 // @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/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" ); 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 . 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 . 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 . 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 checkIfVid = document . querySelectorAll ( ".entry-categories a" ); if (! checkIfVid ) { console . log ( "no media found!" ); } else if ( document . querySelector ( "#MRM_video" ) !== null ) { document . body . appendChild ( videoDlBtn ); } else if ( document . querySelector ( ".img-myreadingmanga" ) !== null ) { document . body . appendChild ( imageDlBtn ); } 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 only shown once per week" ); 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 images = document . querySelectorAll ( ".img-myreadingmanga" ); let imageSources = []; imageSources = Array . from ( images ) . map (( img ) => img . dataset . src ) . filter ( Boolean ); if ( imageSources . length === 0 ) { imageSources = Array . from ( images ) . map (( img ) => img . src ) . filter ( Boolean ); } if ( imageSources . length === 0 ) { const nestedImages = document . querySelectorAll ( ".img-myreadingmanga img" ); imageSources = Array . from ( nestedImages ) . map (( img ) => img . src || img . dataset . src ) . filter ( Boolean ); } 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" ; }, }); }); })();