Greasy Fork is available in English.
Add buttons to quickly download images from Stable Diffusion
当前为
// ==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;
}