Greasy Fork 还支持 简体中文。

Fanbox Image Downloader

Add download button to Fanbox image, and click to download the original image named by format.

  1. // ==UserScript==
  2. // @name Fanbox Image Downloader
  3. // @namespace TypeNANA
  4. // @version 0.5
  5. // @description Add download button to Fanbox image, and click to download the original image named by format.
  6. // @author HY
  7. // @match *.fanbox.cc/*
  8. // @include *.fanbox.cc/*
  9. // @require http://code.jquery.com/jquery-3.3.1.min.js
  10. // @grant GM_xmlhttpRequest
  11. // ==/UserScript==
  12.  
  13. (function () {
  14. /** Edit defaultFileName to change the file name format
  15. *
  16. * <%Userid> Fanbox user ID. eg: shiratamaco
  17. * <%Postid> Fanbox post ID. eg: 1459770
  18. * <%Time> Current timestamp. eg: 1550557810891
  19. * <%PicName> Original pic name. eg: 6tpWcFZjqFmc1faOs4TraugL
  20. * <%PicNo> Ordinal number of pic. eg: 0
  21. *
  22. * default: "fanbox <%Userid> <%Postid>_p<%PicNo>"
  23. * result: "fanbox shiratamaco 1459770_p0.jpg"
  24. *
  25. * example1: "<%Userid> <%Postid> <%PicName>”
  26. * result: "shiratamaco 1459770 6tpWcFZjqFmc1faOs4TraugL.jpg"
  27. *
  28. * example2: "<%Postid>_p<%PicNo>”
  29. * result: "1459770_p0.jpg"
  30. */
  31. let defaultFileName = "fanbox <%Userid> <%Postid>_p<%PicNo>";
  32.  
  33. function download(url, name) {
  34. GM_xmlhttpRequest({
  35. method: "GET",
  36. url: url,
  37. binary: true,
  38. responseType: "blob",
  39. onload: function (response) {
  40. downloadFile(name, response.response)
  41. },
  42. onerror: function (e) {
  43. console.log("failed. cause:", e)
  44. },
  45. });
  46. }
  47.  
  48. function downloadFile(fileName, blob) {
  49. //通过a标签的download属性来下载指定文件名的文件
  50. let anchor = getDlBtn();
  51. let src = URL.createObjectURL(blob);
  52. anchor.download = fileName;
  53. anchor.href = src;
  54. anchor.click();
  55. }
  56.  
  57. function getDlBtn() {
  58. if (document.getElementsByClassName("img-link").length == 0) {
  59. let btnDownloadImg = document.createElement('A');
  60. btnDownloadImg.className = 'img-link';
  61. document.getElementById("root").appendChild(btnDownloadImg);
  62. }
  63. return document.getElementsByClassName("img-link")[0];
  64. }
  65.  
  66. function setBtn(v, index) {
  67. if (v == null || v.length == 0) return;
  68. let target = v[0];
  69. if (target == null) return;
  70. if (target.parentElement.getElementsByClassName("dl_btn_div").length > 0) return;
  71. if (target.getElementsByTagName("img").length <= 0 && target.getElementsByTagName("img")[0].src == null) return;
  72.  
  73. let pageUrl = document.location.href.split("?")[0];
  74. let picUrl = target.href;
  75.  
  76. let dlbtn = document.createElement('DIV');
  77. target.parentElement.appendChild(dlbtn);
  78. dlbtn.outerHTML = '<div class="dl_btn_div" style="cursor: pointer;display: table;font-size: 15px;color: white;position: absolute;right: 5px;bottom: 5px;background: #0000007f;height: 30px;width: 30px;border-radius: 15px;text-align: center;"><svg class="icon" style="width: 15px;height: 15px;vertical-align: top;display: inline-block;margin-top: 7px;fill: currentColor;overflow: hidden;" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3658"><path d="M925.248 356.928l-258.176-258.176a64 64 0 0 0-45.248-18.752H144a64 64 0 0 0-64 64v736a64 64 0 0 0 64 64h736a64 64 0 0 0 64-64V402.176a64 64 0 0 0-18.752-45.248zM288 144h192V256H288V144z m448 736H288V736h448v144z m144 0H800V704a32 32 0 0 0-32-32H256a32 32 0 0 0-32 32v176H144v-736H224V288a32 32 0 0 0 32 32h256a32 32 0 0 0 32-32V144h77.824l258.176 258.176V880z" p-id="3659"></path></svg></div>';
  79. dlbtn = target.parentElement.getElementsByClassName("dl_btn_div")[0]
  80.  
  81. //获取文件名
  82. // https://downloads.fanbox.cc/images/post/1459770/6tpWcFZjqFmc1faOs4TraugL.png
  83. let nameArr = picUrl.split("/");
  84. let picName = nameArr[nameArr.length - 1];
  85. let dl_picname = picName.split(".")[0];
  86. let dl_time = new Date().getTime();
  87.  
  88. //获取图片编号
  89. // https://www.fanbox.cc/@shiratamaco/posts/1459770
  90. let dl_userid;
  91. if(pageUrl.includes("@")){
  92. dl_userid = pageUrl.split("@")[1].split("/")[0];
  93. }else{
  94. dl_userid = pageUrl.split("://")[1].split(".")[0];
  95. }
  96. let dl_tid = pageUrl.split("/")[pageUrl.split("/").length - 1];
  97. let dl_picno = index;
  98. //替换内容,拼接文件名
  99. let dl_filename = defaultFileName
  100. .replace("<%Userid>", dl_userid)
  101. .replace("<%Postid>", dl_tid)
  102. .replace("<%Time>", dl_time)
  103. .replace("<%PicName>", dl_picname)
  104. .replace("<%PicNo>", dl_picno);
  105. //获取拓展名,推特只存在.jpg和.png格式的图片,故偷个懒不做正则判断
  106. let dl_ext = picUrl.split(".")[picUrl.split(".").length - 1];
  107.  
  108. dlbtn.addEventListener('touchstart', function (e) {
  109. dlbtn.onclick = function (e) {
  110. return false;
  111. }
  112. return false;
  113. });
  114. dlbtn.addEventListener('mousedown', function (e) {
  115. dlbtn.onclick = function (e) {
  116. return false;
  117. }
  118. return false;
  119. });
  120. dlbtn.addEventListener('click', function (e) {
  121. //调用下载方法
  122. cancelBubble(e);
  123. download(picUrl, dl_filename + "." + dl_ext);
  124. return false;
  125. });
  126.  
  127. return false;
  128. }
  129.  
  130. function findFirstA(node) {
  131. var tmp = node;
  132. for (var i = 0; i < 20; i++) {
  133. tmp = tmp.parentElement
  134. if (tmp == null) return null;
  135. if (tmp.nodeName == "a" || tmp.nodeName == "A") {
  136. return tmp
  137. }
  138. }
  139. }
  140. function findFirstLi(node) {
  141. var tmp = node;
  142. for (var i = 0; i < 20; i++) {
  143. tmp = tmp.parentElement
  144. if (tmp == null) return null;
  145. if (tmp.nodeName == "li" || tmp.nodeName == "LI") {
  146. return tmp
  147. }
  148. }
  149. }
  150. function cancelBubble(e) {
  151. var evt = e ? e : window.event;
  152. if (evt.stopPropagation) {
  153. evt.stopPropagation();
  154. } else {
  155. evt.cancelBubble = true;
  156. }
  157. }
  158.  
  159. function waitForKeyElements(
  160. selectorTxt,
  161. actionFunction,
  162. bWaitOnce
  163. ) {
  164. var targetNodes, btargetsFound;
  165.  
  166. targetNodes = $(selectorTxt);
  167.  
  168. if (targetNodes && targetNodes.length > 0) {
  169. btargetsFound = true;
  170. targetNodes.each(function (i, v) {
  171. var jThis = $(this);
  172. var alreadyFound = jThis.data('alreadyFound') || false;
  173.  
  174. if (!alreadyFound) {
  175. var cancelFound = actionFunction(jThis, i);
  176. if (cancelFound) {
  177. btargetsFound = false;
  178. } else {
  179. jThis.data('alreadyFound', true);
  180. }
  181. }
  182. });
  183. } else {
  184. btargetsFound = false;
  185. }
  186.  
  187. var controlObj = waitForKeyElements.controlObj || {};
  188. var controlKey = selectorTxt.replace(/[^\w]/g, "_");
  189. var timeControl = controlObj[controlKey];
  190.  
  191. if (btargetsFound && bWaitOnce && timeControl) {
  192. clearInterval(timeControl);
  193. delete controlObj[controlKey]
  194. } else {
  195. if (!timeControl) {
  196. timeControl = setInterval(function () {
  197. waitForKeyElements(selectorTxt,
  198. actionFunction,
  199. bWaitOnce
  200. );
  201. }, 300);
  202. controlObj[controlKey] = timeControl;
  203. }
  204. }
  205. waitForKeyElements.controlObj = controlObj;
  206. }
  207.  
  208. waitForKeyElements('article a[href^="https://downloads.fanbox.cc/images/post"]', setBtn);
  209. })();