YouTube DeBlock

Fully Working 2023 UnBlocker for YouTube. Get rid of that pesky blocker, and return my vids!

目前为 2023-12-21 提交的版本。查看 最新版本

// ==UserScript==
// @name            YouTube DeBlock
// @description     Fully Working 2023 UnBlocker for YouTube. Get rid of that pesky blocker, and return my vids!
// @author          YelloNolo
// @version         1.1.3
// @created         2023-10-10
// @namespace       https://yello.zip
// @homepage        https://github.com/YelloNolo/YouTube-Adblock
// @match           *://www.youtube.com/*
// @grant           none
// ==/UserScript==

(function () {
  "use strict";

  /* User Customization */
  const disableTheaterToggle = false;
  const disableReloadToggle = false;
  const disableOptionsMenu = false;
  /* Language INFO: https://github.com/YelloNolo/YouTube-UnBlock/blob/main/language.md */
  const language = "en";
  /* End User Customization */

  // Any class the blocker uses
  const blockerClass = "ytd-enforcement-message-view-model";
  // Any class on the broken video (e.x. yt-playability-error-supported-renderers)
  const ogVideoClass = "yt-playability-error-supported-renderers";
  // Class of the parent for the custom content locaiton
  const customContentParentID = "end";
  // Original Youtube URL
  const youtubeURL = "youtube.com";
  // Change to theater mode on load
  const changeTheaterOnStart = false;

  // Domains to redirect to.
  var domainList = ["youtube.com/embed", "yout-ube.com", "nsfwyoutube.com"];

  // --- Do Not Touch --- //
  var newDomain = domainList[0];
  // Temp Functions //
  const tempReplaceClass = "replaceme";
  let isBlocked = false;
  let isSubChange = false;
  let isChangingFrame = false;
  var updatedURL = window.location.href;
  var previousDropdownValue;
  // --- Do Not Touch --- //

  // -------------- Main Loop Funcitons -------------- //

  // Function that checks if the page is even blocked
  var theaterStartRunOnce = false;
  function checkClass() {
    const elements = document.querySelectorAll("." + blockerClass);

    if (elements.length > 0) {
      isBlocked = true;
      console.log("blocked [checkClass]: yes");
    }

    if (isBlocked) {
      console.log("Replacing Original [checkClass]");
      replaceVideo();
      addDomainToURLs();
      isBlocked = false;
    } else {
      // console.log("[checkClass] #2") - Cogs Log
      urlTracker();
      dropdownTracker();
    }

    if (changeTheaterOnStart && !theaterStartRunOnce) {
      changeTheaterMode(true);
      theaterStartRunOnce = true;
    }
  }

  // -------------- Event Listeners -------------- //

  // Checks if the url has changed, if so, reload the iframe (thus reloading the video)
  function urlTracker() {
    var currentURL = window.location.href;
    // console.log("Test URL [urlTracker]"); - Cogs Log
    if (currentURL != updatedURL) {
      console.log("Found New URL");
      updatedURL = window.location.href;
      if (isSubChange) {
        isBlocked = true;
      }
    }
  }

  // Checks if the dropdown has changed
  function dropdownTracker() {
    var dropdown = document.getElementById("dropdown");
    if (dropdown) {
      changeSelection(dropdown);
    }
  }

  function changeSelection(dropdown) {
    try {
      dropdown.addEventListener("change", function () {
        checkDropdownChange(dropdown);
      });
    } catch (error) {
      console.error("Error [changeSelection]: " + error);
    }
  }

  function checkDropdownChange(dropdown) {
    var newValue = dropdown.value;

    // Check if the value has actually changed
    if (newValue !== previousDropdownValue) {
      newDomain = domainList[newValue];
      console.log("Selection Changed: " + newDomain);
      reloadFrame();

      // Update the previousValue variable
      previousDropdownValue = newValue;
    }
  }

  // Reload Frame when "Reload Frame" button is clicked
  function reloadFrame() {
    replaceVideo();
    addDomainToURLs();

    console.log("clicked");
  }

  // -------------- Checks -------------- //

  function appendingFrame(isSet) {
    if (isSet == true) {
      isChangingFrame = true;
    } else {
      isChangingFrame = false;
    }
    console.log("ChangingFrames: " + changingFrame);
  }

  // -------------- Actions -------------- //

  // Remove the *blocker* from the page by locating the class name.
  function removeElementsByClassName(removeClass) {
    console.log("Removing [removeElementsByClassName]: " + removeClass);
    const elements = document.querySelectorAll("." + removeClass);
    try {
      elements.forEach((element) => {
        element.remove();
      });
    } catch (error) {
      console.error(
        "Error removing elements [removeElementsByClassName]: " + error
      );
    }
  }

  // Checks string and returns if contains matching text
  function checkText(string, text) {
    console.log("Checking string [checkText]");
    return string.includes(text);
  }

  // Function to replace "youtube.com" with selected domain
  function getNewURL(newDomain) {
    console.log("New URL [newDomain]: " + newDomain);
    const currentURL = window.location.href;
    try {
      if (currentURL.includes(youtubeURL)) {
        const newURL = currentURL.replace(youtubeURL, newDomain);
        return newURL;
      }
    } catch (error) {
      console.error("Error [newURL]: " + error);
    }
  }

  // Adds "youtube.com" to all nameless urls on webpage
  function addDomainToURLs() {
    console.log("Adding [addDomainToURLs]");
    const links = document.querySelectorAll("a");

    try {
      links.forEach((link) => {
        let href = link.getAttribute("href");
        if (href && !href.startsWith("http") && !href.startsWith("www")) {
          href = "https://www." + youtubeURL + href;

          link.setAttribute("href", href);
        }
      });
    } catch (error) {
      console.error("[addDomainToURLs] #1", error);
    }
  }

  // Is it the first video change, or recurring?
  function replaceVideo() {
    if (!isSubChange) {
      console.log("replacing [replaceVideo]");
      removeElementsByClassName(blockerClass);
      createJFrame(ogVideoClass);
      isSubChange = true;

      return;
    }
    if (isSubChange) {
      console.log("replacing subclick [replaceVideo]");
      removeOgIframe();
      createJFrame(tempReplaceClass);
      console.log("In with the new [replaceVideo]");
    }

    isBlocked = false;
  }

  // Fixes unusable URL(s)
  function fixURL(URL) {
    const playlistCheck = "&list=";
    const watchStamp = "watch?v=";
    const timestamp = "&t=";
    const isURL = checkText(URL, youtubeURL);
    const isPlaylist = checkText(URL, playlistCheck);
    const isTimestamp = checkText(URL, timestamp);

    console.log(
      "isURL:" +
        isURL +
        " isPlaylist:" +
        isPlaylist +
        " isTimestamp:" +
        isTimestamp
    );
    console.log("URL [fixURL]:" + URL);

    if (isURL) {
      URL = URL.replace(watchStamp, "");
      console.log("Is Not Playlist [fixURL]: " + URL);
    }
    if (isURL && isTimestamp) {
      URL = URL.split("&t=")[0];
      console.log("URL Split Timestamp Fix [fixURL]: " + URL);
    }
    if (isPlaylist) {
      URL = URL.split(playlistCheck)[0];
      console.log("URL Split Playlist Fix [fixURL]: " + URL);
    }

    return URL;
  }

  var theaterModeToggle = true;
  function toggleTheater() {
    if (theaterModeToggle) {
      console.log("Changing Mode [toggleTheater]: Off");
      theaterModeToggle = false;
    } else {
      console.log("Changing Mode [toggleTheater]: On");
      theaterModeToggle = true;
    }
    changeTheaterMode(theaterModeToggle);
  }

  // Thanx https://stackoverflow.com/questions/53584026/toggle-the-cinema-mode-on-youtube-with-javascript :>
  function changeTheaterMode(state) {
    try {
      const collection = document.getElementsByTagName("ytd-watch-flexy");
      const ytd_watch_flexy = collection.item(0);
      if (!ytd_watch_flexy) {
        console.error("No ytd-watch-flexy Found[changeTheaterMode]");
        return;
      }

      if (state) {
        ytd_watch_flexy.theater = true;
        console.log("Theater On [changeTheaterMode]");
      } else {
        ytd_watch_flexy.theater = false;
        console.log("Theater Off [changeTheaterMode]");
      }
    } catch (error) {
      console.error("Theater-Mode Error [changeTheaterMode]: " + error);
    }
  }

  // -------------- JFrame Control -------------- //
  // Create video embedding frame
  function createJFrame(classToOverturn) {
    var newURL = getNewURL(newDomain);
    const elements = document.querySelectorAll("." + classToOverturn);
    console.log("newURL Beginning [createJFrame]: " + newURL);

    newURL = fixURL(newURL);

    try {
      elements.forEach((element) => {
        const iframe = document.createElement("iframe");
        iframe.width = "100%";
        iframe.height = "100%";
        iframe.src = newURL;
        iframe.allow =
          "accelerometer; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share";
        // autoplay                   ^ (autoplay removed after bug; also never worked anyhow)
        iframe.allowFullscreen = true;
        iframe.zIndex = "9999";

        // Replace the existing element with the custom URL
        element.parentNode.replaceChild(iframe, element);
        console.log("Modified URL:", newURL);
      });
    } catch (error) {
      console.error("Error creating iframe [createJFrame]: " + error);
    }
  }

  // Removes the original video frame.
  function removeOgIframe() {
    const iframes = document.querySelectorAll("iframe");

    console.log("removing jFrame [removeOgIframe]");
    try {
      iframes.forEach((iframe) => {
        const paragraph = document.createElement("p");
        paragraph.className = tempReplaceClass;
        iframe.parentNode.insertBefore(paragraph, iframe);
        iframe.remove();
        console.log("Out with the old [removeOgIframe]");
      });
    } catch (error) {
      console.error("Error [removeOgIframe]: " + error);
    }
  }

  // -------------- Experimental Features (unused) -------------- //

  // Save dropdown selection to local storage
  function dropdownStore() {
    try {
      rememberButton.addEventListener("click", function () {
        localStorage.setItem("selectedOption", dropdown.value);
        console.log("Selection Stored");
      });
    } catch (error) {
      console.error("Error [dropdownStore]: " + error);
    }
  }

  // -------------- Language Information -------------- //

  // xtxt is for the type of text needed (i.e. dropdown / theater mode button / ect...) for the main category of text.
  // vers is the item in the container (i.e. dropdown: youtube, yout-ube) for multiple subtexts.
  // top tier explination!
  function getText(xtxt, vers) {
    const translations = {
      en: {
        theaterMode: ["Theater"],
        reloadFrame: ["Reload Frame"],
        dropdown: ["YouTube™", "YouTube [Embed]"],
      },
      de: {
        theaterMode: ["Theater"],
        reloadFrame: ["Rahmen neu laden"],
        dropdown: ["YouTube™", "YouTube [Einbetten]"],
      },
      es: {
        theaterMode: ["Teatro"],
        reloadFrame: ["Recargar Marco"],
        dropdown: ["YouTube™", "YouTube [Incrustar]"],
      },
      fr: {
        theaterMode: ["Théâtre"],
        reloadFrame: ["Recharger le Cadre"],
        dropdown: ["YouTube™", "YouTube [Intégrer]"],
      },
      it: {
        theaterMode: ["Teatro"],
        reloadFrame: ["Ricarica Cornice"],
        dropdown: ["YouTube™", "YouTube [Incorpora]"],
      },
      jp: {
        theaterMode: ["劇場"],
        reloadFrame: ["フレームを再読み込み"],
        dropdown: ["YouTube™", "YouTube [埋め込み]"],
      },
      ko: {
        theaterMode: ["극장"],
        reloadFrame: ["프레임 다시 로드"],
        dropdown: ["YouTube™", "YouTube [임베드]"],
      },
      nl: {
        theaterMode: ["Theater"],
        reloadFrame: ["Frame Herladen"],
        dropdown: ["YouTube™", "YouTube [Insluiten]"],
      },
      pl: {
        theaterMode: ["Teatr"],
        reloadFrame: ["Przeładuj Ramkę"],
        dropdown: ["YouTube™", "YouTube [Osadź]"],
      },
      pt: {
        theaterMode: ["Teatro"],
        reloadFrame: ["Recarregar Quadro"],
        dropdown: ["YouTube™", "YouTube [Embutir]"],
      },
      ru: {
        theaterMode: ["Театр"],
        reloadFrame: ["Перезагрузить Рамку"],
        dropdown: ["YouTube™", "YouTube [Вставить]"],
      },
      ar: {
        theaterMode: ["مسرح"],
        reloadFrame: ["إعادة تحميل الإطار"],
        dropdown: ["YouTube™", "YouTube [تضمين]"],
      },
      zh: {
        theaterMode: ["剧院"],
        reloadFrame: ["重新加载框架"],
        dropdown: ["YouTube™", "YouTube [嵌入]"],
      },
      hi: {
        theaterMode: ["रंगमंच"],
        reloadFrame: ["फ्रेम पुनः लोड करें"],
        dropdown: ["YouTube™", "YouTube [एम्बेड करें]"],
      },
      sv: {
        theaterMode: ["Teater"],
        reloadFrame: ["Ladda om Ramen"],
        dropdown: ["YouTube™", "YouTube [Bädda in]"],
      },
      no: {
        theaterMode: ["Teater"],
        reloadFrame: ["Last Inn Rammen på Nytt"],
        dropdown: ["YouTube™", "YouTube [Bygg inn]"],
      },
      da: {
        theaterMode: ["Teater"],
        reloadFrame: ["Genindlæs Rammen"],
        dropdown: ["YouTube™", "YouTube [Vložit]"],
      },
      cs: {
        theaterMode: ["Divadlo"],
        reloadFrame: ["Rámeček znovu načíst"],
        dropdown: ["YouTube™", "YouTube [Vložit]"],
      },
      hu: {
        theaterMode: ["Színház"],
        reloadFrame: ["Keret Újratöltése"],
        dropdown: ["YouTube™", "YouTube [Beágyazás]"],
      },
      tr: {
        theaterMode: ["Tiyatro"],
        reloadFrame: ["Çerçeveyi Yeniden Yükle"],
        dropdown: ["YouTube™", "YouTube [Gömme]"],
      },
    };

    const languageTranslations = translations[language];
    return languageTranslations && languageTranslations[xtxt]
      ? languageTranslations[xtxt][vers]
      : undefined;
  }

  // -------------- Custom HTML Start -------------- //

  // Custom CSS
  var css = `
    .btn-style {
        position: relative;
        display: inline-block;
        padding: 9px;
        height: 40px;
        color: white;
        background-color: transparent;
        box-shadow: none;
        text-shadow: none;
        border: 1px solid white;
        border-radius: 0px;
        z-index: 9999;
        opacity: 100%;
        transition: transform 0.3s ease;
        user-select: none;
    }
    .btn-style:hover {
        opacity: 80%;
    }
    .main-btn {
        margin-right: 16px;
        transition: transform 0.1s ease;
    }
    .main-btn:active {
        border: 1px solid transparent !important;
        transform: scale(0.9);
    }
    .dropdown-content {
        position: relative;
        background-color: black;
    }
    .custom-container {
        border: none;
        display: inline-block;
        margin-right: 16px;
        margin-left: 16px;
        transition: transform 0.3s ease;

    }
    `;

  var ranCustomContentOnce = false;
  function customContent() {
    // Create Container
    var customContainer = document.createElement("div");
    customContainer.classList.add("custom-container");
    /* Not Complete */
    // Create Button Theater Mode
    var theaterButton = document.createElement("button");
    theaterButton.textContent = getText("theaterMode", 0);
    theaterButton.classList.add("btn-style", "main-btn");
    /**/
    // Create Button Reload
    var reloadButton = document.createElement("button");
    reloadButton.textContent = getText("reloadFrame", 0);
    reloadButton.classList.add("btn-style", "main-btn");

    // Create Dropdown Menu
    var dropdownButton = document.createElement("select");
    dropdownButton.id = "dropdown";
    dropdownButton.classList.add("btn-style");
    const options = [
      { value: "0", text: getText("dropdown", 0) },
      { value: "1", text: getText("dropdown", 1) },
      //{ value: "2", text: "NSFW YouTube [Broken!]" } Fix later?
    ];
    var htmlContent = options
      .map(
        (option) =>
          `<option class="dropdown-content" value="${option.value}">${option.text}</option>`
      )
      .join("");
    dropdownButton.innerHTML = htmlContent;

    // -------------- Custom HTML End -------------- //

    // ----- Appending custom content to page ----- //

    // Add items to Container

    if (!disableTheaterToggle) {
      customContainer.appendChild(theaterButton);
    }
    if (!disableReloadToggle) {
      customContainer.appendChild(reloadButton);
    }
    if (!disableOptionsMenu) {
      customContainer.appendChild(dropdownButton);
    }

    // Append CSS to page
    var style = document.createElement("style");
    style.appendChild(document.createTextNode(css));
    document.head.appendChild(style);

    // Find ID Location
    var exsistingParent = document.getElementById(customContentParentID);
    console.log("Found exsistingParent: " + exsistingParent);

    // Add Container to Page
    exsistingParent.insertBefore(customContainer, exsistingParent.firstChild);
    console.log("Added customContainer to page");

    theaterButton.addEventListener("click", toggleTheaterStingyWorkaround);
    reloadButton.addEventListener("click", reloadFrame);

    ranCustomContentOnce = true;
  }

  try {
    if (!ranCustomContentOnce) {
      customContent();
      console.log("Loaded [customContent]");
    } else {
      console.log("Tried to load, but was allready loaded [customContent]");
    }
  } catch (error) {
    console.error("Error [customContent]: " + error);
  }

  // -------------- Active Listeners -------------- //

  // Run every second to check for updates on page (Will not ping any server till a new page is clicked)
  try {
    window.setInterval(checkClass, 1000);
  } catch (error) {
    console.error("Error Running [window.setInterval]: " + error);
  }
  try {
    document.addEventListener("click", checkClass, 1000);
  } catch (error) {
    console.error("Error Running [document.addEventListener]: " + error);
  }

  // ------------- Passive Listeners ------------- //

  // Fullscreen Toggle
  function toggleTheaterStingyWorkaround() {
    const event = new KeyboardEvent("keydown", {
      key: "t",
      keyCode: 84,
    });
    document.dispatchEvent(event);
    location.reload();
  }
})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址