Greasy Fork

Greasy Fork is available in English.

Bing Image Creator auto-download

Automatic image downloader for Bing Image Creator.

当前为 2024-11-09 提交的版本,查看 最新版本

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         Bing Image Creator auto-download
// @namespace    http://tampermonkey.net/
// @version      0.01
// @license      MIT
// @description  Automatic image downloader for Bing Image Creator.
// @match        https://copilot.microsoft.com/images/create*?*autosavetimer=*
// @grant        GM_download
// @require      http://code.jquery.com/jquery-3.7.1.min.js
// ==/UserScript==
//
// I just pasted this together from things found scattered around the internet.  Primarily: https://github.com/Emperorlou/MidJourneyTools
//
// To enable periodic downloading of newly-created images, go to a 'recent creations' page, and add "&autosavetimer=60" to the URL;
// something like: `https://www.bing.com/images/create/-/1234?autosavetimer=60`.
//
// This implementation is designed to be left unattended - periodically reloading itself.  If you click a link it will disable the script,
// unless you remove `?*autosavetimer=*` from `@match` above.

(function() {
    'use strict';

    const filename_prefix = "bing/";
    const downloadables = "img[src$='&pid=ImgGn']";
    const downloadInterval = 600;
    var pollRate = 60000;
    var activeDownloads = 0;
    var loadErrors = 0;
    var lastReload = 0;
    var sourceContent;

    $(document).ready(() => {
        var new_images = document.createElement("div");
        new_images.setAttribute("id", "scanbuffer");
        new_images.setAttribute("hidden", "");
        document.body.append(new_images);
        var dialog = document.createElement("dialog");
        dialog.setAttribute("id", "logmessage");
        dialog.setAttribute("style", "z-index: 100;");
        document.body.append(dialog);
        logger("Automatic image downloader is active.");

        // TODO: check for expired busyImage-* tags and retry them

        // TODO: Try to figure this out dynamically:
        sourceContent = location.href + " #girrc";

        var params = new URLSearchParams(window.location.search);
        pollRate = (params.get('autosavetimer') || 60) * 1000;
        setTimeout(reload, 1000);
        lastReload = Date.now();
        setInterval(function() {
            if (Date.now() - lastReload > pollRate * 5) {
                console.log("Reload function seems to have stopped.");
                reload();
            }
        }, pollRate * 1.5);
    });

    // sample: https://tse4.mm.bing.net/th?id=OIG2.AbCdEfGhIjKlMnOp123.&w=100&h=100&c=6&o=5&pid=ImgGn
    function get_img_id(src) {
        var url = new URL(src);
        var id = url.searchParams.get('id') || url.pathname.split('/').pop();
        if (id == null || id.length < 20) {
            console.log("couldn't parse image id from:", src, " got:", id);
        }
        return id;
    }

    // sample: /images/create/kebab-case-prompt/1-0123456789abcedf0123456789abcdef?FORM=GUH2CR
    //         https://copilot.microsoft.com/images/create?q=prompt%20with%20spaces&rt=4&FORM=GENCRE&id=1-0123456789abcedf0123456789abcdef
    function get_page_id(ref) {
        var url = new URL(ref);
        var id = url.searchParams.get('id') || url.searchParams.get('pageId');
        if (id == null) {
            var path = url.pathname.split('/');
            while (path.length && path.shift() != 'create')
                ;
            if (path.length == 2 && path[1].length >= 32) id = path[1];
        }
        if (id == null) {
            console.log("couldn't parse referrer id from:", ref);
        }
        return id;
    }

    // sample: /images/create/kebab-case-prompt/1-0123456789abcedf0123456789abcdef?FORM=GUH2CR
    function get_page_prompt(ref) {
        var url = new URL(ref);
        var q = url.searchParams.get('q');
        if (q == null) {
            var path = url.pathname.split('/');
            while (path.length && path.shift() != 'create')
                ;
            if (path.length == 2 && path[1].length >= 32) q = path[0];
        }
        if (q == null) {
            console.log("couldn't parse referrer prompt from:", ref);
        }
        return q;
    }

    function make_filename(img, src, ref) {
        var src_filename = get_img_id(src);
        var desc = get_page_prompt(ref) || img.getAttribute("alt", "image");
        var pageid = get_page_id(ref) || "page";

        return filename_prefix + src_filename + "_" + pageid + "_" + desc + ".jpg";
    }

    function logger(text) {
        var status = $("#logmessage")[0];
        if (text) {
            status.innerHTML += "<p>" + text + "</p>";
            status.show();
        } else {
            status.innerHTML = "";
            status.close();
        }
    }

    function reload() {
        logger("Rescanning...");
        if (activeDownloads > 0) {
            logger("There are " + activeDownloads + " outstanding.");
        }
        var target = $("#scanbuffer");
        var result = target.load(sourceContent, function(response, status, xhr) {
            var delay = 100;
            if ( status == "error" ) {
                console.log("problem loading content:", response, status, xhr);
                logger(null);
                if (loadErrors > 0) {
                    logger("previous failures: " + loadErrors);
                }
                logger("problem doing rescan: " + status + ": " + response);
                logger("xhr: " + xhr);
                loadErrors++;
            } else {
                loadErrors = 0;
                var allImages = $(target.find(downloadables).get().reverse());
                if (allImages.length < 10) {
                    console.log("Scan buffer doesn't have many images.  Is something wrong?");
                    console.log("all images:", $(target.find("img").get().reverse()));
                }
                for (const img of allImages) {
                    const src = get_download_url(img);
                    if (isUrlReady(src)) {
                        const ref = get_href(img) || "https://www.example.com/";
                        const filename = make_filename(img, src, ref);
                        downloadFile(delay, src, filename, ref);
                        delay += downloadInterval;
                    }
                }
                setTimeout(function() {
                    if (activeDownloads == 0) {
                        logger(null);
                    }
                }, 300);
            }
            setTimeout(reload, pollRate + delay);
        });
        lastReload = Date.now();
    }

    function downloadFile(delay, url, filename, referrer) {
        setUrlBusy(url, filename, referrer);
        logger("downloading: " + url + " as " + filename);
        logger("  referrer: " + referrer + ", in " + delay + "ms");
        setTimeout(function() {
            const download = GM_download({
                url: url,
                name: filename,
                saveAs: false,
                conflictAction: "uniquify",
                onload: function () {
                    setUrlSaved(url);
                },
                onerror: function () {
                    logger("error downloading: " + url);
                    clearUrlBusy(url);
                },
                ontimeout: function () {
                    logger("timeout downloading: " + url);
                    clearUrlBusy(url);
                }
            });
        }, delay);
    };

    function get_download_url(img) {
      var url = new URL(img.attributes.src.nodeValue);
      url.searchParams.delete("w");
      url.searchParams.delete("h");
      url.searchParams.delete("c");
      url.searchParams.delete("o");
      return url.href;
    }

    function get_href(elem) {
        while (elem) {
            if (elem.hasAttribute('href')) return elem.href;
            elem = elem.parentElement;
        }
        return null;
    }

    function mk_img_id(pfx, src) {
        return pfx + "-" + get_img_id(src);
    }

    function setUrlBusy(src, filename, referrer) {
        const id = mk_img_id("busyImage", src);
        sessionStorage.setItem(id, { "time": Date.now(), "src": src, "filename": filename, "referrer": referrer });
        activeDownloads++;
    }

    function clearUrlBusy(src) {
        const id = mk_img_id("busyImage", src);
        sessionStorage.removeItem(id);
        activeDownloads--;
        if (activeDownloads == 0) {
            setTimeout(function() {
                if (activeDownloads == 0) logger(null);
            }, 1000);
        } else if (activeDownloads < 0) {
            logger("Oops, download count underflow!");
            activeDownloads = 0;
        }
    }

    function setUrlSaved(src) {
        const id = mk_img_id("savedImage", src);
        localStorage.setItem(id, true);
        clearUrlBusy(src);
    }

    function isUrlSaved(src) {
        const id = mk_img_id("savedImage", src);
        return localStorage.getItem(id) === "true" ? true : false;
    }

    function isUrlReady(src) {
        if (!src || isUrlSaved(src)) return false;

        const id = mk_img_id("busyImage", src);
        const stamp = sessionStorage.getItem(id);
        if (!stamp) return true;
        stamp = stamp.time;
        if (Date.now() - stamp < 60000) {
            logger("file has been busy too long (lost event?): " + src);
            clearUrlBusy(src);
            return true;
        }
        console.log("still waiting to finish:", src);
        return false;
    }

})();