BetterBoyFriendTV

优化 BoyFriendTV 浏览体验:去除导航栏广告、直播卡片、底栏与边栏冗余区块。

Vous devrez installer une extension telle que Tampermonkey, Greasemonkey ou Violentmonkey pour installer ce script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

Vous devrez installer une extension telle que Tampermonkey ou Violentmonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey ou Userscripts pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey pour installer ce script.

Vous devrez installer une extension de gestionnaire de script utilisateur pour installer ce script.

(J'ai déjà un gestionnaire de scripts utilisateur, laissez-moi l'installer !)

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

(J'ai déjà un gestionnaire de style utilisateur, laissez-moi l'installer!)

// ==UserScript==
// @name         BetterBoyFriendTV
// @namespace    BetterGayPornSites
// @version      0.1.5
// @author       AtmoOmen
// @description  优化 BoyFriendTV 浏览体验:去除导航栏广告、直播卡片、底栏与边栏冗余区块。
// @match        https://www.boyfriendtv.com/*
// @match        https://boyfriendtv.com/*
// @run-at       document-start
// @license      MIT
// @grant        GM_registerMenuCommand
// @grant        GM_unregisterMenuCommand
// @grant        GM_getValue
// @grant        GM_setValue
// ==/UserScript==

(function () {
  "use strict";

  const FEATURES = {
    nav: { key: "betterBftvNav", label: "精简导航栏" },
    topbar: { key: "betterBftvTopbar", label: "精简顶栏" },
    live: { key: "betterBftvLive", label: "精简视频流" },
    footer: { key: "betterBftvFooter", label: "精简底栏" },
    sidebar: { key: "betterBftvSidebar", label: "精简边栏" },
    float: { key: "betterBftvFloat", label: "精简浮动组件" },
    player: { key: "betterBftvPlayer", label: "精简视频播放" },
  };

  const state = {};
  const commandIds = {};
  let bannerPatched = false;
  let menuRetries = 0;

  const loadValue = (key, fallback) => {
    try {
      return GM_getValue(key, fallback);
    } catch (e) {
      return fallback;
    }
  };

  for (const name in FEATURES) {
    state[name] = loadValue(FEATURES[name].key, true);
  }

  const normalize = (value) => (value || "").replace(/\s+/g, " ").trim();

  function cleanNav() {
    for (const item of document.querySelectorAll("ul.navmenu > li")) {
      const link = item.querySelector(":scope > a");
      if (link && ["Live Sex", "AI Lover", "Creators"].includes(normalize(link.textContent))) {
        item.remove();
      }
    }
  }

  function cleanLive() {
    for (const tile of document.querySelectorAll("li.mtile-x7")) {
      const isLive =
        tile.querySelector('a[href*="/link/out/cam"]') !== null ||
        [...tile.querySelectorAll(".js-rating-text-percent")].some((el) => normalize(el.textContent) === "LIVE");
      if (isLive) {
        tile.remove();
      }
    }
    for (const tile of document.querySelectorAll("div.content-item.video-item-wrapper")) {
      const isLive =
        tile.querySelector('a[href*="/link/out/cam"]') !== null ||
        [...tile.querySelectorAll(".content-rating")].some((el) => normalize(el.textContent) === "LIVE");
      if (isLive) {
        tile.remove();
      }
    }
    if (document.body && document.body.classList.contains("js-mobile")) {
      for (const frame of document.querySelectorAll('iframe[src*="janusaent.com/widgets"], iframe[src*="mob-foot.html"]')) {
        const box = frame.closest(".box-listing");
        if (box) {
          box.remove();
        } else {
          const block = frame.closest(".content-block");
          if (block) {
            block.remove();
          }
        }
      }
    }
    for (const heading of document.querySelectorAll("h2.amateur-promo-heading")) {
      if (normalize(heading.textContent).toLowerCase() === "best creator videos") {
        const list = heading.nextElementSibling;
        heading.remove();
        if (list && list.classList.contains("ast-model-promo-list")) {
          list.remove();
        }
      }
    }
  }

  function cleanTopbar() {
    const labels = ["Follow us @ Twitter", "Shemale Porn", "Fetish Porn", "Premium Male Access"];
    for (const item of document.querySelectorAll("ul.pull-left.topmenu > li")) {
      const link = item.querySelector(":scope > a");
      if (link && labels.includes(normalize(link.textContent))) {
        item.remove();
      }
    }
    const mobileLabels = ["AI Boys", "Pornstars", "Creators", "Porn AI"];
    for (const item of document.querySelectorAll("ul.top-links-menu > li")) {
      const link = item.querySelector(":scope > a");
      if (link && mobileLabels.includes(normalize(link.textContent))) {
        item.remove();
      }
    }
  }

  function cleanFloat() {
    for (const el of document.querySelectorAll("#js-chat-bubble, .chat-bubble, #js-chat-panel")) {
      el.remove();
    }
  }

  function patchBannerManager() {
    if (bannerPatched) {
      return;
    }
    let manager = window.CookieManager;
    Object.defineProperty(window, "CookieManager", {
      configurable: true,
      get() {
        return manager;
      },
      set(value) {
        manager = value;
        if (value && typeof value.getBannerDisplayed === "function") {
          const originalGetBanner = value.getBannerDisplayed;
          value.getBannerDisplayed = function (key) {
            if (key === "schat") {
              return true;
            }
            return originalGetBanner.call(this, key);
          };
        }
      },
    });
    window.CookieManager = manager;
    bannerPatched = true;
  }

  function cleanPlayer() {
    patchBannerManager();
    const isPlayerArea = (el) =>
      el && (el.id === "mediaplayer_wrapper" || el.id === "video-details" || el.classList.contains("video-detail") ||
        el.querySelector("#mediaplayer_wrapper, #video-details, .video-detail") !== null);
    for (const overlay of document.querySelectorAll(".az6-overlay")) {
      overlay.remove();
    }
    if (document.body) {
      document.body.classList.remove("az6-overlay-shown");
    }
    if (!location.pathname.startsWith("/videos/")) {
      return;
    }
    for (const ad of document.querySelectorAll("a.ads-stripchat-link")) {
      const item = ad.parentElement && ad.parentElement.tagName === "LI" ? ad.parentElement : ad.closest("li");
      if (item && !isPlayerArea(item)) {
        item.remove();
      } else if (!isPlayerArea(ad)) {
        ad.remove();
      }
    }
    const rightside = document.querySelector(".rightside2");
    if (rightside && !isPlayerArea(rightside)) {
      rightside.remove();
      const main = document.querySelector("#maincolumn2");
      if (main) {
        main.classList.remove("has-right-ads");
      }
    }
    for (const block of document.querySelectorAll(".info-block-wrapper")) {
      if (!isPlayerArea(block)) {
        block.remove();
      }
    }
    for (const wrapper of document.querySelectorAll(".zn-wrapper")) {
      if (!isPlayerArea(wrapper)) {
        wrapper.remove();
      }
    }
    for (const script of document.querySelectorAll("script")) {
      if (/fh-wgt\.com|FapHouse/i.test(script.textContent)) {
        const host = script.previousElementSibling;
        if (host && host.tagName === "DIV" && !isPlayerArea(host)) {
          host.remove();
        }
        script.remove();
      }
    }
    for (const label of document.querySelectorAll(".zn-label")) {
      const host = label.closest("div");
      if (host && !isPlayerArea(host)) {
        host.remove();
      }
    }
    for (const ad of document.querySelectorAll("a.q1-line, a.below-video-link")) {
      const host = ad.parentElement;
      if (host && host.tagName === "DIV" && !isPlayerArea(host)) {
        host.remove();
      } else if (!isPlayerArea(ad)) {
        ad.remove();
      }
    }
  }

  function cleanFooter() {
    const headings = [
      "Videos of BoyFriendTV Creators",
      "Last commented models",
      "Chat with LIVE models",
      "Last commented videos",
    ];
    for (const wrapper of document.querySelectorAll(".js-toggle-content-wrapper")) {
      const menu = wrapper.querySelector(".js-toggle-content-menu");
      if (menu && headings.some((heading) => normalize(menu.textContent).startsWith(heading))) {
        wrapper.remove();
      }
    }
    for (const wrapper of document.querySelectorAll(".js-toggle-content-wrapper.block-amateur")) {
      wrapper.remove();
    }
    for (const heading of document.querySelectorAll("#main-v6 > div.vtab")) {
      const block = heading.nextElementSibling;
      if (block && block.classList.contains("content-block") && block.classList.contains("default-padding")) {
        heading.remove();
        block.remove();
      }
    }
    for (const heading of document.querySelectorAll("h3")) {
      if (normalize(heading.textContent).startsWith("Chat with LIVE models")) {
        const container = heading.parentElement;
        if (container && container.classList.contains("container")) {
          container.remove();
        }
      }
    }
  }

  function cleanSidebar() {
    for (const title of document.querySelectorAll(".left-menu-box-title")) {
      const text = normalize(title.textContent);
      if (text === "Recommended") {
        const block = title.closest(".left-menu-box");
        if (block) {
          block.remove();
        }
      } else if (text === "Top Creators" || text === "Top Pornstars") {
        const sibling = title.nextElementSibling;
        title.remove();
        if (sibling && (sibling.tagName === "DIV" || sibling.tagName === "UL")) {
          sibling.remove();
        }
      }
    }
    const tagsBlock = [...document.querySelectorAll(".left-menu-box")].find(
      (box) => box.querySelector(":scope > .left-menu-box-title") &&
        normalize(box.querySelector(":scope > .left-menu-box-title").textContent) === "Tags"
    );
    if (tagsBlock) {
      const homeList = [...tagsBlock.querySelectorAll("ul.left-menu-box")].find((list) =>
        [...list.querySelectorAll("a")].some((link) => normalize(link.textContent) === "Home")
      );
      if (homeList) {
        for (const item of [...homeList.children]) {
          const link = item.querySelector(":scope > a");
          if (!link) {
            continue;
          }
          const text = normalize(link.textContent);
          if (link.querySelector('img[alt="Country flag"]') || text === "Creators" || text === "Live Cam Models") {
            item.remove();
          }
        }
      }
    }
    const mobileLabels = ["Creators' Videos", "Pornstars", "Creators", "Live Cam Models", "Live Sex"];
    for (const item of document.querySelectorAll("div.navmenu.navbar-default > ul.navmenu-nav > li")) {
      const link = item.querySelector(":scope > a");
      if (link && mobileLabels.includes(normalize(link.textContent))) {
        item.remove();
      }
    }
    if (document.body && document.body.classList.contains("js-mobile")) {
      for (const item of document.querySelectorAll(".trending-tags > li")) {
        const link = item.querySelector(":scope > a");
        if (link && link.querySelector('img[alt="Country flag"]')) {
          item.remove();
        }
      }
    }
  }

  function clean() {
    if (state.nav) {
      cleanNav();
    }
    if (state.topbar) {
      cleanTopbar();
    }
    if (state.live) {
      cleanLive();
    }
    if (state.footer) {
      cleanFooter();
    }
    if (state.sidebar) {
      cleanSidebar();
    }
    if (state.float) {
      cleanFloat();
    }
    if (state.player) {
      cleanPlayer();
    }
  }

  function registerMenus() {
    try {
      for (const name in FEATURES) {
        const feature = FEATURES[name];
        if (commandIds[name] != null) {
          GM_unregisterMenuCommand(commandIds[name]);
        }
        commandIds[name] = GM_registerMenuCommand(`${feature.label}:${state[name] ? "开" : "关"}`, () => {
          state[name] = !state[name];
          GM_setValue(feature.key, state[name]);
          registerMenus();
          clean();
        });
      }
    } catch (e) {
      menuRetries += 1;
      if (menuRetries < 20) {
        setTimeout(registerMenus, 500);
      }
    }
  }

  let scheduled = false;
  const observer = new MutationObserver(() => {
    if (scheduled) {
      return;
    }
    scheduled = true;
    setTimeout(() => {
      scheduled = false;
      clean();
    }, 0);
  });

  function start() {
    registerMenus();
    clean();
    observer.observe(document.documentElement, { childList: true, subtree: true });
    document.addEventListener(
      "click",
      (e) => {
        if (!state.player) {
          return;
        }
        const target = e.target;
        if (!target || typeof target.closest !== "function") {
          return;
        }
        const pop = target.closest(".js-pop");
        if (pop && pop.querySelector("a[href]")) {
          e.stopPropagation();
        }
      },
      true
    );
    window.addEventListener("pageshow", clean);
    document.addEventListener("DOMContentLoaded", clean);
    window.addEventListener("load", clean);
    setTimeout(clean, 1000);
    setTimeout(clean, 3000);
  }

  if (document.documentElement) {
    start();
  } else {
    document.addEventListener("DOMContentLoaded", start);
  }
})();