line-sticker-to-zip

Pack all line-stickers to one zip file

  1. // ==UserScript==
  2. // @name line-sticker-to-zip
  3. // @name:ja line-sticker-to-zip
  4. // @name:en line-sticker-to-zip
  5. // @version 0.0.1
  6. // @description Pack all line-stickers to one zip file
  7. // @description:ja Pack all line-stickers to one zip file
  8. // @description:en Pack all line-stickers to one zip file
  9. // @author freypy
  10. // @match https://store.line.me/stickershop/product/*
  11. // @grant none
  12. // @require https://cdn.bootcss.com/jszip/3.1.3/jszip.js
  13. // @namespace https://greasyfork.org/users/11647
  14. // ==/UserScript==
  15. (function($) {
  16. 'use strict';
  17. var buttonHtml = `
  18. <a class="MdBtn01 mdBtn01" id="line-sticker-downloader-button" style="
  19. position: fixed;
  20. z-index: 9999;
  21. right: 30px;
  22. bottom: 0;
  23. background-color:#ff8cd9;
  24. ">
  25. <span class="mdBtn01Inner">
  26. <span class="mdBtn01Txt">打包贴图</span>
  27. </span>
  28. </a>
  29. `;
  30. var zip = new JSZip();
  31. var stickers = zip.folder("stickers");
  32. var imgs = [];
  33. // function onclick() {
  34. // console.log('&', $);
  35. (function initFileSaver() {
  36. /* FileSaver.js
  37. * A saveAs() FileSaver implementation.
  38. * 1.3.2
  39. * 2016-06-16 18:25:19
  40. *
  41. * By Eli Grey, http://eligrey.com
  42. * License: MIT
  43. * See https://github.com/eligrey/FileSaver.js/blob/master/LICENSE.md
  44. */
  45.  
  46. /*global self */
  47. /*jslint bitwise: true, indent: 4, laxbreak: true, laxcomma: true, smarttabs: true, plusplus: true */
  48.  
  49. /*! @source http://purl.eligrey.com/github/FileSaver.js/blob/master/FileSaver.js */
  50.  
  51. var saveAs = saveAs || (function(view) {
  52. "use strict";
  53. // IE <10 is explicitly unsupported
  54. if (typeof view === "undefined" || typeof navigator !== "undefined" && /MSIE [1-9]\./.test(navigator.userAgent)) {
  55. return;
  56. }
  57. var
  58. doc = view.document
  59. // only get URL when necessary in case Blob.js hasn't overridden it yet
  60. ,
  61. get_URL = function() {
  62. return view.URL || view.webkitURL || view;
  63. },
  64. save_link = doc.createElementNS("http://www.w3.org/1999/xhtml", "a"),
  65. can_use_save_link = "download" in save_link,
  66. click = function(node) {
  67. var event = new MouseEvent("click");
  68. node.dispatchEvent(event);
  69. },
  70. is_safari = /constructor/i.test(view.HTMLElement),
  71. is_chrome_ios = /CriOS\/[\d]+/.test(navigator.userAgent),
  72. throw_outside = function(ex) {
  73. (view.setImmediate || view.setTimeout)(function() {
  74. throw ex;
  75. }, 0);
  76. },
  77. force_saveable_type = "application/octet-stream"
  78. // the Blob API is fundamentally broken as there is no "downloadfinished" event to subscribe to
  79. ,
  80. arbitrary_revoke_timeout = 1000 * 40 // in ms
  81. ,
  82. revoke = function(file) {
  83. var revoker = function() {
  84. if (typeof file === "string") { // file is an object URL
  85. get_URL().revokeObjectURL(file);
  86. } else { // file is a File
  87. file.remove();
  88. }
  89. };
  90. setTimeout(revoker, arbitrary_revoke_timeout);
  91. },
  92. dispatch = function(filesaver, event_types, event) {
  93. event_types = [].concat(event_types);
  94. var i = event_types.length;
  95. while (i--) {
  96. var listener = filesaver["on" + event_types[i]];
  97. if (typeof listener === "function") {
  98. try {
  99. listener.call(filesaver, event || filesaver);
  100. } catch (ex) {
  101. throw_outside(ex);
  102. }
  103. }
  104. }
  105. },
  106. auto_bom = function(blob) {
  107. // prepend BOM for UTF-8 XML and text/* types (including HTML)
  108. // note: your browser will automatically convert UTF-16 U+FEFF to EF BB BF
  109. if (/^\s*(?:text\/\S*|application\/xml|\S*\/\S*\+xml)\s*;.*charset\s*=\s*utf-8/i.test(blob.type)) {
  110. return new Blob([String.fromCharCode(0xFEFF), blob], { type: blob.type });
  111. }
  112. return blob;
  113. },
  114. FileSaver = function(blob, name, no_auto_bom) {
  115. if (!no_auto_bom) {
  116. blob = auto_bom(blob);
  117. }
  118. // First try a.download, then web filesystem, then object URLs
  119. var
  120. filesaver = this,
  121. type = blob.type,
  122. force = type === force_saveable_type,
  123. object_url, dispatch_all = function() {
  124. dispatch(filesaver, "writestart progress write writeend".split(" "));
  125. }
  126. // on any filesys errors revert to saving with object URLs
  127. ,
  128. fs_error = function() {
  129. if ((is_chrome_ios || (force && is_safari)) && view.FileReader) {
  130. // Safari doesn't allow downloading of blob urls
  131. var reader = new FileReader();
  132. reader.onloadend = function() {
  133. var url = is_chrome_ios ? reader.result : reader.result.replace(/^data:[^;]*;/, 'data:attachment/file;');
  134. var popup = view.open(url, '_blank');
  135. if (!popup) view.location.href = url;
  136. url = undefined; // release reference before dispatching
  137. filesaver.readyState = filesaver.DONE;
  138. dispatch_all();
  139. };
  140. reader.readAsDataURL(blob);
  141. filesaver.readyState = filesaver.INIT;
  142. return;
  143. }
  144. // don't create more object URLs than needed
  145. if (!object_url) {
  146. object_url = get_URL().createObjectURL(blob);
  147. }
  148. if (force) {
  149. view.location.href = object_url;
  150. } else {
  151. var opened = view.open(object_url, "_blank");
  152. if (!opened) {
  153. // Apple does not allow window.open, see https://developer.apple.com/library/safari/documentation/Tools/Conceptual/SafariExtensionGuide/WorkingwithWindowsandTabs/WorkingwithWindowsandTabs.html
  154. view.location.href = object_url;
  155. }
  156. }
  157. filesaver.readyState = filesaver.DONE;
  158. dispatch_all();
  159. revoke(object_url);
  160. };
  161. filesaver.readyState = filesaver.INIT;
  162.  
  163. if (can_use_save_link) {
  164. object_url = get_URL().createObjectURL(blob);
  165. setTimeout(function() {
  166. save_link.href = object_url;
  167. save_link.download = name;
  168. click(save_link);
  169. dispatch_all();
  170. revoke(object_url);
  171. filesaver.readyState = filesaver.DONE;
  172. });
  173. return;
  174. }
  175.  
  176. fs_error();
  177. },
  178. FS_proto = FileSaver.prototype,
  179. saveAs = function(blob, name, no_auto_bom) {
  180. return new FileSaver(blob, name || blob.name || "download", no_auto_bom);
  181. };
  182. // IE 10+ (native saveAs)
  183. if (typeof navigator !== "undefined" && navigator.msSaveOrOpenBlob) {
  184. return function(blob, name, no_auto_bom) {
  185. name = name || blob.name || "download";
  186.  
  187. if (!no_auto_bom) {
  188. blob = auto_bom(blob);
  189. }
  190. return navigator.msSaveOrOpenBlob(blob, name);
  191. };
  192. }
  193.  
  194. FS_proto.abort = function() {};
  195. FS_proto.readyState = FS_proto.INIT = 0;
  196. FS_proto.WRITING = 1;
  197. FS_proto.DONE = 2;
  198.  
  199. FS_proto.error =
  200. FS_proto.onwritestart =
  201. FS_proto.onprogress =
  202. FS_proto.onwrite =
  203. FS_proto.onabort =
  204. FS_proto.onerror =
  205. FS_proto.onwriteend =
  206. null;
  207.  
  208. return saveAs;
  209. }(
  210. typeof self !== "undefined" && self ||
  211. typeof window !== "undefined" && window ||
  212. this.content
  213. ));
  214. // `self` is undefined in Firefox for Android content script context
  215. // while `this` is nsIContentFrameMessageManager
  216. // with an attribute `content` that corresponds to the window
  217.  
  218. window.saveAs = saveAs;
  219. })();
  220.  
  221. function getURL() {
  222. // var firstAndLastNode = Array.prototype.slice.call($('ul.FnSticker_animation_list_img li:first-child span,ul.FnSticker_animation_list_img li:last-child span'));
  223.  
  224. var firstAndLastNode = Array.prototype.slice.call($('ul.mdCMN09Ul li:first-child span,ul.mdCMN09Ul li:last-child span'));
  225. var prefix = ""; //url前缀
  226. var extension = ".png";
  227. // console.log(firstAndLastNode);
  228. firstAndLastNode.forEach(function(value, index, array) {
  229. var backgroundImage = value.style.backgroundImage;
  230. // lastIndex = backgroundImage.lastIndexOf('/');
  231.  
  232. var patern = new RegExp(/url\("(.*\/)(\d+)\/.*\/.*(\.[a-z]+);/);
  233. var r = patern.exec(backgroundImage);
  234. //前缀
  235. prefix = r[1]; //前缀
  236. //id
  237. firstAndLastNode[index] = r[2];
  238. //扩展名
  239. extension = r[3];
  240. });
  241. if (firstAndLastNode.length <= 1) {
  242. console.error('No Matched Nodes');
  243. return;
  244. }
  245. var patern = new RegExp(/.*\/(\d+)\/.*\/.*(\.[a-z]+)/);
  246.  
  247.  
  248. return {
  249. first: Number.parseInt(firstAndLastNode[0]),
  250. last: Number.parseInt(firstAndLastNode[1]),
  251. prefix: prefix,
  252. extension: extension
  253. };
  254. }
  255.  
  256. function download(url, callback) {
  257. console.log('Start download!');
  258. var xhr = new XMLHttpRequest();
  259. xhr.onreadystatechange = function() {
  260. if (this.readyState == 4 && this.status == 200) {
  261. //this.response is what you're looking for
  262. callback(this.response);
  263. }
  264. };
  265. xhr.open('GET', url);
  266. xhr.responseType = 'blob';
  267. xhr.send();
  268. }
  269. var urls = getURL();
  270. if (!urls) {
  271. return;
  272. }
  273.  
  274. function selectSingle(xpath_exp) {
  275. return document.evaluate(xpath_exp, document, null,
  276. XPathResult.ANY_TYPE,
  277. null).iterateNext();
  278. }
  279. //is Downloading
  280. var isDownloading = false;
  281.  
  282. //appendButton
  283. $('body').prepend(buttonHtml);
  284. var title = selectSingle('//*[@id=\"FnStickerDetail\"]/div[1]/div[1]/div[2]/h3').textContent;
  285.  
  286. $('#line-sticker-downloader-button').click(function(event) {
  287. zip = new JSZip();
  288. stickers = zip.folder("stickers");
  289. imgs = [];
  290.  
  291. if (isDownloading) return;
  292. var $button = $(this);
  293. isDownloading = true;
  294.  
  295. $button.find('.mdBtn01Txt').text('下载中');
  296. (Promise.resolve(urls))
  297. .then(function(ids) {
  298. if (ids.first > ids.last) {
  299. var temp = first;
  300. first = last;
  301. last = temp;
  302. }
  303. var promiseArray = [];
  304. for (var i = ids.first; i <= ids.last; i++) {
  305. // console.log(ids.prefix + i + "/android/sticker.png");
  306. (function(i) {
  307. var promise = (new Promise(function(resolve, reject) {
  308. download(ids.prefix + i + "/android/sticker.png", function(blob) {
  309. resolve(blob);
  310. });
  311. }))
  312. .then(function(blob) {
  313. imgs.push([i, blob]);
  314. })
  315. .catch(function(err) {
  316. console.log(err);
  317. });
  318. promiseArray.push(promise);
  319. })(i);
  320. }
  321. return Promise.all(promiseArray);
  322. })
  323. .then(function() {
  324. for (var v in imgs) {
  325. stickers.file(imgs[v][0] + '.png', imgs[v][1], { binary: true });
  326. }
  327. try {
  328. console.log('Packing stickers png file to zip...');
  329. zip.generateAsync({ type: "blob" }).then(function(content) {
  330. var title = selectSingle('//*[@id=\"FnStickerDetail\"]/div[1]/div[1]/div[2]/h3').textContent;
  331. console.log('Save as ' + title, zip + '.zip');
  332. saveAs(content, title + ".zip");
  333. });
  334. } catch (error) {
  335. console.log(error);
  336. }
  337. $button.find('.mdBtn01Txt').text('打包完毕');
  338. setTimeout(function() {
  339. $button.find('.mdBtn01Txt').text('打包贴图');
  340. }, 3000);
  341. })
  342. .catch(function(err) {
  343. $button.find('.mdBtn01Txt').text('下载错误!');
  344. console.log(err);
  345. })
  346. .then(function() {
  347. isDownloading = false;
  348. });
  349.  
  350. });
  351.  
  352. })(jQuery);