Greasy Fork is available in English.

批量导入花瓣图片到 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.

// ==UserScript==
// @name                批量导入花瓣图片到 Eagle
// @name:zh             批量导入花瓣图片到 Eagle
// @name:zh-TW          批次導入花瓣圖片到 Eagle
// @name:ja-JP          花瓣の画像を Eagle に保存

// @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.
// @description:zh      请确保你的网路环境可以正常访问 花瓣,如果设备网路无法访问,此脚本将无法正常运作。在 花瓣 画版页面启动脚本,此脚本会自动滚动页面,将页面中所有图片转换成大图(包含链接、名称),添加至 Eagle App。
// @description:zh-TW   在 花瓣 畫版頁面啓動腳本,此腳本會自動滾動頁面,將頁面中所有圖片轉換成大圖(包含鏈接、名稱),添加至 Eagle App。
// @description:ja-JP   花瓣のボードページ上でスクリプトを起動すると、ページが自動的にスクロールし、ページ上のすべての画像を大きな画像(リンク、名前付き)に変換して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://huaban.com/*
// @grant               GM_xmlhttpRequest
// @connect             localhost
// @connect             127.0.0.1
// @run-at              context-menu

// @date                01/27/2022
// @modified            05/31/2022
// @version             0.0.7

// ==/UserScript==

(function() {

    if (location.href.indexOf("huaban.") === -1) {
        alert("此脚本只能在花瓣网运行");
        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`;

    let SELECTOR_IMAGE;
    let SELECTOR_LINK;
    let SELECTOR_NODATA;
    let SELECTOR_BOX;

    // 新版
    if (document.querySelector(".infinite-scroll-component")) {
        SELECTOR_IMAGE = `.infinite-scroll-component a[href*='pins'] img`;
        SELECTOR_LINK = `.infinite-scroll-component a[href*='pins']`;
        SELECTOR_NODATA = `.infinite-scroll-component .noMore`;
        SELECTOR_BOX = `[data-pin-id]`;
    }
    // 旧版
    else {
        SELECTOR_IMAGE = `#waterfall a.img img`;
        SELECTOR_LINK = `#waterfall a.img`;
        SELECTOR_NODATA = `img[src="/img/end.png"]`;
        SELECTOR_BOX = `[data-id]`;
    }

    var startTime = Date.now();     // 开始滚动时间
    var scrollInterval;             // 无限滚动,直到底部
    var lastScrollPos;              // 上一次滚轴位置
    var retryCount = 0;             // 目前重试次数
    var scrollDelay = 100;          // 滚动页面延迟
    var retryThreshold = 20;         // 无法滚动页面重试次数,当超过次数,表示到底部了
    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, window.scrollY + 125);
        // 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 "";
        };

        var getTitle = function(img) {
            var gridItem = img.closest(SELECTOR_BOX);
            if (img.alt) {
                return img.alt;
            }
            if (gridItem && gridItem.textContent) {
                return gridItem.textContent;
            }
            return "";
        };

        imgs = imgElements.map(function(elem, index) {
            pageInfo.imageCount++;
            let src = getHighestResImg(elem) || elem.src;
            src += `?v=${Date.now()}`; // hack 💀
            return {
                name: getTitle(elem),
                url: src, // 取得最大分辨率
                website: getLink(elem), // 取得图片链接
                modificationTime: startTime - pageInfo.imageCount // 强制设置时间,确保在 Eagle 顺序与 花瓣 相同
            }
        });

        return imgs;
    };

    // 滚动页面并取得图片信息,发送至 Eagle App
    var fetchImages = function() {
        var currentScrollPos = window.scrollY;
        scarollToBottom();

        addImagesToEagle(getImgs());

        // 到底了
        if (lastScrollPos === currentScrollPos || currentScrollPos === 0) {
            // 画面如果出现 Spinner 表示后面还有内容尚未载入完成
            if (document.querySelector(SELECTOR_NODATA)) {
                retryCount++;
                console.log(retryCount)
                if (retryCount >= retryThreshold) {
                    clearInterval(scrollInterval);
                    alert(`添加完成,一共添加了 ${pageInfo.imageCount} 张图像。`);
                }
            }
        }
        // 还有内容
        else {
            retryCount = 0;
            var images = getImgs();
            addImagesToEagle(images);
        }
    }

    // 将图片添加至 Eagle
    var addImagesToEagle = function(images) {
        if (!images || images.length === 0) return;
        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) {
        var src = element.currentSrc || element.src;
        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];
    }

    // 脚本开始
    scarollToTop();

    // 创建本次保存使用文件夹
    var folderName = document.querySelector("title").text;
    createFolder(folderName, function(err, folder) {
        if (folder) {
            // 持续滚动列表,直到列表没有更多内容
            pageInfo.folderId = folder.id;
            scrollInterval = setInterval(fetchImages, scrollDelay);
        } else {
            alert("软件尚未打开,或当前软件版本不支持,需至 Eagle 官网下载,手动重新安装最新版本");
        }
    });

})();