Sleazy Fork is available in English.

8Muses Downloader

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(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&amp;/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;
}