批量导入花瓣图片到 Eagle

Launch a script on Huaban that automatically scrolls the page and converts all images on the page into large images (with links, names) to be added to the Eagle App.

  1. // ==UserScript==
  2. // @name 批量导入花瓣图片到 Eagle
  3. // @name:zh 批量导入花瓣图片到 Eagle
  4. // @name:zh-TW 批次導入花瓣圖片到 Eagle
  5. // @name:ja-JP 花瓣の画像を Eagle に保存
  6.  
  7. // @description Launch a script on Huaban that automatically scrolls the page and converts all images on the page into large images (with links, names) to be added to the Eagle App.
  8. // @description:zh 请确保你的网路环境可以正常访问 花瓣,如果设备网路无法访问,此脚本将无法正常运作。在 花瓣 画版页面启动脚本,此脚本会自动滚动页面,将页面中所有图片转换成大图(包含链接、名称),添加至 Eagle App。
  9. // @description:zh-TW 在 花瓣 畫版頁面啓動腳本,此腳本會自動滾動頁面,將頁面中所有圖片轉換成大圖(包含鏈接、名稱),添加至 Eagle App。
  10. // @description:ja-JP 花瓣のボードページ上でスクリプトを起動すると、ページが自動的にスクロールし、ページ上のすべての画像を大きな画像(リンク、名前付き)に変換してEagleアプリに追加することができます。
  11.  
  12. // @author Augus
  13. // @namespace https://eagle.cool/
  14. // @homepageURL https://eagle.cool/
  15. // @supportURL https://docs-cn.eagle.cool/
  16. // @icon https://cn.eagle.cool/favicon.png
  17. // @license MIT License
  18.  
  19. // @match https://huaban.com/*
  20. // @grant GM_xmlhttpRequest
  21. // @connect localhost
  22. // @connect 127.0.0.1
  23. // @run-at context-menu
  24.  
  25. // @date 01/27/2022
  26. // @modified 05/31/2022
  27. // @version 0.0.7
  28.  
  29. // ==/UserScript==
  30.  
  31. (function() {
  32.  
  33. if (location.href.indexOf("huaban.") === -1) {
  34. alert("此脚本只能在花瓣网运行");
  35. return;
  36. }
  37.  
  38. // Eagle API 服务器位置
  39. const EAGLE_SERVER_URL = "http://localhost:41595";
  40. const EAGLE_IMPORT_API_URL = `${EAGLE_SERVER_URL}/api/item/addFromURLs`;
  41. const EAGLE_CREATE_FOLDER_API_URL = `${EAGLE_SERVER_URL}/api/folder/create`;
  42.  
  43. let SELECTOR_IMAGE;
  44. let SELECTOR_LINK;
  45. let SELECTOR_NODATA;
  46. let SELECTOR_BOX;
  47.  
  48. // 新版
  49. if (document.querySelector(".infinite-scroll-component")) {
  50. SELECTOR_IMAGE = `.infinite-scroll-component a[href*='pins'] img`;
  51. SELECTOR_LINK = `.infinite-scroll-component a[href*='pins']`;
  52. SELECTOR_NODATA = `.infinite-scroll-component .noMore`;
  53. SELECTOR_BOX = `[data-pin-id]`;
  54. }
  55. // 旧版
  56. else {
  57. SELECTOR_IMAGE = `#waterfall a.img img`;
  58. SELECTOR_LINK = `#waterfall a.img`;
  59. SELECTOR_NODATA = `img[src="/img/end.png"]`;
  60. SELECTOR_BOX = `[data-id]`;
  61. }
  62.  
  63. var startTime = Date.now(); // 开始滚动时间
  64. var scrollInterval; // 无限滚动,直到底部
  65. var lastScrollPos; // 上一次滚轴位置
  66. var retryCount = 0; // 目前重试次数
  67. var scrollDelay = 100; // 滚动页面延迟
  68. var retryThreshold = 20; // 无法滚动页面重试次数,当超过次数,表示到底部了
  69. var pageInfo = {
  70. imageCount: 0,
  71. imageSet: {},
  72. folderId: ""
  73. };
  74.  
  75. // 创建文件夹
  76. var createFolder = function(folderName, callback) {
  77. GM_xmlhttpRequest({
  78. url: EAGLE_CREATE_FOLDER_API_URL,
  79. method: "POST",
  80. data: JSON.stringify({ folderName: folderName }),
  81. onload: function(response) {
  82. try {
  83. var result = JSON.parse(response.response);
  84. if (result.status === "success" && result.data && result.data.id) {
  85. callback(undefined, result.data);
  86. } else {
  87. callback(true);
  88. }
  89. } catch (err) {
  90. callback(true);
  91. }
  92. }
  93. });
  94. };
  95.  
  96. // 滚动至页面顶端
  97. var scarollToTop = function() {
  98. window.scrollTo(0, 0);
  99. lastScrollPos = window.scrollY;
  100. };
  101.  
  102. // 滚动至页面底端
  103. var scarollToBottom = function() {
  104. window.scrollTo(0, window.scrollY + 125);
  105. // window.scrollTo(0, window.innerHeight);
  106. lastScrollPos = window.scrollY;
  107. };
  108.  
  109. // 取得当前画面所有图片链接
  110. var getImgs = function() {
  111. var imgs = [];
  112. var imgElements = Array.from(document.querySelectorAll(SELECTOR_IMAGE));
  113.  
  114. // 避免重复添加
  115. imgElements = imgElements.filter(function(elem) {
  116. var src = elem.src;
  117. if (!pageInfo.imageSet[src]) {
  118. pageInfo.imageSet[src] = true;
  119. return true;
  120. }
  121. return false;
  122. });
  123.  
  124. var getLink = function(img) {
  125. var links = Array.from(document.querySelectorAll(SELECTOR_LINK));
  126. for (var i = 0; i < links.length; i++) {
  127. if (links[i].contains(img)) {
  128. return absolutePath(links[i].href);
  129. }
  130. }
  131. return "";
  132. };
  133.  
  134. var getTitle = function(img) {
  135. var gridItem = img.closest(SELECTOR_BOX);
  136. if (img.alt) {
  137. return img.alt;
  138. }
  139. if (gridItem && gridItem.textContent) {
  140. return gridItem.textContent;
  141. }
  142. return "";
  143. };
  144.  
  145. imgs = imgElements.map(function(elem, index) {
  146. pageInfo.imageCount++;
  147. let src = getHighestResImg(elem) || elem.src;
  148. src += `?v=${Date.now()}`; // hack 💀
  149. return {
  150. name: getTitle(elem),
  151. url: src, // 取得最大分辨率
  152. website: getLink(elem), // 取得图片链接
  153. modificationTime: startTime - pageInfo.imageCount // 强制设置时间,确保在 Eagle 顺序与 花瓣 相同
  154. }
  155. });
  156.  
  157. return imgs;
  158. };
  159.  
  160. // 滚动页面并取得图片信息,发送至 Eagle App
  161. var fetchImages = function() {
  162. var currentScrollPos = window.scrollY;
  163. scarollToBottom();
  164.  
  165. addImagesToEagle(getImgs());
  166.  
  167. // 到底了
  168. if (lastScrollPos === currentScrollPos || currentScrollPos === 0) {
  169. // 画面如果出现 Spinner 表示后面还有内容尚未载入完成
  170. if (document.querySelector(SELECTOR_NODATA)) {
  171. retryCount++;
  172. console.log(retryCount)
  173. if (retryCount >= retryThreshold) {
  174. clearInterval(scrollInterval);
  175. alert(`添加完成,一共添加了 ${pageInfo.imageCount} 张图像。`);
  176. }
  177. }
  178. }
  179. // 还有内容
  180. else {
  181. retryCount = 0;
  182. var images = getImgs();
  183. addImagesToEagle(images);
  184. }
  185. }
  186.  
  187. // 将图片添加至 Eagle
  188. var addImagesToEagle = function(images) {
  189. if (!images || images.length === 0) return;
  190. GM_xmlhttpRequest({
  191. url: EAGLE_IMPORT_API_URL,
  192. method: "POST",
  193. data: JSON.stringify({ items: images, folderId: pageInfo.folderId }),
  194. onload: function(response) {}
  195. });
  196. }
  197.  
  198. function absolutePath(href) {
  199. if (href && href.indexOf(" ") > -1) {
  200. href = href.trim().split(" ")[0];
  201. }
  202. var link = document.createElement("a");
  203. link.href = href;
  204. return link.href;
  205. }
  206.  
  207. function getHighestResImg(element) {
  208. var src = element.currentSrc || element.src;
  209. return src.replace(/_\/fw(.*)/, '').replace(/_sq\d+\/format(.*)/, '').split('/format/')[0].replace(/_sq235$/, '').replace(/_sq75$/, '').replace(/_fw[\d]+[w]*$/, '').split("_fw")[0].split('/fw/')[0];
  210. }
  211.  
  212. // 脚本开始
  213. scarollToTop();
  214.  
  215. // 创建本次保存使用文件夹
  216. var folderName = document.querySelector("title").text;
  217. createFolder(folderName, function(err, folder) {
  218. if (folder) {
  219. // 持续滚动列表,直到列表没有更多内容
  220. pageInfo.folderId = folder.id;
  221. scrollInterval = setInterval(fetchImages, scrollDelay);
  222. } else {
  223. alert("软件尚未打开,或当前软件版本不支持,需至 Eagle 官网下载,手动重新安装最新版本");
  224. }
  225. });
  226.  
  227. })();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址