关于油猴脚本
范例:使用油猴脚本及 Eagle API,开发 Pinterest 批量导入扩展
跨域访问 CORS 问题
如果你想要透过「油猴」等脚本扩展在网页中使用 Eagle API,请使用 GM_xmlhttpRequest
方法进行呼叫,即可避免跨域安全限制的问题。
GM_xmlhttpRequest({
url: EAGLE_IMPORT_API_URL,
method: "POST",
data: JSON.stringify({ items: images, folderId: pageInfo.folderId }),
onload: function(response) {}
});
完整代码:
// ==UserScript==
// @name Save Pinterest images to Eagle
// @name:zh-CN 批量导入 Pinterest 图片到 Eagle
// @name:zh-TW 批次導入 Pinterest 圖片到 Eagle
// @name:ja-JP Pinterestの画像を Eagle に保存
// @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.
// @description:zh-CN 请确保你的网路环境可以正常访问 Pinterest,如果设备网路无法访问,此脚本将无法正常运作。在 Pinterest 画版页面启动脚本,此脚本会自动滚动页面,将页面中所有图片转换成大图(包含链接、名称),添加至 Eagle App。
// @description:zh-TW 在 Pinterest 畫版頁面啓動腳本,此腳本會自動滾動頁面,將頁面中所有圖片轉換成大圖(包含鏈接、名稱),添加至 Eagle App。
// @description:ja-JP Pinterestのボードページ上でスクリプトを起動すると、ページが自動的にスクロールし、ページ上のすべての画像を大きな画像(リンク、名前付き)に変換してEagleアプリに追加することができます。
// @author Augus
// @namespace https://eagle.cool/
// @homepageURL https://eagle.cool/
// @supportURL https://docs-cn.eagle.cool/
// @icon https://cn.eagle.cool/favicon.png
// @license MIT License
// @match https://www.pinterest.com/*
// @grant GM_xmlhttpRequest
// @run-at context-menu
// @date 06/16/2020
// @modified 06/16/2020
// @version 0.0.3
// ==/UserScript==
(function() {
if (location.href.indexOf("pinterest.com") === -1) {
alert("This script only works on pinterest.com.");
return;
}
// Eagle API 服务器位置
const EAGLE_SERVER_URL = "http://localhost:41595";
const EAGLE_IMPORT_API_URL = `${EAGLE_SERVER_URL}/api/item/addFromURLs`;
const EAGLE_CREATE_FOLDER_API_URL = `${EAGLE_SERVER_URL}/api/folder/create`;
// Pinterest 当前图片、链接命名规则
const SELECTOR_IMAGE = "[data-grid-item] a img[srcset]";
const SELECTOR_LINK = "[data-grid-item] a";
const SELECTOR_SPINNER = `[aria-label="Board Pins grid"]`;
var startTime = Date.now(); // 开始滚动时间
var scrollInterval; // 无限滚动,直到底部
var lastScrollPos; // 上一次滚轴位置
var retryCount = 0; // 目前重试次数
var scrollDelay = 500; // 滚动页面延迟
var retryThreshold = 4; // 无法滚动页面重试次数,当超过次数,表示到底部了
var pageInfo = {
imageCount: 0,
imageSet: {},
folderId: ""
};
// 创建文件夹
var createFolder = function(folderName, callback) {
GM_xmlhttpRequest({
url: EAGLE_CREATE_FOLDER_API_URL,
method: "POST",
data: JSON.stringify({ folderName: folderName }),
onload: function(response) {
try {
var result = JSON.parse(response.response);
if (result.status === "success" && result.data && result.data.id) {
callback(undefined, result.data);
} else {
callback(true);
}
} catch (err) {
callback(true);
}
}
});
};
// 滚动至页面顶端
var scarollToTop = function() {
window.scrollTo(0, 0);
lastScrollPos = window.scrollY;
};
// 滚动至页面底端
var scarollToBottom = function() {
window.scrollTo(0, document.body.scrollHeight);
// window.scrollTo(0, window.innerHeight);
lastScrollPos = window.scrollY;
};
// 取得当前画面所有图片链接
var getImgs = function() {
var imgs = [];
var imgElements = Array.from(document.querySelectorAll(SELECTOR_IMAGE));
// 避免重复添加
imgElements = imgElements.filter(function(elem) {
var src = elem.src;
if (!pageInfo.imageSet[src]) {
pageInfo.imageSet[src] = true;
return true;
}
return false;
});
var getLink = function(img) {
var links = Array.from(document.querySelectorAll(SELECTOR_LINK));
for (var i = 0; i < links.length; i++) {
if (links[i].contains(img)) {
return absolutePath(links[i].href);
}
}
return "";
};
imgs = imgElements.map(function(elem, index) {
pageInfo.imageCount++;
return {
name: elem.alt || "",
url: getHighestResImg(elem) || elem.src, // 取得最大分辨率
website: getLink(elem), // 取得图片链接
modificationTime: startTime - pageInfo.imageCount // 强制设置时间,确保在 Eagle 顺序与 Pinterest 相同
}
});
return imgs;
};
// 滚动页面并取得图片信息,发送至 Eagle App
var fetchImages = function() {
var currentScrollPos = window.scrollY;
scarollToBottom();
// 到底了
if (lastScrollPos === currentScrollPos) {
// 画面如果出现 Spinner 表示后面还有内容尚未载入完成
if (!document.querySelector(SELECTOR_SPINNER)) {
retryCount++;
if (retryCount >= retryThreshold) {
clearInterval(scrollInterval);
alert(`Scan completed, a total of ${pageInfo.imageCount} images have been added to Eagle App.`);
}
}
}
// 还有内容
else {
retryCount = 0;
var images = getImgs();
addImagesToEagle(images);
}
}
// 将图片添加至 Eagle
var addImagesToEagle = function(images) {
GM_xmlhttpRequest({
url: EAGLE_IMPORT_API_URL,
method: "POST",
data: JSON.stringify({ items: images, folderId: pageInfo.folderId }),
onload: function(response) {}
});
}
function absolutePath(href) {
if (href && href.indexOf(" ") > -1) {
href = href.trim().split(" ")[0];
}
var link = document.createElement("a");
link.href = href;
return link.href;
}
function getHighestResImg(element) {
if (element.getAttribute('srcset')) {
let highResImgUrl = '';
let maxRes = 0;
let imgWidth, urlWidthArr;
element.getAttribute('srcset').split(',').forEach((item) => {
urlWidthArr = item.trim().split(' ');
imgWidth = parseInt(urlWidthArr[1]);
if (imgWidth > maxRes) {
maxRes = imgWidth;
highResImgUrl = urlWidthArr[0];
}
});
return highResImgUrl;
} else {
return element.getAttribute('src');
}
}
// 脚本开始
scarollToTop();
// 创建本次保存使用文件夹
var folderName = document.querySelector("h1") && document.querySelector("h1").innerText || "Pinterest";
createFolder(folderName, function(err, folder) {
if (folder) {
// 持续滚动列表,直到列表没有更多内容
pageInfo.folderId = folder.id;
scrollInterval = setInterval(fetchImages, 1000);
} else {
alert("An error has occurred or the Eagle app is not open.");
}
});
})();