Greasy Fork

Greasy Fork is available in English.

Huggingface Image Downloader

Add buttons to quickly download images from Stable Diffusion

当前为 2023-02-24 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name           Huggingface Image Downloader
// @description    Add buttons to quickly download images from Stable Diffusion
// @author         Isaiah Odhner
// @namespace      https://isaiahodhner.io
// @version        1.2
// @license        MIT
// @match          https://*.hf.space/*
// @icon           https://www.google.com/s2/favicons?sz=64&domain=huggingface.co
// @grant          none
// ==/UserScript==

// v1.1 adds support for Stable Diffusion 2. It expands the domain scope to match other applications on HuggingFace, and includes negative prompts in the filename, with format: "'<positive>' (anti '<negative>')".
// v1.2 handles updated DOM structure of the site

let foundGrid = false;

setInterval(() => {
    // assuming prompt comes before negative prompt in DOM
	const input = document.querySelector('#prompt-text-input input, [name=prompt], [placeholder*="prompt"]');
    const negativeInput = document.querySelector('#negative-prompt-text-input input, [name=negative-prompt], [placeholder*="negative prompt"]');

	const dlButtons = [];
	for (const img of document.querySelectorAll(".grid img, #gallery img, .grid-container img, .thumbnail-item img")) {
		const existingA = img.parentElement.querySelector("a");
		if (existingA) {
			if (existingA._imgSrc !== img.src) {
				existingA.remove();
				const index = dlButtons.indexOf(existingA);
				if (index > -1) {
					dlButtons.splice(index);
				}
			} else {
				continue; // don't add a duplicate <a> or change the supposed prompt it was generated with
			}
		}

		const a = document.createElement("a");
		a.style.position = "absolute";
		a.style.opacity = "0";
		a.style.top = "0";
		a.style.left = "0";
		a.style.background = "black";
		a.style.color = "white";
		a.style.borderRadius = "5px";
		a.style.padding = "5px";
		a.style.margin = "5px";
		a.style.fontSize = "50px";
		a.style.lineHeight = "50px";
		a.textContent = "Download";
		a._imgSrc = img.src;

		let filename = `'${sanitizeFilename(input.value)}'`;
		if (negativeInput) {
			filename += ` (anti '${sanitizeFilename(negativeInput.value)}')`;
		}
		filename += ".jpeg";
		a.download = filename;

		a.href = img.src;
		img.parentElement.append(a);
		dlButtons.push(a);

		// Can't be delegated because it needs to stop the click event from bubbling up to the handler that zooms in
		a.addEventListener("click", (event) => {
			// Prevent also zooming into the image when clicking Download
			event.stopImmediatePropagation();
		});
	}
	const grid = document.querySelector(".grid, #gallery");
	if (grid && !foundGrid) {
		foundGrid = true;
		grid.addEventListener("mouseover", (event) => {
			for (const a of dlButtons) {
				a.style.opacity = "0";
			}
			const cell = event.target.closest(".gallery-item, .thumbnail-item");
			if (cell) {
				cell.querySelector("a[download]").style.opacity = "1";
			}
		});
		grid.addEventListener("mouseout", (event) => {
			if (event.target instanceof HTMLImageElement) {
				const cell = event.target.closest(".gallery-item, .thumbnail-item");
				const newCell = event.relatedTarget.closest(".gallery-item, .thumbnail-item");
				if (cell === newCell) return;
				cell.querySelector("a[download]").style.opacity = "0";
			}
		});
		document.addEventListener("mouseleave", (event) => {
			for (const a of document.querySelectorAll(".gallery-item a[download], .thumbnail-item a[download]")) {
				a.style.opacity = "0";
			}
		});
	}
}, 300);

function sanitizeFilename(str) {
	// Sanitize for file name, replacing symbols rather than removing them
	str = str.replace(/\//g, "⧸");
	str = str.replace(/\\/g, "⧹");
	str = str.replace(/</g, "ᐸ");
	str = str.replace(/>/g, "ᐳ");
	str = str.replace(/:/g, "꞉");
	str = str.replace(/\|/g, "∣");
	str = str.replace(/\?/g, "?");
	str = str.replace(/\*/g, "∗");
	str = str.replace(/(^|[-—\s(\["])'/g, "$1\u2018");  // opening singles
	str = str.replace(/'/g, "\u2019");                  // closing singles & apostrophes
	str = str.replace(/(^|[-—/\[(‘\s])"/g, "$1\u201c"); // opening doubles
	str = str.replace(/"/g, "\u201d");                  // closing doubles
	str = str.replace(/--/g, "\u2014");                 // em-dashes
	str = str.replace(/\.\.\./g, "…");                  // ellipses
	str = str.replace(/~/g, "\u301C");                  // Chrome at least doesn't like tildes
	str = str.trim();
	return str;
}