您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Various toggleable changes to Gelbooru such as enlarging the gallery, removing the sidebar, and more.
// ==UserScript== // @name Gelbooru Overhaul // @namespace https://github.com/Enchoseon/gelbooru-overhaul-userscript/raw/main/gelbooru-overhaul.user.js // @version 0.7.2 // @description Various toggleable changes to Gelbooru such as enlarging the gallery, removing the sidebar, and more. // @author Enchoseon // @include *gelbooru.com* // @run-at document-start // @grant GM_getValue // @grant GM_setValue // @grant GM_download // ==/UserScript== (function() { "use strict"; // ============= // Configuration // ============= const config = { general: { amoled: true, // A very lazy Amoled theme autoDarkMode: true, // Apply Amoled theme if system in Dark mode, higher priority than 'amoled' autoDarkModeForceTime: false, // Ignore system theme and check time for dark mode autoDarkModeStartHour: 19, // Start and End time if ForceTime is enabled or system does not supports dark mode autoDarkModeEndHour: 7, sexySidebar: true, // Move the leftmost sidebar to the top-left of the screen next to the Gelbooru logo }, post: { fitVertically: true, // Scale media to fit vertically in the screen center: true, // Center media }, gallery: { removeTitle: true, // Removes the title attribute from thumbnails rightClickDownload: true, // Makes it so that when you right-click thumbnails you'll download their highest-resolution counterpart rightClickDownloadSaveAsPrompt: false, // Show the "Save As" File Explorer prompt when right-click downloading enlargeFlexbox: true, // Make the thumbnails in the gallery slightly larger & reduce the number of columns enlargeThumbnailsOnHover: true, // Make the thumbnails in the gallery increase in scale when you hover over them (best paired with gallery.higherResThumbnailsOnHover) higherResThumbnailsOnHover: true, // Make the thumbnails in the gallery higher-resolution when you hover over them advancedBlacklist: true, // Use the advanced blacklist that supports AND operators & // comments advancedBlacklistConfig: ` // Humans realistic photo_(medium) // Extremely Niche Kinks egg_laying minigirl penis_hug // Shitty Artists shadman morrie `, // ^ This arbitrary blacklist is purely for demo purposes. For a larger blacklist, see blacklist.txt in the GitHub repository }, download: { blockUnknownArtist: true, // Block the download of files without a tagged artist missingArtistText: "_unknown-artist", // Text that replaces where the artist name would usually be in images missing artist tags }, }; var css = ""; // ======================================================= // Higher-Resolution Preview When Hovering Over Thumbnails // Download Images in Gallery on Right-Click // Remove Title from Thumbnails // Advanced Blacklist // ======================================================= if (config.gallery.higherResThumbnailsOnHover || config.gallery.rightClickDownload || config.gallery.removeTitle || config.gallery.advancedBlacklist) { document.addEventListener("DOMContentLoaded", function () { Object.values(document.querySelectorAll(".thumbnail-preview")).forEach((elem) => { var aElem = elem.querySelector("a"); var imgElem = aElem.querySelector("img"); if (config.gallery.higherResThumbnailsOnHover) { // Higher-Resolution Preview When Hovering Over Thumbnails imgElem.addEventListener("mouseenter", function() { convertThumbnail(imgElem, aElem, false); }, false); } if (config.gallery.rightClickDownload) { // Download Images in Gallery on Right-Click imgElem.addEventListener("contextmenu", (event) => { event.preventDefault(); convertThumbnail(imgElem, aElem, true).then(function() { downloadImage(imgElem, aElem); }); }) } if (config.gallery.removeTitle) { // Remove Title from Thumbnails imgElem.title = ""; } if (config.gallery.advancedBlacklist) { // Advanced Blacklist config.gallery.advancedBlacklistConfig.forEach((blacklistLine) => { if (blacklistLine.includes("&&")) { // AND statements var remove = true; blacklistLine = blacklistLine.split("&&"); blacklistLine.forEach((andArg) => { if (!tagFound(andArg)) { remove = false; } }); if (remove) { elem.remove(); } } else if (tagFound(blacklistLine)) { // Simple & straightforward blacklisting elem.remove(); } }); function tagFound(query) { // Check if a tag is present in the imgElem var tags = imgElem.alt.split(","); tags = tags.map(tag => tag.trim()) if (tags.includes(query)) { return true; } return false; } } }); Object.values(document.querySelector(".mainBodyPadding").querySelectorAll("div")).reverse()[1].querySelectorAll("a").forEach((aElem) => { var imgElem = aElem.querySelector("img"); if (config.gallery.higherResThumbnailsOnHover) { // Higher-Resolution Preview When Hovering Over Thumbnails imgElem.addEventListener("mouseenter", function() { convertThumbnail(imgElem, aElem, false); }, false); } if (config.gallery.rightClickDownload) { // Download Images in Gallery on Right-Click imgElem.addEventListener("contextmenu", (event) => { event.preventDefault(); convertThumbnail(imgElem, aElem, true).then(function() { downloadImage(imgElem, aElem); }); }) } if (config.gallery.removeTitle) { // Remove Title from Thumbnails imgElem.title = ""; } if (config.gallery.advancedBlacklist) { // Advanced Blacklist config.gallery.advancedBlacklistConfig.forEach((blacklistLine) => { if (blacklistLine.includes("&&")) { // AND statements var remove = true; blacklistLine = blacklistLine.split("&&"); blacklistLine.forEach((andArg) => { if (!tagFound(andArg)) { remove = false; } }); if (remove) { elem.remove(); } } else if (tagFound(blacklistLine)) { // Simple & straightforward blacklisting elem.remove(); } }); function tagFound(query) { // Check if a tag is present in the imgElem var tags = imgElem.alt.split(","); tags = tags.map(tag => tag.trim()) if (tags.includes(query)) { return true; } return false; } } }); }); } // ================================= // Make Leftmost Sidebar Collapsable // ================================= if (config.general.sexySidebar && window.location.search !== "") { document.addEventListener("DOMContentLoaded", function () { var div = document.createElement("div"); div.id = "sidebar"; div.innerHTML = document.querySelectorAll(".aside")[0].innerHTML; document.body.appendChild(div); }); css += ` .aside { grid-area: aside; display: none; } #container { grid-template-columns: 0px auto; } #sidebar { position: fixed; width: 4px; height: 100%; padding-top: 60px; overflow: hidden; background: red; top: 0; left: 0; transition: 142ms; z-index: 420690; } #sidebar:hover { position: fixed; width: 240px; height: 100%; padding-top: 0px; overflow-y: scroll; background: ${isDarkMode() ? 'black' : 'white'}; opacity: 0.9; } `; } // ============================= // Scale Media To Fit Vertically // ============================= if (config.post.fitVertically) { css += ` #image, #gelcomVideoPlayer { height: 90vh !important; width: auto !important; } `; // resize to fit horizontally on 'Click here to expand image.' document.addEventListener("DOMContentLoaded", function () { let resizeLink = document.querySelector("#resize-link").querySelector("a"); let oldOnClick = resizeLink.onclick; resizeLink.onclick = function(event) { oldOnClick(event); Object.values(document.querySelectorAll("#image, #gelcomVideoPlayer")).forEach((elem) => { elem.style.cssText += ` height: auto !important; width: 95vw !important; `; }); }; }); } // ============ // Center Media // ============ if (config.post.center) { css += ` .image-container { display: flex !important; justify-content: center; } `; } // =========================== // Enlarge Thumbnails On Hover // =========================== if (config.gallery.enlargeThumbnailsOnHover) { css += ` .thumbnail-preview a img { transform: scale(1); transition: transform 169ms; } .thumbnail-preview a img:hover { transform: scale(2.42); transition-delay: 142ms; } .thumbnail-preview:hover { position: relative; z-index: 690; } .mainBodyPadding div a img { max-height: 10vw !important; transform: scale(1); transition: transform 169ms; } .mainBodyPadding div a img:hover { transform: scale(2.42); transition-delay: 142ms; position: relative; z-index: 690; } `; } // ======================= // Enlarge Gallery Flexbox // ======================= if (config.gallery.enlargeFlexbox) { css += ` .thumbnail-preview { height: 21em; width: 20%; } .thumbnail-preview { transform: scale(1.42); } html, body { overflow-x: hidden; } .searchArea { z-index: 420; } #paginator { margin-top: 6.9em; } main { margin-top: 1.21em; } `; } // =========================== // Extremely Lazy Amoled Theme // =========================== if (isDarkMode()) { css += ` body, #tags-search { color: white; } .note-body { color: black !important; } .aside, .searchList, header, .navSubmenu, #sidebar { filter: saturate(42%); } .thumbnail-preview a img { border-radius: 0.42em; } #container, header, .navSubmenu, body, .alert-info, footer, html, #tags-search { background-color: black !important; background: black !important; } .searchArea a, .commentBody, textarea, .ui-menu { filter: invert(1) saturate(42%); } .aside, .alert-info, #tags-search { border: unset; } `; } // ========== // Inject CSS // ========== (function() { var s = document.createElement("style"); s.setAttribute("type", "text/css"); s.appendChild(document.createTextNode(css)); document.querySelector("head").appendChild(s); })(); // ================= // Process Blacklist // ================= (function() { var blacklist = config.gallery.advancedBlacklistConfig.split(/\r?\n/); var output = []; blacklist.forEach((line) => { // Convert blacklist to array form line = line.trim(); if (!line.startsWith("//") && line !== "") { // Ignore comments output.push(line.replace(/ /g, "&&") // Marker to tell the blacklist loop this is an AND statement .replace(/_/g, " ") // Format to be same as imgElem alt text .toLowerCase() ); } }); config.gallery.advancedBlacklistConfig = output; })(); // ================ // Utility Functions // ================ // Get higher-resolution counterpart of a thumbnail function convertThumbnail(imgElem, aElem, highestQuality) { return new Promise(function(resolve, reject) { var gelDB = GM_getValue("gelDB", {}); var index = hash(aElem.href); if (!gelDB[index] || (highestQuality && !gelDB[index].high) || (!gelDB[index].medium)) { // Request higher-resolution image (unless it's already indexed) var xobj = new XMLHttpRequest(); xobj.open("GET", aElem.href, true); xobj.onreadystatechange = function() { if (xobj.readyState == 4 && xobj.status == "200") { const responseDocument = new DOMParser().parseFromString(xobj.responseText, "text/html"); if (responseDocument.querySelector("#gelcomVideoPlayer")) { // Reject videos reject("Gelbooru Overhaul doesn't support videos in convertThumbnail() or downloadImage() yet."); if (highestQuality) { alert("Gelbooru Overhaul doesn't support videos in convertThumbnail() or downloadImage() yet."); } return; } gelDB[index] = {}; gelDB[index].tags = convertTagElem(responseDocument.querySelector("#tag-list")); // Grab tags gelDB[index].medium = responseDocument.querySelector("#image").src; // Get medium-quality src gelDB[index].high = responseDocument.querySelectorAll("script:not([src])"); // Get highest-quality src gelDB[index].high = gelDB[index].high[gelDB[index].high.length - 1] .innerHTML .split(`image.attr('src','`)[1] .split(`');`)[0]; GM_setValue("gelDB", gelDB); output(); } }; xobj.send(null); } else { // Skip the AJAX voodoo if it's already indexed. Added bonus of cache speed. output(); } function output() { if (highestQuality) { imgElem.src = gelDB[index].high; } else { imgElem.src = gelDB[index].medium; } resolve(); } }); } // Convert tag list elem into a friendlier object function convertTagElem(tagElem) { var tagObj = { "artist": [], "character": [], "copyright": [], "metadata": [], "general": [], "deprecated": [], }; Object.values(tagElem.querySelectorAll("li")).forEach((tag) => { if (tag.className.startsWith("tag-type-")) { var type = tag.className.replace("tag-type-", ""); tag = tag.querySelector("span a") .href .replace("https://gelbooru.com/index.php?page=wiki&s=list&search=", ""); tagObj[type].push(tag); } }); return tagObj; } // Generate hash from string (https://stackoverflow.com/a/52171480) function hash(str, seed = 0) { let h1 = 0xdeadbeef ^ seed, h2 = 0x41c6ce57 ^ seed; for (let i = 0, ch; i < str.length; i++) { ch = str.charCodeAt(i); h1 = Math.imul(h1 ^ ch, 2654435761); h2 = Math.imul(h2 ^ ch, 1597334677); } h1 = Math.imul(h1 ^ (h1>>>16), 2246822507) ^ Math.imul(h2 ^ (h2>>>13), 3266489909); h2 = Math.imul(h2 ^ (h2>>>16), 2246822507) ^ Math.imul(h1 ^ (h1>>>13), 3266489909); return 4294967296 * (2097151 & h2) + (h1>>>0); }; // Download image function downloadImage(imgElem, aElem) { var gelDB = GM_getValue("gelDB", {}); var index = hash(aElem.href); var extension = imgElem.src.split(".").at(-1); var artist = gelDB[index].tags.artist.join(" "); if (config.download.blockUnknownArtist && artist === "") { // Don't download if blockUnknownArtist is enabled and artist tag is missing return; } GM_download({ url: imgElem.src, name: formatFilename(artist, index, extension), saveAs: config.gallery.rightClickDownloadSaveAsPrompt, }) } // Create the filename from the artist's name function formatFilename(artist, index, extension) { if (artist === "") { artist = config.download.missingArtistText; } const illegalRegex = /[\/\?<>\\:\*\|":]/g; artist = decodeURI(artist).replace(illegalRegex, "_") // Make filename-safe (https://stackoverflow.com/a/11101624) .replace(/_{2,}/g, "_") // and remove consecutive underscores .toLowerCase() + "_" + index + "." + extension; return artist; } // Check if dark mode should be applied function isDarkMode() { //if auto enabled if(config.general.autoDarkMode) { let hasMediaColorScheme = (window.matchMedia && window.matchMedia('(prefers-color-scheme)').media !== 'not all'); if(config.general.autoDarkModeForceTime || !hasMediaColorScheme) { let hours = new Date().getHours(); if(hours >= config.general.autoDarkModeStartHour || hours <= config.general.autoDarkModeEndHour) { return true; } else { return false; } } //system in dark mode if(window.matchMedia('(prefers-color-scheme: dark)').matches) { return true; } //system in light mode else { return false; } } //if permanent dark mode enabled else if(config.general.amoled) { return true; } else { return false; } } })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址