您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Add kemono.su patreon & fanbox & fantia links into ppixiv
当前为
// ==UserScript== // @name kemono.su links for ppixiv // @author EnergoStalin // @description Add kemono.su patreon & fanbox & fantia links into ppixiv // @license AGPL-3.0-only // @version 1.4.7 // @namespace https://pixiv.net // @match https://*.pixiv.net/* // @run-at document-body // @icon https://www.google.com/s2/favicons?sz=64&domain=pixiv.net // @connect www.patreon.com // @connect kemono.su // @grant GM.xmlHttpRequest // ==/UserScript== "use strict"; (() => { var __defProp = Object.defineProperty; var __name = (target, value) => __defProp(target, "name", { value, configurable: true }); var __async = (__this, __arguments, generator) => { return new Promise((resolve, reject) => { var fulfilled = (value) => { try { step(generator.next(value)); } catch (e) { reject(e); } }; var rejected = (value) => { try { step(generator.throw(value)); } catch (e) { reject(e); } }; var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected); step((generator = generator.apply(__this, __arguments)).next()); }); }; // src/utils.ts function normalizeUrl(url) { let normalized = url.trim(); if (!normalized.startsWith("http")) normalized = `https://${normalized}`; return normalized; } __name(normalizeUrl, "normalizeUrl"); // src/ppixiv.ts var BODY_LINK_REGEX = /[\W\s]((?:https?:\/\/)?(?:\w+[\.\/])+(?:\w?)+)/g; var labelMatchingMap = { patreon: "patreon.com", fanbox: "Fanbox", fantia: "fantia.jp" }; function preprocessMatches(matches) { return matches.map((e) => { try { const url = new URL(normalizeUrl(e)); return { label: labelMatchingMap[Object.keys(labelMatchingMap).filter((e2) => url.host.includes(e2))[0]], url }; } catch (e2) { } return void 0; }); } __name(preprocessMatches, "preprocessMatches"); function getLinksFromDescription(extraLinks) { const desc = document.body.querySelector(".description"); return removeDuplicates(preprocessMatches( // eslint-disable-next-line unicorn/prefer-dom-node-text-content Array.from(desc.innerText.matchAll(BODY_LINK_REGEX)).map((e) => e[1]) ).filter((e) => e), extraLinks); } __name(getLinksFromDescription, "getLinksFromDescription"); function removeDuplicates(links, extraLinks) { const labels = extraLinks.map((e) => e.label); return links.filter((e) => !labels.includes(e.label)); } __name(removeDuplicates, "removeDuplicates"); function notifyUserUpdated(userId) { unsafeWindow.ppixiv.userCache.callUserModifiedCallbacks(userId); } __name(notifyUserUpdated, "notifyUserUpdated"); // src/deadLinks.ts function fastHash(str) { let hash = 0; if (str.length === 0) return hash; for (let i = 0; i < str.length; i++) { hash += str.charCodeAt(i); } return hash; } __name(fastHash, "fastHash"); var cachedRedirects = {}; function cacheRedirect(url) { return __async(this, null, function* () { const response = yield GM.xmlHttpRequest({ method: "GET", redirect: "manual", url }); const value = response.finalUrl !== url; cachedRedirects[url] = value; }); } __name(cacheRedirect, "cacheRedirect"); var pending = /* @__PURE__ */ new Set(); function disableDeadLinks(links, userInfo) { const hash = fastHash(JSON.stringify(links)); if (!pending.has(hash)) { pending.add(hash); Promise.all(links.filter((e) => cachedRedirects[e.url.toString()] === void 0).map((e) => cacheRedirect(e.url.toString()))).then((e) => { pending.delete(hash); if (e.length > 0) { notifyUserUpdated(userInfo.userId); } }).catch(console.error); } for (const l of links) { l.disabled = true; if (cachedRedirects[l.url.toString()] === true) { l.label += " (Redirected)"; } else { delete l.disabled; } } return links; } __name(disableDeadLinks, "disableDeadLinks"); // src/sites/fanbox.ts function fanbox(extraLinks, userInfo) { extraLinks.push({ url: new URL(`https://kemono.su/fanbox/user/${userInfo.userId}`), icon: "mat:money_off", type: "kemono_fanbox", label: "Kemono fanbox" }); } __name(fanbox, "fanbox"); // src/sites/fantia.ts function fantia(link, extraLinks) { const id = link.url.toString().split("/").pop(); extraLinks.push({ url: new URL(`https://kemono.su/fantia/user/${id}`), icon: "mat:money_off", type: "kemono_fantia", label: "Kemono fantia" }); } __name(fantia, "fantia"); // src/sites/parteon.ts function normalizePatreonLink(link) { if (typeof link.url === "string") link.url = new URL(normalizeUrl(link.url)); link.url.protocol = "https"; if (!link.url.host.startsWith("www.")) link.url.host = `www.${link.url.host}`; } __name(normalizePatreonLink, "normalizePatreonLink"); var PATREON_ID_REGEX = new RegExp('"id":\\s*"(\\d+)",[\\n\\s]*"type":\\s*"user"', "ms"); function ripPatreonId(link) { return __async(this, null, function* () { const response = yield GM.xmlHttpRequest({ method: "GET", url: link }); return response.responseText.match(PATREON_ID_REGEX)[1]; }); } __name(ripPatreonId, "ripPatreonId"); var cachedPatreonUsers = {}; function patreon(link, extraLinks, userInfo) { normalizePatreonLink(link); const url = link.url.toString(); const cachedId = cachedPatreonUsers[url]; if (!cachedId) { ripPatreonId(url).then((id) => { cachedPatreonUsers[url] = id; notifyUserUpdated(userInfo.userId); }).catch(console.error); } else { extraLinks.push({ url: new URL(`https://kemono.su/patreon/user/${cachedId}`), icon: "mat:money_off", type: "kemono_patreon", label: "Kemono patreon" }); } } __name(patreon, "patreon"); // src/index.ts var addUserLinks = /* @__PURE__ */ __name(({ extraLinks, userInfo }) => { const toBeChecked = []; for (const link of [ ...extraLinks, ...getLinksFromDescription(extraLinks) ]) { switch (link.label) { case "Fanbox": fanbox(toBeChecked, userInfo); break; case "patreon.com": patreon(link, toBeChecked, userInfo); break; case "fantia.jp": fantia(link, toBeChecked); break; default: } } extraLinks.push(...disableDeadLinks(toBeChecked, userInfo)); }, "addUserLinks"); unsafeWindow.vviewHooks = { addUserLinks }; })();