GoogleBookDown - Download Google Books as PNGs or PDF

Saves all available Preview pages from a Google Book as PNGs or PDF, Base on GBookDown

  1. // ==UserScript==
  2. // @name GoogleBookDown - Download Google Books as PNGs or PDF
  3. // @description Saves all available Preview pages from a Google Book as PNGs or PDF, Base on GBookDown
  4. // @author kawais
  5. // @namespace kawais
  6. // @include https://books.google.*/books*
  7. // @version 1.0
  8. // @require https://cdnjs.cloudflare.com/ajax/libs/jquery/1.12.0/jquery.min.js
  9. // @require https://cdnjs.cloudflare.com/ajax/libs/jszip/2.5.0/jszip.min.js
  10. // @require https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/1.3.8/FileSaver.min.js
  11. // @require https://cdnjs.cloudflare.com/ajax/libs/jspdf/1.5.3/jspdf.min.js
  12. // @grant GM.xmlhttpRequest
  13. // ==/UserScript==
  14.  
  15. this.$ = this.jQuery = jQuery.noConflict(true);
  16. console.log(GM_info);
  17.  
  18. (function () {
  19. var css = [
  20. '#savePNG {',
  21. ' position: fixed;',
  22. ' padding: 7px;',
  23. ' background-color: #F8EEB1;',
  24. ' border: 2px solid #333;',
  25. ' border-radius: 6px;',
  26. ' z-index: 9999;',
  27. ' font-size: 18px;',
  28. ' right: 30px;',
  29. ' bottom: 20px;',
  30. ' color: #000;',
  31. ' width: 650px;',
  32. ' text-align: center;',
  33. ' box-shadow: 0 1px 4px rgba(0, 0, 0, 0.5), 0 0 15px rgba(0, 0, 0, 0.3);',
  34. '}',
  35. '#savePNG button {',
  36. ' font-size: 18px;',
  37. ' width: 100px;',
  38. ' cursor: pointer;',
  39. '}',
  40. '#savePNG input {',
  41. ' width: 60px;',
  42. ' font-size: 18px;',
  43. ' text-align: center;',
  44. '}'
  45. ].join('\n');
  46. if (typeof GM_addStyle != 'undefined') {
  47. GM_addStyle(css);
  48. } else if (typeof PRO_addStyle != 'undefined') {
  49. PRO_addStyle(css);
  50. } else if (typeof addStyle != 'undefined') {
  51. addStyle(css);
  52. } else {
  53. var node = document.createElement('style');
  54. node.type = 'text/css';
  55. node.appendChild(document.createTextNode(css));
  56. var heads = document.getElementsByTagName('head');
  57. if (heads.length > 0) {
  58. heads[0].appendChild(node);
  59. } else {
  60. // no head yet, stick it whereever
  61. document.documentElement.appendChild(node);
  62. }
  63. }
  64. }) ();
  65.  
  66. var html = '<div id="savePNG">' +
  67. '<span>There are max </span> ' +
  68. '<input type="text" id="total" value="---" readonly> ' +
  69. '<span>pages available. Input the page range to download </span> ' +
  70. '<br>from<input type="text" id="count" value="0"> ' +
  71. 'to<input type="text" id="max" value="0"> ' +
  72. '<span>.</span> ' +
  73. '<button>Start</button>' +
  74. '<input type="radio" name="saveAs" value="pdf" checked>pdf <input type="radio" name="saveAs" value="zip">zip'+
  75. '</div>';
  76.  
  77. $('body').prepend(html);
  78.  
  79. $('#count').select();
  80.  
  81. var title, id, zipfile, zipname;
  82. var zipflag = false;
  83. var next = true;
  84. var zip = new JSZip();
  85. var pids = [];
  86.  
  87. var doc = new jsPDF();
  88. doc.deletePage(1);
  89.  
  90. var saveAsType='pdf';
  91.  
  92. $('#savePNG button').click(function(){
  93. doTheMagic();
  94. })
  95.  
  96. $("#count").keyup(function(event){
  97. if(event.keyCode == 13){
  98. $("#savePNG button").click();
  99. }
  100. });
  101.  
  102. function addPdfPage(imgStr) {
  103. let u8a=doc.binaryStringToUint8Array(imgStr);
  104. let prop=doc.getImageProperties (u8a);
  105. doc.addPage([prop.width,prop.height]);
  106. doc.addImage(u8a,prop.fileType,0,0);
  107. }
  108.  
  109. function savePdf(count, number){
  110. let name = title + '_' + id + '_' +
  111. pad(count, number.toString().length) +
  112. '_' + number + '.pdf';
  113. $('#savePNG button').prop('disabled', false).text('Start');
  114. saveAs(doc.output('blob'), name);
  115. }
  116.  
  117. function addIMG(url, name, count) {
  118. var filename = name + '.png';
  119. $.ajax({
  120. type: "GET",
  121. //dataType: 'binary',
  122. mimeType: "image/png; charset=x-user-defined",
  123. async: true,
  124. url: url,
  125. }).done(function (data) {
  126. try{
  127. console.log(url);
  128. $('#savePNG button').prop('disabled', true).text('Working…');
  129. $('#count').val(count);
  130. $('#count').css('font-weight', 'normal').css('text-decoration', 'none');
  131. if(saveAsType==='zip'){
  132. zip.file(filename, data, {binary: true});
  133. }else{
  134. addPdfPage(data);
  135. }
  136. }catch(e){
  137. console.error(e);
  138. }
  139. }).error(function(xhr,status,e){
  140. console.error(e);
  141. });
  142. return;
  143. /*
  144. GM.xmlhttpRequest({
  145. method: 'GET',
  146. synchronous: true,
  147. url: url,
  148. overrideMimeType: 'image/png; charset=x-user-defined',
  149. onload: function (response) {
  150. $('#savePNG button').prop('disabled', true).text('Working…');
  151. $('#count').val(count);
  152. $('#count').css('font-weight', 'normal').css('text-decoration', 'none');
  153. zip.file(filename, response.responseText, {
  154. binary: true
  155. });
  156. }
  157. });
  158. */
  159. }
  160.  
  161. function saveZip(count, number) {
  162. zipname = title + '_' + id + '_' +
  163. pad(count, number.toString().length) +
  164. '_' + number + '.zip';
  165. zipfile = zip.generate({
  166. type: 'blob'
  167. });
  168. saveAs(zipfile, zipname);
  169. }
  170.  
  171. function save(count,number)
  172. {
  173. if(saveAsType==='zip')
  174. {
  175. saveZip(count,number);
  176. }else{
  177. savePdf(count,number);
  178. }
  179. }
  180.  
  181. function pad(num, size) {
  182. var s = "000000000" + num;
  183. return s.substr(s.length-size);
  184. }
  185.  
  186. function doTheMagic() {
  187. saveAsType=$('input[name="saveAs"]:checked').val();
  188. $('#savePNG button').prop('disabled', true);
  189. var href = window.location.href;
  190. var domain = href.split('/') [2];
  191.  
  192. id = href.match(/id=([^&]+)/) [1];
  193. title = $('title').text().split(' - ')[0].replace(/\s+/g,'_');
  194.  
  195. var pid = 'PP1';
  196. var count = parseInt($('#count').val());
  197. var max = parseInt($('#max').val());
  198. var url = 'https://' + domain + '/books?id=' + id + '&lpg=' + pid + '&pg=' + pid + '&jscmd=click3';
  199.  
  200. $.ajax({
  201. dataType: 'json',
  202. async: false,
  203. url: url,
  204. }).done(function (data) {
  205. pids = $.map(data.page, function (val, i) {
  206. if (i > 3) {
  207. return val.pid;
  208. }
  209. });
  210. });
  211.  
  212. var number = pids.length;
  213. $('#total').val(number);
  214. if(max===0) {
  215. max=number;
  216. $('#max').val(max);
  217. }
  218.  
  219. if (count >= number) {
  220. alert('Wrong number of pages. There are only ' + number + ' available.');
  221. $('#savePNG button').prop('disabled', false);
  222. } else {
  223. var i = count;
  224. var timer = setInterval(function () {
  225. if (i == max || i == number) {
  226. clearInterval(timer);
  227. alert('Downloaded every available page out of '+ number + ' total. Saving…');
  228. save(i, number);
  229. return false;
  230. }
  231.  
  232. var url = 'https://' + domain + '/books?id=' + id + '&lpg=' + pids[i] + '&pg=' + pids[i] + '&jscmd=click3';
  233. $.ajax({
  234. dataType: 'json',
  235. async: false,
  236. url: url,
  237. }).done(function (data) {
  238. if (data.page[0].hasOwnProperty('src') || data.page[0].flags == 8) {
  239. var url = data.page[0].src + '&w=1600';
  240. var name = pad((i+1), number.toString().length) + '_' + pids[i];
  241.  
  242. if (data.page[0].flags == 8) {
  243. $('#count').val(i + 1);
  244. $('#count').css('font-weight', 'bold').css('text-decoration', 'line-through');
  245. } else {
  246. try{
  247. addIMG(url, name, (i + 1));
  248. }catch(e){
  249. console.log(e);
  250. }
  251. }
  252. i=i+1;
  253.  
  254. } else {
  255. clearInterval(timer);
  256. alert('Can\'t download more.\n' +
  257. 'Saving ' + i + ' out of ' + number + ' pages…\n' +
  258. 'Change your IP and continue!');
  259. save(i, number);
  260. }
  261. });
  262. }, 400);
  263. }
  264. }