您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Utilities for Pixiv
当前为
// ==UserScript== // @name Pixiv Utils // @namespace https://greasyfork.org/en/scripts/510779-pixiv-utils/ // @version 2024-09-29 // @description Utilities for Pixiv // @author V.H. // @copyright V.H. // @match *://*.pixiv.net/* // @icon https://icons.duckduckgo.com/ip2/pixiv.net.ico // @grant unsafeWindow // @grant GM_log // @grant GM_registerMenuCommand // @grant GM_unregisterMenuCommand // @grant GM_addStyle // @grant GM_getValue // @grant GM_setValue // @run-at document-start // @tag utilities // @connect self // @webRequest [{"selector":"ads-pixiv.net","action":"cancel"}] // @license MIT // @supportURL https://greasyfork.org/en/scripts/510779-pixiv-utils/feedback // @antifeature Can get your Pixiv account in trouble if you overuse it (might be detected as a harmful bot), and can slow-down the network (not observed though) while loading images (it loads them twice to fetch the actual tags) // ==/UserScript== "use strict"; const sep = /[\s\n]*[,\n][\s\n]*/gmis, idsep = /\//gi, idmatch = /^\d+/i, showc = /show all|reading works?/i, prems = [ [ /(?<=['"]_setCustomVar['"]\s*?,\s*?\d+?\s*?,\s*?['"]plan['"]\s*?,\s*?['"])(normal)(?=['"])/gmis, "premium" ], [ /(?<=['"]?premium['"]?\s*?:\s*?['"])(no)(?=['"])/gmis, "yes" ], [ /(?<=['"]?plan['"]?\s*?:\s*?['"])(normal)(?=['"])/gmis, "premium" ], [ /(?<=['"]?premium['"]?\s*?:\s*?['"]?)(false|0|no)(?=['"]?)/gmis, "true" ], ]; let cfg = { name: "Pixiv Utils", prefix: "gm_vh_pxvutl_", base: "https://www.pixiv.net/en/artworks/", intr: 1000, baseopts: { }, panel: { name: "Panel", title: "Panel", accessKey: "P", autoClose: true, id: null, }, observe: { subtree: true, childList: true, characterData: false, characterDataOldValue: false, attributes: true, attributeOldValue: false, attributeFilter: [ "src", "alt", "title" ], }, style: ` .gm_vh_pxvutl_ { user-select: contain; text-shadow: 1px 1px 1px black; } .gm_vh_pxvutl_.row { display: flex; flex-flow: row wrap; justify-content: center; align-items: stretch; align-content: stretch; gap: 5px; } div:has(> div[width][height] .gm_vh_pxvutl_violet), div[type][size]:has(.gm_vh_pxvutl_violet) { border: medium dashed violet !important; } div:has(> div[width][height] .gm_vh_pxvutl_high), div[type][size]:has(.gm_vh_pxvutl_high) { border: medium dashed yellow !important; } div:has(> div[width][height] .gm_vh_pxvutl_green), div[type][size]:has(.gm_vh_pxvutl_green) { border: medium dashed green !important; } .gm_vh_pxvutl_block:not(.gm_vh_pxvutl_blue) { visibility: collapse !important; cursor: not-allowed; } div:has(> div[width][height] .gm_vh_pxvutl_block), div[type][size]:has(.gm_vh_pxvutl_block) { border: thick double red !important; } div:has(> div[width][height] .gm_vh_pxvutl_blue), div[type][size]:has(.gm_vh_pxvutl_blue) { visibility: visible !important; border-width: thick !important; border-style: dashed; border-color: blue !important; } .gm_vh_pxvutl_hide, div:has(> div[width][height] .gm_vh_pxvutl_hide), div[type][size]:has(.gm_vh_pxvutl_hide), div:has(> div > div[width][height] .gm_vh_pxvutl_hide, > div[type][size] .gm_vh_pxvutl_hide), li:has(> div > div > div[width][height] .gm_vh_pxvutl_hide, > div > div[type][size] .gm_vh_pxvutl_hide) { display: none !important; visibility: hidden !important; opacity: 0 !important; pointer-events: none !important; cursor: not-allowed !important; } :where(h1, h2, h3, h4).gm_vh_pxvutl_ { text-decoration: underline; margin: 5px; } label.gm_vh_pxvutl_ { user-select: none; } label.gm_vh_pxvutl_::after { content: ": "; } textarea.gm_vh_pxvutl_ { resize: both; border-radius: 5px; padding: 5px; margin: 5px; background: revert; } .gm_vh_pxvutl_:is(button, input, select) { cursor: pointer !important; opacity: .8; padding: 5px; margin: 5px; box-shadow: 1px 1px 1px 0 black; border-radius: 5px; background: revert; transition: all 200ms; } .gm_vh_pxvutl_:is(button, input, select):hover { box-shadow: 2px 2px 1px 1px black; opacity: .9; padding: 7px; border-radius: 7px; } .gm_vh_pxvutl_:is(button, input, select):active { box-shadow: 2px 2px 2px 2px black; opacity: 1; border-radius: 10px; padding: 8px; } #gm_vh_pxvutl_panel { display: flex; flex-flow: column nowrap; position: fixed !important; justify-content: space-between; align-items: stretch; align-content: stretch; resize: both !important; gap: 5px; bottom: 1vh !important; left: 1vw !important; min-width: 10vw; width: 30vw; max-width: 40vw; min-height: 10vh; height: 95vh; max-height: 100vh; padding: 5px; margin: 5px; border-radius: 5px; z-index: 999 !important; background: radial-gradient(circle farthest-side at center, rgba(230, 230, 230, 1) 0%, rgba(150, 150, 150, .7) 90%); overflow: auto; user-select: contain; pointer-events: none; } #gm_vh_pxvutl_panel * { pointer-events: auto; } #gm_vh_pxvutl_panel_close { position: sticky; top: 5px; right: 5px; } `, }, data = { enabled: false, exposed: true, show: true, violet: "", high: "", green: "", block: "", blue: "", hide: "", }, css = GM_addStyle(cfg.style), observer = new MutationObserver(see), tags = { violet: [ ], high: [ ], green: [ ], block: [ ], blue: [ ], hide: [ ], }, intr = null; document.addEventListener("DOMContentLoaded", premiumUnlock, true); function start() { cfg.panel.id = GM_registerMenuCommand(cfg.panel.name, panel, cfg.panel); data = Object.assign(data, GM_getValue(getPrefixed("settings"), data)); update(); GM_log(`--- '${cfg.name}' has started.`); } //start function stop() { GM_unregisterMenuCommand(cfg.panel.id); css.remove(); clearInterval(intr); } //stop function panel(e) { const root = document.createElement("dialog"), title = document.createElement("h3"), close = document.createElement("button"), statesp = document.createElement("span"), statelb = document.createElement("label"), state = document.createElement("input"), expsp = document.createElement("span"), explb = document.createElement("label"), exp = document.createElement("input"), showsp = document.createElement("span"), showlb = document.createElement("label"), show = document.createElement("input"), violetsp = document.createElement("span"), violetlb = document.createElement("label"), violet = document.createElement("textarea"), highsp = document.createElement("span"), highlb = document.createElement("label"), high = document.createElement("textarea"), greensp = document.createElement("span"), greenlb = document.createElement("label"), green = document.createElement("textarea"), blocksp = document.createElement("span"), blocklb = document.createElement("label"), block = document.createElement("textarea"), bluesp = document.createElement("span"), bluelb = document.createElement("label"), blue = document.createElement("textarea"), hidesp = document.createElement("span"), hidelb = document.createElement("label"), hide = document.createElement("textarea"); root.id = getPrefixed("panel"); root.classList.add(getPrefixed()); root.setAttribute("open", ""); { const e = document.getElementById(root.id); if (e) e.remove(); } title.id = getPrefixed("panel_title"); title.classList.add(getPrefixed()); title.innerHTML = "Pixiv Utils Control Panel"; close.id = getPrefixed("panel_close"); close.classList.add(getPrefixed()); close.innerHTML = "Close"; close.onclick = () => { const root = document.getElementById(getPrefixed("panel")); root.close(); root.remove(); }; state.id = getPrefixed("panel_enable"); state.type = "checkbox"; state.classList.add(getPrefixed()); state.onchange = () => { data.enabled = state.checked; update(); }; if (data.enabled) state.setAttribute("checked", ""); statelb.classList.add(getPrefixed()); statelb.htmlFor = state.id; statelb.innerHTML = "Enable"; exp.id = getPrefixed("panel_expose"); exp.type = "checkbox"; exp.classList.add(getPrefixed()); exp.onchange = () => { data.exposed = exp.checked; update(); }; if (data.exposed) exp.setAttribute("checked", ""); explb.classList.add(getPrefixed()); explb.htmlFor = exp.id; explb.title = "The Alt field of images contains the tags"; explb.innerHTML = "Expose Alt"; show.id = getPrefixed("panel_show"); show.type = "checkbox"; show.classList.add(getPrefixed()); show.onchange = () => { data.show = show.checked; update(); }; if (data.show) show.setAttribute("checked", ""); showlb.classList.add(getPrefixed()); showlb.htmlFor = show.id; showlb.innerHTML = "Auto Show-All"; violet.id = getPrefixed("panel_violet"); violet.classList.add(getPrefixed()); violet.value = data.violet; violet.cols = 50; violet.rows = 3; violet.placeholder = "tag1, tag2, ..."; violet.setAttribute("spellcheck", "false"); violet.onchange = () => { data.violet = violet.value; update(); }; violetlb.classList.add(getPrefixed()); violetlb.htmlFor = violet.id; violetlb.innerHTML = "Violet Tags"; high.id = getPrefixed("panel_high"); high.classList.add(getPrefixed()); high.value = data.high; high.cols = 50; high.rows = 3; high.placeholder = "tag1, tag2, ..."; high.setAttribute("spellcheck", "false"); high.onchange = () => { data.high = high.value; update(); }; highlb.classList.add(getPrefixed()); highlb.htmlFor = high.id; highlb.innerHTML = "Highlighted Tags"; green.id = getPrefixed("panel_green"); green.classList.add(getPrefixed()); green.value = data.green; green.cols = 50; green.rows = 3; green.placeholder = "tag1, tag2, ..."; green.setAttribute("spellcheck", "false"); green.onchange = () => { data.green = green.value; update(); }; greenlb.classList.add(getPrefixed()); greenlb.htmlFor = green.id; greenlb.innerHTML = "Green Tags"; block.id = getPrefixed("panel_block"); block.classList.add(getPrefixed()); block.value = data.block; block.cols = 50; block.rows = 3; block.placeholder = "tag1, tag2, ..."; block.setAttribute("spellcheck", "false"); block.onchange = () => { data.block = block.value; update(); }; blocklb.classList.add(getPrefixed()); blocklb.htmlFor = block.id; blocklb.innerHTML = "Blocked Tags"; blue.id = getPrefixed("panel_blue"); blue.classList.add(getPrefixed()); blue.value = data.blue; blue.cols = 50; blue.rows = 3; blue.placeholder = "tag1, tag2, ..."; blue.setAttribute("spellcheck", "false"); blue.onchange = () => { data.blue = blue.value; update(); }; bluelb.classList.add(getPrefixed()); bluelb.htmlFor = blue.id; bluelb.innerHTML = "Blue Tags"; hide.id = getPrefixed("panel_hide"); hide.classList.add(getPrefixed()); hide.value = data.hide; hide.cols = 50; hide.rows = 3; hide.placeholder = "tag1, tag2, ..."; hide.setAttribute("spellcheck", "false"); hide.onchange = () => { data.hide = hide.value; update(); }; hidelb.classList.add(getPrefixed()); hidelb.htmlFor = hide.id; hidelb.innerHTML = "Hide Tags"; title.classList.add(getPrefixed(), "row"); statesp.classList.add(getPrefixed(), "row"); expsp.classList.add(getPrefixed(), "row"); showsp.classList.add(getPrefixed(), "row"); blocksp.classList.add(getPrefixed(), "row"); highsp.classList.add(getPrefixed(), "row"); greensp.classList.add(getPrefixed(), "row"); bluesp.classList.add(getPrefixed(), "row"); violetsp.classList.add(getPrefixed(), "row"); hidesp.classList.add(getPrefixed(), "row"); statesp.append(statelb, state); expsp.append(explb, exp); showsp.append(showlb, show); violetsp.append(violetlb, violet); highsp.append(highlb, high); greensp.append(greenlb, green); blocksp.append(blocklb, block); bluesp.append(bluelb, blue); hidesp.append(hidelb, hide); root.append(title, close, violetsp, highsp, greensp, blocksp, bluesp, hidesp, expsp, showsp, statesp); document.body.appendChild(root); root.show(); GM_log(`--- '${cfg.name}' Panel opened.`); } //panel function update() { GM_setValue(getPrefixed("settings"), data); tags.violet = data.violet.trim().split(sep).map(t => t.trim()).filter(t => t).map(r => new RegExp(wrap(r, "\\b"), "i")); tags.high = data.high.trim().split(sep).map(t => t.trim()).filter(t => t).map(r => new RegExp(wrap(r, "\\b"), "i")); tags.green = data.green.trim().split(sep).map(t => t.trim()).filter(t => t).map(r => new RegExp(wrap(r, "\\b"), "i")); tags.block = data.block.trim().split(sep).map(t => t.trim()).filter(t => t).map(r => new RegExp(wrap(r, "\\b"), "i")); tags.blue = data.blue.trim().split(sep).map(t => t.trim()).filter(t => t).map(r => new RegExp(wrap(r, "\\b"), "i")); tags.hide = data.hide.trim().split(sep).map(t => t.trim()).filter(t => t).map(r => new RegExp(wrap(r, "\\b"), "i")); clearInterval(intr); if (data.enabled) observer.observe(document, cfg.observe); else observer.disconnect(); if (data.show) intr = setInterval(timed, cfg.intr); GM_log("Update."); } //update function timed() { const show = Array.from(document.querySelectorAll(":where(button, div):not(.hidden)")).find(b => showc.test(b.innerText.trim().toLowerCase())); if (show) { show.click(); show.classList.add("hidden"); } } //timed start(); function premiumUnlock() { const scripts = Array.from(document.scripts).filter(s => s.innerText.trim()), metas = Array.from(document.querySelectorAll("meta[content]")).filter(m => m.content.trim()); GM_log(`PREMS:\t${prems.length}\t${scripts.length}\t${metas.length}`); for (let script of scripts) { prems.forEach(p => { script.innerText = script.innerText.replaceAll(p[0], p[1]); }); } for (let meta of metas) { prems.forEach(p => { meta.content = meta.content.replaceAll(p[0], p[1]); }); } GM_log(`--- '${cfg.name}' has premium-unlocked.`); } //premiumUnlock function see(e, o) { for (let mut of e) { mut.addedNodes && mut.addedNodes.forEach(nd => { if (nd.tagName && nd.tagName.toLowerCase() == "img" && nd.alt) process(nd); }); } } //see function process(img, skip = false) { GM_log(`IMG:\t${img.src}\t${img.alt}`); const alt = img.alt.trim().toLowerCase().split(sep).map(t => t.trim()).filter(r => r); if (rule(tags.violet, alt)) { img.classList.add(getPrefixed("violet")); GM_log(`VIOLET:\t${img.src}\t${img.alt}`); } if (rule(tags.high, alt)) { img.classList.add(getPrefixed("high")); GM_log(`HIGH:\t${img.src}\t${img.alt}`); } if (rule(tags.green, alt)) { img.classList.add(getPrefixed("green")); GM_log(`GREEN:\t${img.src}\t${img.alt}`); } if (rule(tags.block, alt)) { img.classList.add(getPrefixed("block")); GM_log(`BLOCK:\t${img.src}\t${img.alt}`); } if (rule(tags.blue, alt)) { img.classList.add(getPrefixed("blue")); GM_log(`BLUE:\t${img.src}\t${img.alt}`); } if (rule(tags.hide, alt)) { img.classList.add(getPrefixed("hide")); GM_log(`HIDE:\t${img.src}\t${img.alt}`); } if (data.exposed) img.parentNode.title = img.title = img.alt.trim(); if (skip) return; getData(img).then(tags => { img.alt += ", " + tags.join(", "); process(img, true); }); } //process async function getData(img) { try { const id = getID(img.src), res = await fetch(cfg.base + id, cfg.baseopts), dat = await res.text(), tree = Document.parseHTMLUnsafe(dat).querySelector("meta[name='preload-data']"), json = JSON.parse(tree.content)["illust"][id]["tags"]["tags"], tags = [ ]; for (let tag of json) { if (tag["tag"]) tags.push(tag["tag"]); if (tag["romaji"]) tags.push(tag["romaji"]); if (tag["translation"]) for (let trans in tag["translation"]) tags.push(tag["translation"][trans]); } return tags; } catch(err) { return [ ]; } } //getData function rule(r = [], t = []) { return r.reduce((acc, curr) => acc + t.reduce((acc, curt) => acc + curr.test(curt), 0), 0); } //rule function getID(src) { return src.trim().split(idsep).map(i => i.trim()).filter(i => i).pop().match(idmatch)[0]; } //getID function getPrefixed(thing = "") { return cfg.prefix.trim() + thing.trim(); } //getPrefixed function wrap(thing = "", w = "") { return w + thing + w; } //wrap