- // ==UserScript==
- // @name ViewOnYP
- // @name:de ViewOnYP
- // @name:en ViewOnYP
- // @namespace sun/userscripts
- // @version 2.19.2
- // @description Links various membership platforms to Kemono and Coomer.
- // @description:de Vernetzt verschiedene Mitgliedschaftsplattformen mit Kemono und Coomer.
- // @description:en Links various membership platforms to Kemono and Coomer.
- // @compatible chrome
- // @compatible edge
- // @compatible firefox
- // @compatible opera
- // @compatible safari
- // @homepageURL https://forgejo.sny.sh/sun/userscripts
- // @supportURL https://forgejo.sny.sh/sun/userscripts/issues
- // @contributionURL https://liberapay.com/sun
- // @contributionAmount €1.00
- // @author Sunny <sunny@sny.sh>
- // @include *://afdian.net/a/*
- // @include *://boosty.to/*
- // @include *://candfans.jp/*
- // @include *://discord.com/channels/*
- // @include *://discord.com/invite/*
- // @include *://www.dlsite.com/*/circle/profile/=/maker_id/*
- // @include *://*.fanbox.cc/
- // @include *://www.fanbox.cc/@*
- // @include *://fansly.com/*
- // @include *://fantia.jp/fanclubs/*
- // @include *://*.gumroad.com/
- // @include *://onlyfans.com/*
- // @include *://www.patreon.com/*
- // @include *://www.subscribestar.com/*
- // @include *://subscribestar.adult/*
- // @match *://afdian.net/a/*
- // @match *://boosty.to/*
- // @match *://candfans.jp/*
- // @match *://discord.com/channels/*
- // @match *://discord.com/invite/*
- // @match *://www.dlsite.com/*/circle/profile/=/maker_id/*
- // @match *://*.fanbox.cc/
- // @match *://www.fanbox.cc/@*
- // @match *://fansly.com/*
- // @match *://fantia.jp/fanclubs/*
- // @match *://*.gumroad.com/
- // @match *://onlyfans.com/*
- // @match *://www.patreon.com/*
- // @match *://www.subscribestar.com/*
- // @match *://subscribestar.adult/*
- // @exclude *://boosty.to/app
- // @exclude *://boosty.to/app/*
- // @exclude *://candfans.jp/
- // @exclude *://candfans.jp/auth/*
- // @exclude *://www.fanbox.cc/
- // @exclude *://fansly.com/
- // @exclude *://fansly.com/application
- // @exclude *://fansly.com/dmca
- // @exclude *://fansly.com/explore/*
- // @exclude *://fansly.com/privacy
- // @exclude *://fansly.com/tos
- // @exclude *://fansly.com/usc2257
- // @exclude *://gumroad.com/
- // @exclude *://onlyfans.com/
- // @exclude *://onlyfans.com/about
- // @exclude *://onlyfans.com/antitraffickingstatement
- // @exclude *://onlyfans.com/brand
- // @exclude *://onlyfans.com/contact
- // @exclude *://onlyfans.com/contract
- // @exclude *://onlyfans.com/cookies
- // @exclude *://onlyfans.com/dmca
- // @exclude *://onlyfans.com/help
- // @exclude *://onlyfans.com/help/*
- // @exclude *://onlyfans.com/legalguide/
- // @exclude *://onlyfans.com/legalinquiry
- // @exclude *://onlyfans.com/privacy
- // @exclude *://onlyfans.com/terms
- // @exclude *://onlyfans.com/transparency-center
- // @exclude *://onlyfans.com/transparency-center/*
- // @exclude *://onlyfans.com/usc2257
- // @exclude *://www.patreon.com/
- // @exclude *://www.patreon.com/about
- // @exclude *://www.patreon.com/apps
- // @exclude *://www.patreon.com/brand
- // @exclude *://www.patreon.com/careers
- // @exclude *://www.patreon.com/create
- // @exclude *://www.patreon.com/creators/*
- // @exclude *://www.patreon.com/de-DE
- // @exclude *://www.patreon.com/de-DE/*
- // @exclude *://www.patreon.com/en-GB
- // @exclude *://www.patreon.com/en-GB/*
- // @exclude *://www.patreon.com/es-ES
- // @exclude *://www.patreon.com/es-ES/*
- // @exclude *://www.patreon.com/forgot-password
- // @exclude *://www.patreon.com/fr-FR
- // @exclude *://www.patreon.com/fr-FR/*
- // @exclude *://www.patreon.com/home
- // @exclude *://www.patreon.com/it-IT
- // @exclude *://www.patreon.com/it-IT/*
- // @exclude *://www.patreon.com/login
- // @exclude *://www.patreon.com/messages?*
- // @exclude *://www.patreon.com/mobile
- // @exclude *://www.patreon.com/notifications?*
- // @exclude *://www.patreon.com/policy
- // @exclude *://www.patreon.com/policy/*
- // @exclude *://www.patreon.com/press
- // @exclude *://www.patreon.com/pricing
- // @exclude *://www.patreon.com/product/*
- // @exclude *://www.patreon.com/search
- // @exclude *://www.patreon.com/search?*
- // @exclude *://www.patreon.com/settings/*
- // @exclude *://www.subscribestar.com/
- // @exclude *://www.subscribestar.com/about
- // @exclude *://www.subscribestar.com/api
- // @exclude *://www.subscribestar.com/brand
- // @exclude *://www.subscribestar.com/contacts
- // @exclude *://www.subscribestar.com/dropp
- // @exclude *://www.subscribestar.com/features
- // @exclude *://www.subscribestar.com/guidelines
- // @exclude *://www.subscribestar.com/help
- // @exclude *://www.subscribestar.com/login
- // @exclude *://www.subscribestar.com/posts/*
- // @exclude *://www.subscribestar.com/pricing
- // @exclude *://www.subscribestar.com/privacy
- // @exclude *://www.subscribestar.com/refund
- // @exclude *://www.subscribestar.com/search
- // @exclude *://www.subscribestar.com/search?*
- // @exclude *://www.subscribestar.com/signup
- // @exclude *://www.subscribestar.com/stars
- // @exclude *://www.subscribestar.com/stars?*
- // @exclude *://www.subscribestar.com/taxes
- // @exclude *://www.subscribestar.com/tos
- // @exclude *://subscribestar.adult/
- // @exclude *://subscribestar.adult/about
- // @exclude *://subscribestar.adult/api
- // @exclude *://subscribestar.adult/brand
- // @exclude *://subscribestar.adult/contacts
- // @exclude *://subscribestar.adult/dropp
- // @exclude *://subscribestar.adult/features
- // @exclude *://subscribestar.adult/guidelines
- // @exclude *://subscribestar.adult/help
- // @exclude *://subscribestar.adult/login
- // @exclude *://subscribestar.adult/posts/*
- // @exclude *://subscribestar.adult/pricing
- // @exclude *://subscribestar.adult/privacy
- // @exclude *://subscribestar.adult/refund
- // @exclude *://subscribestar.adult/search
- // @exclude *://subscribestar.adult/search?*
- // @exclude *://subscribestar.adult/signup
- // @exclude *://subscribestar.adult/stars
- // @exclude *://subscribestar.adult/stars?*
- // @exclude *://subscribestar.adult/taxes
- // @exclude *://subscribestar.adult/tos
- // @connect coomer.su
- // @connect kemono.su
- // @connect nekohouse.su
- // @connect afdian.net
- // @connect api.fanbox.cc
- // @connect apiv3.fansly.com
- // @connect discord.com
- // @run-at document-end
- // @inject-into auto
- // @grant GM.addStyle
- // @grant GM_addStyle
- // @grant GM.deleteValue
- // @grant GM_deleteValue
- // @grant GM.getValue
- // @grant GM_getValue
- // @grant GM.registerMenuCommand
- // @grant GM_registerMenuCommand
- // @grant GM.setValue
- // @grant GM_setValue
- // @grant GM.xmlHttpRequest
- // @grant GM_xmlhttpRequest
- // @noframes
- // @require https://greasemonkey.github.io/gm4-polyfill/gm4-polyfill.js
- // @icon https://forgejo.sny.sh/sun/userscripts/raw/branch/main/icons/ViewOnYP.ico
- // @copyright 2020-present, Sunny (https://sny.sh/)
- // @license Hippocratic License; https://forgejo.sny.sh/sun/userscripts/src/branch/main/LICENSE.md
- // ==/UserScript==
-
- (async () => {
- if (!(await GM.getValue("cache2"))) await GM.setValue("cache2", {});
- const cache = await GM.getValue("cache2");
-
- if (await GM.getValue("cache")) {
- cache.Kemono = await GM.getValue("cache");
- await GM.deleteValue("cache");
- }
-
- if (await GM.getValue("style")) await GM.deleteValue("style");
-
- const sites = [
- {
- name: "Coomer",
- check: "https://coomer.su/api/v1/creators.txt",
- cache: (response, x) => {
- for (const y of JSON.parse(response.responseText)) {
- if (!cache[x.name]) cache[x.name] = {};
- if (!cache[x.name][y.service]) cache[x.name][y.service] = [];
- cache[x.name][y.service].push(y.id);
- }
- return cache;
- },
- get: (response, host, user) => {
- return Boolean(
- JSON.parse(response.responseText)
- .filter((x) => x.service === host)
- .filter((x) => x.id === user).length,
- );
- },
- profile: "https://coomer.su/$HOST/user/$USER",
- },
- {
- name: "Kemono",
- check: "https://kemono.su/api/v1/creators.txt",
- cache: (response, x) => {
- for (const y of JSON.parse(response.responseText)) {
- if (!cache[x.name]) cache[x.name] = {};
- if (!cache[x.name][y.service]) cache[x.name][y.service] = [];
- cache[x.name][y.service].push(y.id);
- }
- return cache;
- },
- get: (response, host, user) => {
- return Boolean(
- JSON.parse(response.responseText)
- .filter((x) => x.service === host)
- .filter((x) => x.id === user).length,
- );
- },
- profile: "https://kemono.su/$HOST/user/$USER",
- },
- {
- name: "Nekohouse",
- check: "https://nekohouse.su/api/creators",
- cache: (response, x) => {
- for (const y of JSON.parse(response.responseText)) {
- if (!cache[x.name]) cache[x.name] = {};
- if (!cache[x.name][y.service]) cache[x.name][y.service] = [];
- cache[x.name][y.service].push(y.user_id);
- }
- return cache;
- },
- get: (response, host, user) => {
- return Boolean(
- JSON.parse(response.responseText)
- .filter((x) => x.service === host)
- .filter((x) => x.user_id === user).length,
- );
- },
- profile: "https://nekohouse.su/$HOST/user/$USER",
- },
- ];
-
- GM.registerMenuCommand("Populate cache", () => {
- for (const x of sites) {
- GM.xmlHttpRequest({
- url: x.check,
- method: "GET",
- onload: async (response) => {
- const cache = x.cache(response, x);
- await GM.setValue("cache2", cache);
- alert(`Populated cache for ${x.name}.`);
- },
- });
- }
- });
-
- GM.registerMenuCommand("Clear cache", () => {
- GM.deleteValue("cache2").then(alert("Cache cleared successfully."));
- });
-
- GM.addStyle(`
- #voyp {
- background: white;
- position: fixed;
- top: calc(100vh - 60px);
- left: 50%;
- transform: translateX(-50%);
- border-radius: 5px 5px 0 0;
- padding: 25px;
- box-shadow: 0 1px 2px 0 rgba(60, 64, 67, 0.302), 0 1px 3px 1px rgba(60, 64, 67, 0.149);
- text-align: center;
- z-index: 9999;
- }
- #voyp:hover {
- top: initial;
- bottom: 0;
- }
- #voyp div {
- font-size: small;
- text-transform: uppercase;
- opacity: 0.5;
- text-align: center;
- }
- #voyp div i {
- position: absolute;
- top: 0;
- right: 0;
- font-size: 1.5em;
- padding: 0.375em 0.5em;
- cursor: pointer;
- }
- `);
-
- document
- .getElementsByTagName("body")[0]
- .insertAdjacentHTML(
- "beforeend",
- '<div id="voyp"><div><b>ViewOnYP</b> · hover to reveal<i>✕</i></div><span><br>Querying platforms, please wait...</span></div>',
- );
-
- document.querySelector("#voyp div i").addEventListener("click", () => {
- document.getElementById("voyp").style.display = "none";
- });
-
- const host = window.location.hostname.split(".").slice(-2, -1)[0];
- if (!host) return;
-
- const p = new Promise((resolve, reject) => {
- switch (host) {
- case "afdian":
- GM.xmlHttpRequest({
- url: `https://afdian.net/api/user/get-profile-by-slug?url_slug=${document.location.pathname.split("/")[2]}`,
- onload: (response) =>
- resolve(JSON.parse(response.responseText).data.user.user_id),
- });
- break;
- case "candfans":
- resolve(
- new URL(
- document
- .querySelector("meta[property='og:image']")
- .getAttribute("content"),
- ).pathname.split("/")[2],
- );
- break;
- case "discord":
- switch (document.location.pathname.split("/")[1]) {
- case "channels":
- resolve(document.location.pathname.split("/")[2]);
- break;
- case "invite":
- GM.xmlHttpRequest({
- url: `https://discord.com/api/v10/invites/${document.location.pathname.split("/")[2]}`,
- onload: (response) => {
- resolve(JSON.parse(response.responseText).guild.id);
- },
- });
- break;
- }
- break;
- case "dlsite":
- resolve(document.location.pathname.split("/")[6]);
- break;
- case "fanbox": {
- let creatorId = document.location.hostname.split(".").slice(-3, -2)[0];
- if (creatorId === "www")
- creatorId = window.location.pathname.split("/")[1].slice(1);
- GM.xmlHttpRequest({
- url: `https://api.fanbox.cc/creator.get?creatorId=${creatorId}`,
- headers: { Origin: "https://fanbox.cc" },
- onload: (response) => {
- try {
- resolve(JSON.parse(response.responseText).body.user.userId);
- } catch {
- resolve(
- new URL(
- JSON.parse(
- document.querySelector("script[type='application/ld+json']")
- .textContent,
- )[0].image,
- ).pathname.split("/")[7],
- );
- }
- },
- });
- break;
- }
- case "fansly": {
- GM.xmlHttpRequest({
- url: `https://apiv3.fansly.com/api/v1/account?usernames=${document.location.pathname.split("/")[1]}`,
- onload: (response) =>
- resolve(JSON.parse(response.responseText).response[0].id),
- });
- break;
- }
- case "fantia":
- if (document.location.pathname.split("/")[1] === "fanclubs") {
- resolve(document.location.pathname.split("/")[2]);
- } else {
- resolve(
- document
- .querySelector(".fanclub-header a")
- .getAttribute("href")
- .split("/")[2],
- );
- }
- break;
- case "gumroad":
- resolve([
- document.location.hostname.split(".")[0],
- JSON.parse(
- document.getElementsByClassName("js-react-on-rails-component")[0]
- .textContent,
- ).creator_profile.external_id,
- ]);
- break;
- case "patreon":
- window.addEventListener("load", () => {
- resolve(
- unsafeWindow.patreon.bootstrap.campaign.data.relationships.creator
- ?.data.id ||
- unsafeWindow.patreon.bootstrap.post?.data.relationships.user.data
- .id,
- );
- });
- break;
- case "boosty":
- case "onlyfans":
- case "subscribestar":
- resolve(document.location.pathname.split("/")[1]);
- break;
- }
- });
-
- p.then((u) => {
- let users = u;
- if (!Array.isArray(users)) users = [users];
-
- for (const user of users) {
- for (const x of sites) {
- if (
- cache[x.name] &&
- cache[x.name][host] &&
- cache[x.name][host].includes(user)
- )
- return show(x, host, user, true);
- GM.xmlHttpRequest({
- url: x.check,
- method: "GET",
- onload: (response) => {
- if (x.get(response, host, user)) {
- show(x, host, user, true);
- } else {
- show(x, host, user, false);
- }
- },
- });
- }
- }
- });
-
- function show(site, host, user, success) {
- const name = site.name;
- const url = site.profile.replace("$HOST", host).replace("$USER", user);
-
- document.getElementById("voyp").getElementsByTagName("span")[0]?.remove();
-
- document
- .getElementById("voyp")
- .insertAdjacentHTML(
- "beforeend",
- `<br><b>${name}:</b> ${
- success
- ? `<a href="${url}">${url}</a>`
- : `User ${user} not found in platform's database.`
- }`,
- );
-
- if (!success) return;
-
- if (!cache[site.name]) cache[site.name] = {};
- if (!cache[site.name][host]) cache[site.name][host] = [];
- if (!cache[site.name][host].includes(user))
- cache[site.name][host].push(user);
- GM.setValue("cache2", cache);
- }
- })();