Save Pinterest images to Eagle

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

QingJ © 2025

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