8Muses Downloader

Download comics from 8muses.com

Versión del día 25/07/2017. Echa un vistazo a la versión más reciente.

  1. // ==UserScript==
  2. // @name 8Muses Downloader
  3. // @namespace https://github.com/Kayla355
  4. // @version 0.1
  5. // @description Download comics from 8muses.com
  6. // @author Kayla355
  7. // @match https://www.8muses.com/comix/album/*
  8. // @grant GM_xmlhttpRequest
  9. // @run-at document-idle
  10. // @icon https://www.8muses.com/favicon.ico
  11. // @require https://cdn.jsdelivr.net/jszip/3.1.3/jszip.min.js
  12. // @require https://cdn.jsdelivr.net/filesaver.js/1.3.3/FileSaver.min.js
  13. // ==/UserScript==
  14. var progress = {
  15. current: 0,
  16. items: 0
  17. };
  18.  
  19. (function() {
  20. 'use strict';
  21. init();
  22. })();
  23.  
  24. function init() {
  25. var imagebox = document.querySelector('.gallery .image-box:not(.image-a)');
  26. if(imagebox) {
  27. var isImageAlbum = !!imagebox.href.match(/comix\/picture\//i);
  28. if(isImageAlbum) {
  29. createElements('single');
  30. } else {
  31. createElements('multi');
  32. }
  33. } else {
  34. setTimeout(init, 100);
  35. }
  36. }
  37.  
  38. function createElements(type) {
  39. if(!type) type = 'single';
  40. var downloadText = (type == "multi") ? 'Download All':'Download';
  41. var div = document.createElement('div');
  42. div.className += 'download show-tablet show-desktop block';
  43. div.style = "background-color: #3a4050; margin-right: -20px; margin-left: 21px;";
  44. var a = document.createElement('a');
  45. a.href = "#";
  46. a.style = "color: #fff; text-decoration: none; padding: 15px 20px 15px 10px;";
  47. a.innerHTML = '<i class="fa fa-arrow-down icon-inline" style="color: #242730;"></i>'+ downloadText;
  48. a.onclick = (type == "multi") ? downloadAll:downloadComic;
  49. var bar = document.createElement('div');
  50. bar.innerHTML = `<div class="loading-bar" style="position: absolute; right: 0px; top: 50px; background-color: aliceblue; display: none;">
  51. <center class="value" style="position: absolute; left: 0px; right: 0px; color: #242730;">0%</center>
  52. <div class="progressbar" style="width: 0%; height:20px; background-color: #b1c6ff;"></div>
  53. </div>`;
  54. div.append(a);
  55. document.querySelector('#top-menu > div.top-menu-right').append(div);
  56. bar.querySelector('.loading-bar').style.width = document.querySelector('#top-menu > div.top-menu-right .download').clientWidth+'px';
  57. document.querySelector('#content').append(bar);
  58. }
  59.  
  60. function updateProgressbar(status, hide) {
  61. status = (typeof status === "string") ? status:status+'%';
  62. if(hide) {
  63. document.querySelector('.loading-bar').style.display = 'none';
  64. } else {
  65. document.querySelector('.loading-bar').style.display = '';
  66. document.querySelector('.loading-bar .value').innerText = status;
  67. document.querySelector('.loading-bar .progressbar').style.width = status;
  68. }
  69. }
  70.  
  71. function downloadComic(container) {
  72. var imageContainers = (container.length) ? container:document.querySelectorAll('.gallery .image-box:not(.image-a)');
  73. var images = [];
  74. var doneLength = 0;
  75. var isImageAlbum = !!imageContainers[0].attributes.href.value.match(/comix\/picture\//i);
  76.  
  77. if(!container.length) updateProgressbar(0);
  78. if(isImageAlbum) progress.items += imageContainers.length;
  79.  
  80. for(var i=0; i < imageContainers.length; i++) {
  81. images.push({href: location.protocol +'//'+ location.hostname + imageContainers[i].attributes.href.value});
  82.  
  83. getPage(i, images[i], function(j, object) {
  84. images[j].path = object.path;
  85. images[j].name = object.name;
  86. images[j].imageHref = object.imageHref;
  87. images[j].blob = object.blob;
  88. doneLength++;
  89.  
  90. if(!container.length) {
  91. updateProgressbar(Math.round((doneLength/imageContainers.length)*100));
  92. } else if(isImageAlbum) {
  93. progress.current++;
  94. updateProgressbar(Math.round((progress.current/progress.items)*100));
  95. }
  96.  
  97. if(doneLength >= imageContainers.length) generateZip(images);
  98. })
  99. }
  100. }
  101.  
  102. function downloadAll(container) {
  103. var itemContainers = (container.length) ? container:document.querySelectorAll('.gallery .image-box:not(.image-a)');
  104. var items = [];
  105. var doneLength = 0;
  106.  
  107. if(!container.length) updateProgressbar(0);
  108.  
  109. for(var i=0; i < itemContainers.length; i++) {
  110. let href = location.protocol +'//'+ location.hostname + itemContainers[i].attributes.href.value;
  111. getImageAlbum(href, function(albumContainer) {
  112. var imagebox = albumContainer.querySelectorAll('.gallery .image-box:not(.image-a)');
  113. var isImageAlbum = !!imagebox[0].attributes.href.value.match(/comix\/picture\//i);
  114.  
  115. if(isImageAlbum) {
  116. downloadComic(imagebox);
  117. } else {
  118. downloadAll(imagebox);
  119. }
  120. })
  121. }
  122. }
  123.  
  124. function getImageAlbum(url, callback) {
  125. var xhr = new XMLHttpRequest();
  126. xhr.open('GET', url);
  127. xhr.onload = function(e) {
  128. var container = document.implementation.createHTMLDocument().documentElement;
  129. container.innerHTML = xhr.responseText;
  130. callback(container);
  131. };
  132. xhr.send();
  133. }
  134.  
  135. function getPage(i, image, callback) {
  136. var object = {};
  137. var xhr = new XMLHttpRequest();
  138. xhr.open('GET', image.href);
  139. // xhr.responseType = 'blob';
  140. xhr.onload = function(e) {
  141. var container = document.implementation.createHTMLDocument().documentElement;
  142. container.innerHTML = xhr.responseText;
  143. object.path = image.href.match(/^.*?[0-9]+\/(.*\/).*$/)[1]; // including author
  144. // object.path = image.href.match(/^.*?[0-9]+\/.*?\/(.*\/).*$/)[1]; // no author
  145. object.name = container.querySelector('.top-menu-breadcrumb li:last-of-type').innerText.trim();
  146. object.imageHref = 'https://cdn.ampproject.org/i/s/www.8muses.com' + container.querySelector('#imageDir').value + container.querySelector('#imageName').value;
  147.  
  148. getImageAsBlob(object.imageHref, function(blob) {
  149. if(!blob) return;
  150. object.blob = blob;
  151. callback(i, object);
  152. })
  153. };
  154. xhr.send();
  155. }
  156.  
  157. function getImageAsBlob(url, callback) {
  158. GM_xmlhttpRequest({
  159. url: url,
  160. method: 'GET',
  161. responseType: 'blob',
  162. onload: function(xhr) {
  163. var blob = xhr.response;
  164.  
  165. callback(blob);
  166. }
  167. });
  168.  
  169. // Non-GM CORS xhr request.
  170. // var xhr = new XMLHttpRequest();
  171. // xhr.open('GET', 'https://cors-anywhere.herokuapp.com/'+object.imageHref);
  172. // xhr.responseType = 'blob';
  173. // xhr.onload = function(e) {
  174. // var blob = xhr.response;
  175. // callback(blob);
  176. // }
  177. // xhr.send();
  178. }
  179.  
  180. function generateZip(images) {
  181. var zip = new JSZip();
  182. for(var i=0; i < images.length; i++) {
  183. zip.file(images[i].name, images[i].blob);
  184. }
  185.  
  186. // Blob
  187. zip.generateAsync({type:"blob"}).then(function (blob) {
  188. var filename = getFileName(images[0].path);
  189. if(progress.current === progress.items) updateProgressbar('Done!');
  190. saveAs(blob, filename+'.zip');
  191. }, function (err) {
  192. console.error('Error saving zip: ' +err);
  193. });
  194. }
  195.  
  196. function getFileName(pathname) {
  197. var pathArray = pathname.replace(/\/$/, '').split('/');
  198. var filename = "";
  199.  
  200. for(var i=0; i<pathArray.length; i++) {
  201. let partialName;
  202.  
  203. if(i === 0) partialName = '['+ pathArray[i] +']';
  204. if(i === 1) partialName = pathArray[i];
  205. if(i >= 2) partialName = ' - '+ pathArray[i];
  206.  
  207. filename += partialName;
  208. }
  209.  
  210. return filename;
  211. }