您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Kemono links everywhere!
当前为
// ==UserScript== // @name Kemono Browser // @namespace Violentmonkey Scripts // @version 1.0 // @description Kemono links everywhere! // @author zWolfrost // @license MIT // @match https://*.vimeo.com/* // @match https://*.kemono.su/* // @match https://*.patreon.com/* // @match https://*.fanbox.cc/* // @match https://*.pixiv.net/* // @match https://*.fantia.jp/* // @match https://*.boosty.to/* // @match https://*.dlsite.com/* // @match https://*.gumroad.com/* // @match https://*.subscribestar.com/* // @match https://*.subscribestar.adult/* // @match https://*.kemono.su/* // @icon https://kemono.su/static/favicon.ico // @grant GM.xmlHttpRequest // ==/UserScript== "use strict" const PROMPT_BTN_ID = "kemono-url"; const UPDATE_POLLING_MS = 222; const badgePresets = { found: {stateText: "Found", textColor: "white", backgroundColor: "green"}, incomplete: {stateText: "Incomplete", textColor: "black", backgroundColor: "gold"}, missing: {stateText: "Missing", textColor: "white", backgroundColor: "red"}, } document.onreadystatechange = () => { if (document.readyState == "complete") { setInterval(function() { if (this.lastPathStr !== location.pathname || this.lastQueryStr !== location.search || this.lastPathStr === null || this.lastQueryStr === null) { this.lastPathStr = location.pathname; this.lastQueryStr = location.search; main(); } }, UPDATE_POLLING_MS); } } async function main() { document.getElementById(PROMPT_BTN_ID)?.remove() console.log(document.readyState) const domain = window.location.hostname.split(".").slice(-2).join(".") const url = await Promise.resolve(domainMethods[domain]?.()); console.log(url) const state = await getCreatorState(url); promptURL(url, badgePresets[state]) } function promptURL(url, {stateText, textColor, backgroundColor}) { const btn = document.createElement("a") btn.id = PROMPT_BTN_ID; btn.innerText = "Kemono Creator: " + stateText; btn.style.cssText = ` position: fixed; display: block; bottom: 5px; right: 5px; z-index: 9999; font-family: arial; font-weight: bold; font-size: 20px line-height: normal; border: 2px solid black; border-radius: 5px; padding: 2px; margin: 0px; cursor: pointer; text-decoration: none; color: ${textColor}; background-color: ${backgroundColor}; `; btn.target = "_blank"; btn.href = url; document.body.prepend(btn) } async function getCreatorState(url) { if (url) { const response = await new Promise(resolve => { GM.xmlHttpRequest({ url: url, method: "HEAD", onload: resolve }); }); switch (response.finalUrl) { case url: return "found"; case "https://kemono.su/artists": return "missing" default: return "incomplete"; } } } const domainMethods = { "patreon.com": getRedirectURLFromPatreon, "fanbox.cc": getRedirectURLFromFanbox, "pixiv.net": getRedirectURLFromPixiv, "fantia.jp": getRedirectURLFromFantia, "boosty.to": getRedirectURLFromBoosty, "dlsite.com": getRedirectURLFromDLsite, "gumroad.com": getRedirectURLFromGumroad, "subscribestar.com": getRedirectURLFromSubscribeStar, "subscribestar.adult": getRedirectURLFromSubscribeStar } function getRedirectURLFromPatreon() { function getPatreonUserID() //html { const userIDPrefix = `"creator":{"data":{"id":"` const userIDSuffix = `"` return getStringBetween(document.documentElement.innerHTML, userIDPrefix, userIDSuffix) } function getPatreonPostID() //url { const postIDPrefix = `posts` const postIDInternalPrefix = `-` return getURLPathAfter(postIDPrefix)?.split(postIDInternalPrefix).at(-1) ?? null } return getRedirectURL({ service: "patreon", userID: getPatreonUserID(), postID: getPatreonPostID() }) } async function getRedirectURLFromFanbox() { function getFanboxCreatorID() //url { const creatorIDPrefix1st = `www.fanbox.cc` const creatorIDInternalPrefix1st = `@` const creatorIDSuffix2nd = `.` let creatorID = getURLPathAfter(creatorIDPrefix1st)?.split(creatorIDInternalPrefix1st)[1] ?? window.location.hostname.split(creatorIDSuffix2nd)[0] return creatorID == "www" ? null : creatorID } function getFanboxUserID() //html { const userIDPrefix = `creator/` const userIDSuffix = `/` return getStringBetween(document.documentElement.innerHTML, userIDPrefix, userIDSuffix) } async function getFanboxUserID2nd() //url (api) { const creatorID = getFanboxCreatorID() if (!creatorID) return null const creatorFetch = await new Promise(resolve => { GM.xmlHttpRequest({ url: `https://api.fanbox.cc/creator.get?creatorId=${creatorID}`, method: "GET", headers: { Origin: "https://fanbox.cc" }, onload: resolve }); }); return JSON.parse(creatorFetch.responseText)?.body?.user?.userId ?? null } function getFanboxPostID() //url { const postIDPrefix = `posts` return getURLPathAfter(postIDPrefix) } return getRedirectURL({ service: "fanbox", userID: getFanboxUserID() ?? await getFanboxUserID2nd(), postID: getFanboxPostID() }) } function getRedirectURLFromPixiv() { function getPixivUserID() //url { const userIDPrefix = `users` return getURLPathAfter(userIDPrefix) } function getPixivUserID2nd() //html { const userIDPrefix = `users/` const userIDSuffix = `"` return getStringBetween(document.documentElement.innerHTML, userIDPrefix, userIDSuffix) } return getRedirectURL({ service: "fanbox", userID: getPixivUserID() ?? getPixivUserID2nd() }) } function getRedirectURLFromFantia() { function getFantiaUserID() //url { const userIDPrefix = `fanclubs` return getURLPathAfter(userIDPrefix) } function getFantiaUserID2nd() //html { const userIDPrefix = `fanclubs/` const userIDSuffix = `"` return getStringBetween(document.documentElement.innerHTML, userIDPrefix, userIDSuffix) } function getFantiaPostID() //url { const postIDPrefix = `posts` return getURLPathAfter(postIDPrefix) } return getRedirectURL({ service: "fantia", userID: getFantiaUserID() ?? getFantiaUserID2nd(), postID: getFantiaPostID() }) } function getRedirectURLFromBoosty() { function getBoostyUserID() //url { const userIDPrefix = `boosty.to` return getURLPathAfter(userIDPrefix) } function getBoostyPostID() //url { const postIDPrefix = `posts` return getURLPathAfter(postIDPrefix) } return getRedirectURL({ service: "boosty", userID: getBoostyUserID(), postID: getBoostyPostID() }) } function getRedirectURLFromDLsite() { function getDLsiteUserID() //html { const userIDPrefix = `maker_id/` const userIDSuffix = `.` return getStringBetween(document.documentElement.innerHTML, userIDPrefix, userIDSuffix) } function getDLsitePostID() //url { const postIDPrefix = `product_id` const postIDMatch = `\\d` let postID = getURLPathAfter(postIDPrefix).match(new RegExp(postIDMatch, "g")).join("") return postID ? "RE" + postID : null } return getRedirectURL({ service: "dlsite", userID: getDLsiteUserID(), postID: getDLsitePostID() }) } function getRedirectURLFromGumroad() { function getGumroadUserID() //html { const elementSelector = `script.js-react-on-rails-component` const userIDPrefix = `id":"` const userIDSuffix = `"` return getStringBetween(document.querySelector(elementSelector).innerText, userIDPrefix, userIDSuffix) } function getGumroadPostID() //url { const postIDPrefix = `l` return getURLPathAfter(postIDPrefix) } return getRedirectURL({ service: "gumroad", userID: getGumroadUserID(), postID: getGumroadPostID() }) } function getRedirectURLFromSubscribeStar() { function getSubScribeStarUserID() //html { const elementSelector = `img[data-type="avatar"]` return document.querySelector(elementSelector)?.alt.toLowerCase() ?? null } function getSubScribeStarPostID() //url { const postIDPrefix = `posts` return getURLPathAfter(postIDPrefix) } return getRedirectURL({ service: "subscribestar", userID: getSubScribeStarUserID(), postID: getSubScribeStarPostID() }) } function getStringBetween(string, prefix, suffix) { let begIndex = string.indexOf(prefix) if (begIndex == -1) return null else begIndex += prefix.length let endIndex = string.indexOf(suffix, begIndex) if (endIndex == -1) endIndex = undefined; let result = string.slice(begIndex, endIndex) return result } function getURLPathAfter(string) { let urlSplit = (window.location.hostname + window.location.pathname).split("/") let pathIndex = urlSplit.indexOf(string) + 1 if (pathIndex == 0) return null return urlSplit[pathIndex] } function getRedirectURL({service, userID=null, postID=null} = {}) { let redirectURL = `https://kemono.su/${service}` if (userID) { redirectURL += `/user/${userID}` if (postID) { redirectURL += `/post/${postID}` } } else return null return redirectURL }