您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
This script will hide models that you add to block list
// ==UserScript== // @name PornHub | Hide Block List Models // @namespace http://tampermonkey.net/ // @version 1.0.5 // @description This script will hide models that you add to block list // @author extra.lewd // @match https://*.pornhub.com/* // @exclude https://*.pornhub.com/model/* // @exclude https://*.pornhub.com/channels/* // @exclude https://*.pornhub.com/categories // @icon https://pornhub.com/favicon.ico // @run-at document-end // @license CC BY-NC-ND 4.0 // @license-url https://creativecommons.org/licenses/by-nc-nd/4.0/ // @homepage https://discord.gg/TcWrM6pXWD // @homepageURL https://discord.gg/TcWrM6pXWD // @website https://discord.gg/TcWrM6pXWD // @source https://discord.gg/TcWrM6pXWD // @compatible firefox // @compatible chrome // @compatible opera // @compatible safari // @compatible edge // @grant GM_addStyle // @grant GM_registerMenuCommand // @grant GM.getValue // @grant GM.setValue // ==/UserScript== (function () { 'use strict'; class PCache { cache; storageKey = "blocked_phub"; constructor(cache = new Map()) { this.cache = cache; this.load(); } has(key) { return this.cache.has(key); } get(key) { const cache = this.cache.get(key); if (!!cache && cache.ttl > Date.now()) { return cache.value; } this.del(key); } set(key, value, ttl) { this.cache.set(key, { value, ttl: Date.now() + ttl }); this.save(); } del(key) { this.cache.delete(key); this.save(); } clear() { this.cache.clear(); this.save(); } async wrap(key, callback, ttl) { const cache = this.cache.get(key); if (cache) { return cache.value; } const value = await callback(); this.set(key, value, ttl); return value; } save() { localStorage.setItem(this.storageKey, JSON.stringify([...this.cache.entries()])); } load() { const cache = localStorage.getItem(this.storageKey); if (cache) { try { this.cache = new Map(JSON.parse(cache)); } catch (error) { } } } } GM_addStyle(` .phub-blur { filter: blur(10px); } .phub-hide { display: none !important; } .phub-blur:hover,.phub-blur:active,.phub-blur:focus { filter: none; } `); const cache = new PCache(); let busy = false; const P_CLEAR = "phub-clear"; const P_SKIP = "phub-skip"; const P_BLUR = "phub-blur"; const P_HIDE = "phub-hide"; scan(); // todo: add also hide by .languageType span, but it require phub spoken tag on videos // todo: add hide channels feature setInterval(scan, 10_000); async function scan() { if (!checkAuth()) return; // we don't have block list, cuz we don't logined yet if (busy) return; try { busy = true; const videos = document.querySelectorAll(`li.videoblock:not(.${P_CLEAR}):not(.${P_HIDE}):not(.${P_BLUR}):not(.${P_SKIP}),li[data-video-id]:not(.${P_CLEAR}):not(.${P_HIDE}):not(.${P_BLUR}):not(.${P_SKIP})`); if (videos.length < 1) { return; } const mode = await GM.getValue("phub_mode", 0); for (const video of videos) { try { // note: some (as germancouple1) has only "span.username" without href / url instead of "a". Devs? const path = video.querySelector(".usernameWrap")?.querySelector("a")?.getAttribute("href") || video.querySelector(".uploaderLink")?.getAttribute("href"); if (!path) { console.warn("Skip", video); video.classList.add(P_SKIP); continue; } ; const url = new URL(path, window.location.href); // we cache any value to prevent a lot of queries, purge cache on add block listed const blocked = await cache.wrap(url.href, async () => { return fetch(url, { credentials: "include" }) .then(r => r.text()) .then(isBlocked); }, 1000 * 60 * 60); // 1 hour video.classList.add(blocked ? mode ? P_HIDE : P_BLUR : P_CLEAR); } catch (error) { console.error(error, video); } } } catch (error) { console.error(error); } finally { busy = false; } } // note: phub developers clowns a bit, so... function isBlocked(html) { const p = new DOMParser(); const doc = p.parseFromString(html, "text/html"); // Currently, support only on eng lang // new method: // .profileBlockIcon -> desktop // i.ph-icon-cancel -> mobile const pc = doc.querySelector(".profileBlockIcon")?.parentElement?.textContent?.trim().toLowerCase() === "unblock user"; const mobile = doc.querySelector("i.ph-icon-cancel")?.parentElement?.textContent?.trim().toLowerCase() === "unblock user"; return pc || mobile; } function checkAuth() { return !!document.querySelector(".js_userName"); } GM_registerMenuCommand("Blur Blocked (Default)", () => { GM.setValue("phub_mode", 0); }); GM_registerMenuCommand("Hide Blocked", () => { GM.setValue("phub_mode", 1); }); GM_registerMenuCommand("Clear Cache", () => { cache.clear(); }); // GM_registerMenuCommand("Check Block (Dev only)", () => { // const isBlocked = !document.querySelector(".addFriendButton:not(.disabled)") && !!document.querySelector(".addFriendButton"); // GM.notification({ // text: `Target on current page ${isBlocked ? "IN" : "NOT in"} block list.`, // title: "Debug", // }) // }) })();