您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Download comics from 8muses.com
// ==UserScript== // @name 8Muses Downloader // @namespace https://github.com/Kayla355 // @version 0.5.1 // @description Download comics from 8muses.com // @author Kayla355 // @match http://comics.8muses.com/comics/album/* // @match https://comics.8muses.com/comics/album/* // @grant GM_xmlhttpRequest // @grant GM_addStyle // @grant GM_setValue // @grant GM_getValue // @grant GM_registerMenuCommand // @run-at document-idle // @icon https://www.8muses.com/favicon.ico // @require https://cdn.jsdelivr.net/jszip/3.1.3/jszip.min.js // @require https://cdn.jsdelivr.net/filesaver.js/1.3.3/FileSaver.min.js // @require https://gitcdn.xyz/repo/Kayla355/MonkeyConfig/d152bb448db130169dbd659b28375ae96e4c482d/monkeyconfig.js // @history 0.2.0 'Download all', now has an option to compress all the individual .zip files into one zip with sub-folders. By default this will be on. // @history 0.2.1 Added an option to compress the subfolders created from the single file download option. // @history 0.2.2 Fixed a bug where it would trigger the download multiple times when the "single file" option was enabled and the "compress sub folders" option was not. // @history 0.3.0 ALso added the basis for download trying to download something with pagination. However, this is disabled until I solve the issue of running out of memory while doing it. // @history 0.3.1 Fixed an issue caused by classnames being changed on the site. // @history 0.3.2 Fixed the URL match since it was changed. // @history 0.4.0 Updated the script to work with the new use of Ractive.js on the 8muses website. // @history 0.4.1 Fixed some css to better match the site. // @history 0.5.0 Fixed Image links and more // ==/UserScript== cfg = new MonkeyConfig({ title: '8Muses Downloader - Configuration', menuCommand: true, params: { single_file: { type: 'checkbox', default: true }, compress_sub_folders: { type: 'checkbox', default: false } }, onSave: setOptions }); var Settings = { singleFile: null, compressSubFolders: null, }; var zipArray = []; var containerArray = []; var downloadType; var progress = { pages: { current: 0, items: 0, }, current: 0, items: 0, zips: 0 }; function setOptions() { Settings.singleFile = cfg.get('single_file'); Settings.compressSubFolders = cfg.get('compress_sub_folders'); } (function() { 'use strict'; setOptions(); init(); })(); function init() { var imagebox = document.querySelector('.gallery .c-tile:not(.image-a)'); if(imagebox) { var isImageAlbum = !!imagebox.href.match(/comics\/picture\//i); if(isImageAlbum) { createElements('single'); } else { createElements('multi'); } } else { setTimeout(init, 100); } } function createElements(type) { downloadType = type || 'single'; var downloadText = (downloadType == "multi") ? 'Download All':'Download'; var div = document.createElement('div'); div.className += 'download show-tablet show-desktop block'; div.style = "background-color: #3a4050; border-left: 1px solid #1a1c22;"; var a = document.createElement('a'); a.href = "#"; a.style = "color: #fff; text-decoration: none; padding: 15px 20px 15px 10px;"; a.innerHTML = '<i class="fa fa-arrow-down icon-inline" style="color: #242730;"></i>'+ downloadText; a.onclick = downloadHandler; var bar = document.createElement('div'); bar.innerHTML = `<div class="loading-bar" style="position: absolute; right: 0px; top: 50px; background-color: aliceblue; display: none;"> <center class="value" style="position: absolute; left: 0px; right: 0px; color: #242730;">0%</center> <div class="progressbar" style="width: 0%; height:20px; background-color: #b1c6ff;"></div> </div>`; div.append(a); document.querySelector('#top-menu > div.top-menu-right').append(div); bar.querySelector('.loading-bar').style.width = document.querySelector('#top-menu > div.top-menu-right .download').clientWidth+'px'; document.querySelector('#content').append(bar); } function updateProgressbar(status, hide) { status = (typeof status === "string") ? status:status+'%'; if(hide) { document.querySelector('.loading-bar').style.display = 'none'; } else { document.querySelector('.loading-bar').style.display = ''; document.querySelector('.loading-bar .value').innerText = status; document.querySelector('.loading-bar .progressbar').style.width = status; } } function downloadHandler(e) { e.preventDefault(); e.stopPropagation(); if(document.querySelector('.loading-bar').style.display !== "none") return; if(downloadType == "multi") { downloadAll(); } else { downloadComic(); } } function downloadComic(container) { var imageContainers = (container) ? container:document.querySelectorAll('.gallery .c-tile:not(.image-a)'); var images = []; var doneLength = 0; var isImageAlbum = !!imageContainers[0].attributes.href.value.match(/comics\/picture\//i); if(!container) updateProgressbar(0); if(isImageAlbum) progress.pages.items += imageContainers.length; if(isImageAlbum) progress.items++; for(var i=0; i < imageContainers.length; i++) { images.push({href: location.protocol +'//'+ location.hostname + imageContainers[i].attributes.href.value}); getPageImage(i, images[i], function(j, object) { images[j].path = object.path; images[j].name = object.name; images[j].imageHref = object.imageHref; images[j].blob = object.blob; doneLength++; if(!container) { updateProgressbar(Math.round((doneLength/imageContainers.length)*100)); } else if(isImageAlbum) { if(j === 0) progress.current++; progress.pages.current++; updateProgressbar(Math.round((progress.pages.current/progress.pages.items)*100)); } if(doneLength >= imageContainers.length) createZip(images); }); } } function downloadAll(container) { var itemContainers = (container) ? container:document.querySelectorAll('.gallery .c-tile:not(.image-a)'); var pagination = document.querySelector('.pagination'); // var pagination = false; //Disabled var items = []; var doneLength = 0; var downloadFunc = function(albumContainer) { //console.log(albumContainer) var imagebox = albumContainer.querySelectorAll('.gallery .c-tile:not(.image-a)'); var isImageAlbum = !!imagebox[0].attributes.href.value.match(/comics\/picture\//i); if(isImageAlbum) { downloadComic(imagebox); } else { downloadAll(imagebox); } }; if(pagination && !container) { var lastHref = pagination.querySelector('span:last-child a').attributes.href.value; var pageCount = parseInt(lastHref.match(/[0-9]+$/)[0]); var urls = []; for(let i=1; i <= pageCount; i++) { urls.push(location.protocol +'//'+ location.hostname + lastHref.replace(/[0-9]+$/, i)); } getImageAlbum(urls, downloadFunc); } else { if(!container) updateProgressbar(0); for(let i=0; i < itemContainers.length; i++) { if(!itemContainers[i].attributes.href || itemContainers[i].attributes.href.value == "") continue; let href = location.protocol +'//'+ location.hostname + itemContainers[i].attributes.href.value; getImageAlbum(href, downloadFunc); } } } function getImageAlbum(url, callback) { if(typeof url === "object" && url.length) { for(var i=0; i < url.length; i++) { getImageAlbum(url[i], function(pageContainer) { var items = pageContainer.querySelector('.gallery').innerHTML; containerArray.push(items); if(containerArray.length >= url.length) { var container = document.implementation.createHTMLDocument().documentElement; container.innerHTML = '<div class="gallery">' + containerArray.join('') + '</div>'; callback(container); } }); } } else { var xhr = new XMLHttpRequest(); xhr.open('GET', url); xhr.onload = function(e) { var container = document.implementation.createHTMLDocument().documentElement; container.innerHTML = xhr.responseText; callback(container); }; xhr.send(); } } function getPageImage(i, image, callback) { var decodePublic = function(t) { return "!" === (e = t.replace(/>/g, ">").replace(/</g, "<").replace(/&/g, "&")).charAt(0) ? e.substr(1).replace(/[\x21-\x7e]/g, function(t) { return String.fromCharCode(33 + (t.charCodeAt(0) + 14) % 94); }) : ""; }; var object = {}; var xhr = new XMLHttpRequest(); xhr.open('GET', image.href); // xhr.responseType = 'blob'; xhr.onload = function(e) { var container = document.implementation.createHTMLDocument().documentElement; container.innerHTML = xhr.responseText; var data = JSON.parse(decodePublic(container.querySelector("#ractive-public").innerHTML.trim())); var ext = data.picture.normalizedPath.match(/\..*?$/i); object.path = image.href.match(/^.*?(picture|album)\/.*?\/(.*\/).*$/i)[2]; // including author // object.path = image.href.match(/^.*?[0-9]+\/.*?\/(.*\/).*$/)[1]; // no author //object.name = container.querySelector('.top-menu-breadcrumb li:last-of-type').innerText.trim(); //+ container.querySelector('#imageName').value.match(/\.([0-9a-z]+)(?:[\?#]|$)/i)[0]; //object.name = data.picture.name + ext; object.name = data.picture.name + ".jpg"; //object.imageHref = 'https://www-8muses-com.cdn.ampproject.org/i/www.8muses.com/image/fl' + container.querySelector('#imageDir').value + container.querySelector('#imageName').value; //object.imageHref = 'https://www.8muses.com/image/fl/' + data.picture.publicUri + ext; object.imageHref = 'https://www.8muses.com/image/fl/' + data.picture.publicUri + ".jpg"; console.log(object); getImageAsBlob(object.imageHref, function(blob) { if(!blob) return; object.blob = blob; callback(i, object); }); }; xhr.send(); } function getImageAsBlob(url, callback) { GM_xmlhttpRequest({ url: url, method: 'GET', responseType: 'blob', onload: function(xhr) { var blob = xhr.response; callback(blob); } }); // Non-GM CORS xhr request. // var xhr = new XMLHttpRequest(); // xhr.open('GET', 'https://cors-anywhere.herokuapp.com/'+object.imageHref); // xhr.responseType = 'blob'; // xhr.onload = function(e) { // var blob = xhr.response; // callback(blob); // } // xhr.send(); } function createZip(images) { var filename = getFileName(images[0].path); var zip = new JSZip(); // Generate single or multiple zip files. if(Settings.singleFile && progress.current > 0) { if(Settings.compressSubFolders) { for(let i=0; i < images.length; i++) { zip.file(images[i].name, images[i].blob); } generateZip(zip, filename, function(blob, filename) { zipArray.push({name: filename, blob: blob}); progress.zips++; if(progress.zips === progress.items) { var singleZip = new JSZip(); for(let i=0; i < zipArray.length; i++) { singleZip.file(zipArray[i].name, zipArray[i].blob); } generateZip(singleZip, filename.match(/\[(.*)\]/)[1], function(blob, filename) { saveAs(blob, filename); }); } }); } else { for(let i=0; i < images.length; i++) { zipArray.push({name: filename +'/'+ images[i].name, blob: images[i].blob}); // zip.file(images[i].name, images[i].blob); } if(progress.pages.current === progress.pages.items) { var singleZip = new JSZip(); for(let i=0; i < zipArray.length; i++) { singleZip.file(zipArray[i].name, zipArray[i].blob); } generateZip(singleZip, filename.match(/\[(.*)\]/)[1], function(blob, filename) { saveAs(blob, filename); }); } } } else { for(let i=0; i < images.length; i++) { zip.file(images[i].name, images[i].blob); } generateZip(zip, filename, function(blob, filename) { saveAs(blob, filename); }); } } // function generateZip(zip, filename, callback) { // zip.generateAsync({type:"blob"}).then(function (blob) { // if(progress.pages.current === progress.pages.items) updateProgressbar('Done!'); // if(typeof callback === 'function') callback(blob, filename+'.zip'); // }, function (err) { // console.error('Error saving zip: ' +err); // }); // } function generateZip(zip, filename, callback) { zip.generateInternalStream({type:"blob", streamFiles: true}) .accumulate(function updateCallback(metadata) { // console.log(metadata); updateProgressbar(metadata.percent); // metadata contains for example currentFile and percent, see the generateInternalStream doc. }).then(function (blob) { if(typeof callback === 'function') callback(blob, filename+'.zip'); if(progress.pages.current === progress.pages.items) updateProgressbar('Done!'); // data contains here the complete zip file as a uint8array (the type asked in generateInternalStream) }); } function getFileName(pathname) { var pathArray = pathname.replace(/\/$/, '').split('/'); var filename = ""; for(var i=0; i<pathArray.length; i++) { let partialName; if(i === 0) partialName = '['+ pathArray[i] +']'; if(i === 1) partialName = pathArray[i]; if(i >= 2) partialName = ' - '+ pathArray[i]; filename += partialName; } return filename; }