YouTube Web Tweaks

This script optimizes YouTube's performance by modified configs, anti-shorts and much more!

目前为 2023-09-10 提交的版本。查看 最新版本

// ==UserScript==
// @name         YouTube Web Tweaks
// @version      3.3.0
// @description  This script optimizes YouTube's performance by modified configs, anti-shorts and much more!
// @author       Magma_Craft
// @license MIT
// @match        *://www.youtube.com/*
// @namespace    https://gf.qytechs.cn/en/users/933798
// @icon         https://www.youtube.com/favicon.ico
// @unwrap
// @run-at       document-start
// @unwrap
// @inject-into  page
// @allFrames    true
// @grant        none
// @require      https://cdnjs.cloudflare.com/ajax/libs/arrive/2.4.1/arrive.min.js
// ==/UserScript==

// Modifiying yt.config flags to optimize loading times and disables animations
(function() {
    window['yt'] = window['yt'] || {};
    yt['config_'] = yt.config_ || {};
    yt.config_['EXPERIMENT_FLAGS'] = yt.config_.EXPERIMENT_FLAGS || {};

    var iv = setInterval(function() {
        yt.config_.IS_TABLET = true;
        yt.config_.DISABLE_YT_IMG_DELAY_LOADING = true;
        yt.config_.EXPERIMENT_FLAGS.polymer_verifiy_app_state = false;
        yt.config_.EXPERIMENT_FLAGS.desktop_delay_player_resizing = false;
        yt.config_.EXPERIMENT_FLAGS.web_animated_like = false;
        yt.config_.EXPERIMENT_FLAGS.web_animated_like_lazy_load = false;
        yt.config_.EXPERIMENT_FLAGS.render_unicode_emojis_as_small_images = true;
        yt.config_.EXPERIMENT_FLAGS.kevlar_refresh_on_theme_change = false;
        yt.config_.EXPERIMENT_FLAGS.kevlar_watch_cinematics = false;
    }, 1);

    var to = setTimeout(function() {
        clearInterval(iv);
    }, 1000)
})();

// Improving the system performance by using their native engine
((__CONTEXT01__) => {
 
 
  const win = this instanceof Window ? this : window;
 
  // Create a unique key for the script and check if it is already running
  const hkey_script = 'ikkaorpwuzvt';
  if (win[hkey_script]) throw new Error('Duplicated Userscript Calling'); // avoid duplicated scripting
  win[hkey_script] = true;
 
  /** @type {globalThis.PromiseConstructor} */
  const Promise = ((async () => { })()).constructor;
 
  const cleanContext = async (win) => {
 
 
    const waitFn = requestAnimationFrame; // shall have been binded to window
    try {
      let mx = 16; // MAX TRIAL
      const frameId = 'vanillajs-iframe-v1'
      let frame = document.getElementById(frameId);
      let removeIframeFn = null;
      if (!frame) {
        frame = document.createElement('iframe');
        frame.id = 'vanillajs-iframe-v1';
        frame.sandbox = 'allow-same-origin'; // script cannot be run inside iframe but API can be obtained from iframe
        let n = document.createElement('noscript'); // wrap into NOSCRPIT to avoid reflow (layouting)
        n.appendChild(frame);
        while (!document.documentElement && mx-- > 0) await new Promise(waitFn); // requestAnimationFrame here could get modified by YouTube engine
        const root = document.documentElement;
        root.appendChild(n); // throw error if root is null due to exceeding MAX TRIAL
        removeIframeFn = (setTimeout) => {
          const removeIframeOnDocumentReady = (e) => {
            e && win.removeEventListener("DOMContentLoaded", removeIframeOnDocumentReady, false);
            win = null;
            setTimeout(() => {
              n.remove();
              n = null;
            }, 200);
          }
          if (document.readyState !== 'loading') {
            removeIframeOnDocumentReady();
          } else {
            win.addEventListener("DOMContentLoaded", removeIframeOnDocumentReady, false);
          }
        }
      }
      while (!frame.contentWindow && mx-- > 0) await new Promise(waitFn);
      const fc = frame.contentWindow;
      if (!fc) throw "window is not found."; // throw error if root is null due to exceeding MAX TRIAL
      const { requestAnimationFrame, cancelAnimationFrame, getComputedStyle, setInterval, clearInterval, setTimeout, clearTimeout } = fc;
      const res = { requestAnimationFrame, cancelAnimationFrame, getComputedStyle, setInterval, clearInterval, setTimeout, clearTimeout };
      for (let k in res) res[k] = res[k].bind(win); // necessary
      res.animate = fc.Element.prototype.animate;
      if (removeIframeFn) Promise.resolve(res.setTimeout).then(removeIframeFn);
      return res;
    } catch (e) {
      console.warn(e);
      return null;
    }
  };
 
  cleanContext(win).then(__CONTEXT02__ => {
 
    const { requestAnimationFrame, cancelAnimationFrame, getComputedStyle, setInterval, clearInterval, setTimeout, clearTimeout } = __CONTEXT02__;
 
    const { animate } = __CONTEXT02__;
 
    const { frames, defineProperty, window, CDATASection, ProcessingInstruction, FocusEvent } = __CONTEXT01__;
 
    const ENABLE_NATIVE_CONSTRUCTOR_CHECK = false;
    let cids = {};
    function cleanCId(k) {
      Promise.resolve().then(() => clearInterval(cids[k]));
    }
 
 
    Object.defineProperty = function (o, p, opts) {
 
      if (arguments.length !== 3) return defineProperty.apply(this, arguments);
 
      if (o instanceof Window) {
        if (p === 'getComputedStyle') return;
        if (p === 'Promise' && (p in o)) return; // WaterFox Classic
        if (p === 'customElements' || p === 'Polymer') {
          if (p in o) return; // duplicate declaration?
        }
        const value = opts.value;
        if (value) {
          opts.writable = true;
          opts.configurable = true;
          opts.enumerable = true;
        }
        if (p === 'ytInitialPlayerResponse' || p === 'playerResponse') {
          // Firefox Chatroom? TBC
        } else {
          console.log(923, 'window[p]=', p, opts);
        }
        return defineProperty.call(this, o, p, opts);
      }
 
 
      const nativeConstructorCheck = ENABLE_NATIVE_CONSTRUCTOR_CHECK ? (o.constructor + "").indexOf('native code') > 0 : true;
 
      if (p.startsWith('__shady_')) {
 
        const { get, value } = opts;
        if (!get) {
          o[p] = value;
          return;
        }
 
        if (p === '__shady_native_eventPhase') {
          // Event -> __shady_native_eventPhase
          return defineProperty.call(this, o, p, opts);
        }
 
        let constructor = o instanceof Node ? Node : o instanceof DocumentFragment ? DocumentFragment : o instanceof Document ? Document : null;
 
        if (!constructor) {
          let constructorName = (o.constructor || 0).name;
          if (constructorName === 'Node') {
            constructor = Node;
          }
        }
 
        if (constructor && opts && (typeof opts.get === 'function')) {
 
          if (!(p in o.constructor.prototype) && !(p in o)) {
            defineProperty.call(this, o.constructor.prototype, p, opts);
          }
          return;
 
        }
        console.log(926, o, p, opts, !!constructor, !!opts, !!(typeof opts.get === 'function'))
        // return;
      }
 
      if ((p in o) && nativeConstructorCheck) {
        if (o instanceof Text) return;
        if (o instanceof Comment) return;
        if (CDATASection && o instanceof CDATASection) return;
        if (ProcessingInstruction && o instanceof ProcessingInstruction) return;
        if (o instanceof Event) return;
        if (FocusEvent && o instanceof FocusEvent) return;
      }
 
      return defineProperty.call(this, o, p, opts);
    }
 
    const asserter = (f) => Promise.resolve().then(() => console.assert(f(), f + ""));
 
    const setVJS = () => {
      if (window.Promise !== Promise) window.Promise = Promise;
      if (window.getComputedStyle !== getComputedStyle) window.getComputedStyle = getComputedStyle;
      if (Element.prototype.animate !== animate) Element.prototype.animate = animate;
      if (window.requestAnimationFrame !== requestAnimationFrame) window.requestAnimationFrame = requestAnimationFrame
      if (window.cancelAnimationFrame !== cancelAnimationFrame) window.cancelAnimationFrame = cancelAnimationFrame
    };
 
    const finishFn = () => {
      cids.finish = 0;
      setVJS();
      try {
        document.getElementById('zihrS').remove();
      } catch (e) { }
      cleanCId('timeVJS');
      if (document.isConnected === false) return;
      setTimeout(() => {
        if (document.isConnected === false) return;
        asserter(() => window.Promise === Promise);
        asserter(() => window.getComputedStyle === getComputedStyle);
        asserter(() => Element.prototype.animate === animate);
        asserter(() => window.requestAnimationFrame === requestAnimationFrame);
        asserter(() => window.cancelAnimationFrame === cancelAnimationFrame);
 
      }, 800);
 
    };
 
    function fastenFinishFn() {
      if (cids.finish > 0) {
        clearInterval(cids.finish);
        cids.finish = setTimeout(finishFn, 40);
      }
    }
 
    function preFinishFn() {
      let mo = new MutationObserver(function () {
        Promise.resolve().then(fastenFinishFn)
        mo.disconnect();
        mo.takeRecords();
        mo = null;
      });
      mo.observe(document, { subtree: true, childList: true });
      return setTimeout(finishFn, 400);
    }
 
    cids.timeVJS = setInterval(() => {
      if (!cids.finish && ('Polymer' in window)) cids.finish = preFinishFn();
      setVJS();
    }, 1);
 
 
    let isInnerFrame = false;
    try {
      isInnerFrame = window !== top && window.document.domain === top.document.domain;
    } catch (e) { }
 
    if (!isInnerFrame) {
 
      console.groupCollapsed(
        "%cYouTube Native - Vanilla Engine (Experimental)",
        "background-color: #e0005a ; color: #ffffff ; font-weight: bold ; padding: 4px ;"
      );
 
      console.log("Script is loaded.");
      console.log("This is an experimental script.");
      console.log("If you found any issue in using YouTube, please disable this script to check whether the issue is due to this script or not.");
 
      console.groupEnd();
 
    }
 
 
  });
 
})({
  frames,
  defineProperty: Object.defineProperty,
  window,
  CDATASection, ProcessingInstruction, FocusEvent
});

// Auto redirect shorts to watch page
var oldHref = document.location.href;
if (window.location.href.indexOf('youtube.com/shorts') > -1) {
    window.location.replace(window.location.toString().replace('/shorts/', '/watch?v='));
}
window.onload = function() {
    var bodyList = document.querySelector("body")
    var observer = new MutationObserver(function(mutations) {
        mutations.forEach(function(mutation) {
            if (oldHref != document.location.href) {
                oldHref = document.location.href;
                console.log('location changed!');
                if (window.location.href.indexOf('youtube.com/shorts') > -1) {
                    window.location.replace(window.location.toString().replace('/shorts/', '/watch?v='));
                }
            }
        });
    });
    var config = {
        childList: true,
        subtree: true
    };
    observer.observe(bodyList, config);
};

/**
 * Shorts URL redirect.
 *
 * This is called on initial visit only. Successive navigations
 * are managed by modifying the YouTube Desktop application.
 */
(function(){

/** @type {string} */
var path = window.location.pathname;

if (0 == path.search("/shorts"))
{
    // Extract the video ID from the shorts link and redirect.

    /** @type {string} */
    var id = path.replace(/\/|shorts|\?.*/g, "");

    window.location.replace("https://www.youtube.com/watch?v=" + id);
}

})();

/**
 * YouTube Desktop Shorts remover.
 *
 * If the initial URL was not a shorts link, traditional redirection
 * will not work. This instead modifies video elements to replace them with
 * regular links.
 */
(function(){

/**
 * @param {string} selector (CSS-style) of the element
 * @return {Promise<Element>}
 */
async function querySelectorAsync(selector)
{
    while (null == document.querySelector(selector))
    {
        // Pause for a frame and let other code go on.
        await new Promise(r => requestAnimationFrame(r));
    }

    return document.querySelector(selector);
}

/**
 * Small toolset for interacting with the Polymer
 * YouTube Desktop application.
 *
 * @author Taniko Yamamoto <[email protected]>
 * @version 1.0
 */
class YtdTools
{
    /** @type {string} Page data updated event */
    static EVT_DATA_UPDATE = "yt-page-data-updated";

    /** @type {Element} Main YT Polymer manager */
    static YtdApp;

    /** @type {bool} */
    static hasInitialLoaded = false;

    /** @return {Promise<bool>} */
    static async isPolymer()
    {
        /** @return {Promise<void>} */
        function waitForBody() // nice hack lazy ass
        {
            return new Promise(r => {
                document.addEventListener("DOMContentLoaded", function a(){
                    document.removeEventListener("DOMContentLoaded", a);
                    r();
                });
            });
        }

        await waitForBody();

        if ("undefined" != typeof document.querySelector("ytd-app"))
        {
            this.YtdApp = document.querySelector("ytd-app");
            return true;
        }
        return false;
    }

    /** @async @return {Promise<void|string>} */
    static waitForInitialLoad()
    {
        var updateEvent = this.EVT_DATA_UPDATE;
        return new Promise((resolve, reject) => {
            if (!this.isPolymer())
            {
                reject("Not Polymer :(");
            }

            function _listenerCb()
            {
                document.removeEventListener(updateEvent, _listenerCb);
                resolve();
            }

            document.addEventListener(updateEvent, _listenerCb);
        });
    }

    /** @return {string} */
    static getPageType()
    {
        return this.YtdApp.data.page;
    }
}

class ShortsTools
{
    /** @type {MutationObserver} */
    static mo = new MutationObserver(muts => {
        muts.forEach(mut => {
            Array.from(mut.addedNodes).forEach(node => {
                if (node instanceof HTMLElement) {
                    this.onMutation(node);
                }
            });
        });
    });

    /** @return {void} */
    static watchForShorts()
    {
        /*
        this.mo.observe(YtdTools.YtdApp, {
            childList: true,
            subtree: true
        });
        */
        var me = this;
        YtdTools.YtdApp.arrive("ytd-video-renderer, ytd-grid-video-renderer", function() {
            me.onMutation(this);

            // This is literally the worst hack I ever wrote, but it works ig...
            (new MutationObserver(function(){
                if (me.isShortsRenderer(this))
                {
                    me.onMutation(this);
                }
            }.bind(this))).observe(this, {"subtree": true, "childList": true, "characterData": "true"});
        });
    }

    /** @return {void} */
    static stopWatchingForShorts()
    {
        this.mo.disconnect();
    }

    /**
     * @param {HTMLElement} node
     * @return {void}
     */
    static onMutation(node)
    {
        if (node.tagName.search("VIDEO-RENDERER") > -1 && this.isShortsRenderer(node))
        {
            this.transformShortsRenderer(node);
        }
    }

    /** @return {bool} */
    static isShortsRenderer(videoRenderer)
    {
        return "WEB_PAGE_TYPE_SHORTS" == videoRenderer?.data?.navigationEndpoint?.commandMetadata?.webCommandMetadata?.webPageType;
    }

    /** @return {string} */
    static extractLengthFromA11y(videoData)
    {
        // A11y = {title} by {creator} {date} {*length*} {viewCount} - play Short
        // tho hopefully this works in more than just English
        var a11yTitle = videoData.title.accessibility.accessibilityData.label;

        var publishedTimeText = videoData.publishedTimeText.simpleText;
        var viewCountText = videoData.viewCountText.simpleText;

        var isolatedLengthStr = a11yTitle.split(publishedTimeText)[1].split(viewCountText)[0]
            .replace(/\s/g, "");

        var numbers = isolatedLengthStr.split(/\D/g);

        var string = "";

        // Remove all empties before iterating it
        for (var i = 0; i < numbers.length; i++)
        {
            if ("" === numbers[i])
            {
                numbers.splice(i, 1);
                i--;
            }
        }

        for (var i = 0; i < numbers.length; i++)
        {
            // Lazy 0 handling idc im tired
            if (1 == numbers.length)
            {
                string += "0:";
                if (1 == numbers[i].length)
                {
                    string += "0" + numbers[i];
                }
                else
                {
                    string += numbers[i];
                }

                break;
            }

            if (0 != i) string += ":";
            if (0 != i && 1 == numbers[i].length) string += "0";
            string += numbers[i];
        }

        return string;
    }

    /**
     * @param {HTMLElement} videoRenderer
     * @return {void}
     */
    static transformShortsRenderer(videoRenderer)
    {

        /** @type {string} */
        var originalOuterHTML = videoRenderer.outerHTML;

        /** @type {string} */
        var lengthText = videoRenderer.data?.lengthText?.simpleText ?? this.extractLengthFromA11y(videoRenderer.data);

        /** @type {string} */
        var lengthA11y = videoRenderer.data?.lengthText?.accessibility?.accessibilityData?.label ?? "";

        /** @type {string} */
        var originalHref = videoRenderer.data.navigationEndpoint.commandMetadata.webCommandMetadata.url;
        var href = "/watch?v=" + originalHref.replace(/\/|shorts|\?.*/g, "");

        var reelWatchEndpoint = videoRenderer.data.navigationEndpoint.reelWatchEndpoint;

        var i;
        videoRenderer.data.thumbnailOverlays.forEach((a, index) =>{
            if ("thumbnailOverlayTimeStatusRenderer" in a)
            {
                i = index;
            }
        });

        // Set the thumbnail overlay style
        videoRenderer.data.thumbnailOverlays[i].thumbnailOverlayTimeStatusRenderer.style = "DEFAULT";

        delete videoRenderer.data.thumbnailOverlays[i].thumbnailOverlayTimeStatusRenderer.icon;

        // Set the thumbnail overlay text
        videoRenderer.data.thumbnailOverlays[i].thumbnailOverlayTimeStatusRenderer.text.simpleText = lengthText;

        // Set the thumbnail overlay accessibility label
        videoRenderer.data.thumbnailOverlays[i].thumbnailOverlayTimeStatusRenderer.text.accessibility.accessibilityData.label = lengthA11y;

        // Set the navigation endpoint metadata (used for middle click)
        videoRenderer.data.navigationEndpoint.commandMetadata.webCommandMetadata.webPageType = "WEB_PAGE_TYPE_WATCH";
        videoRenderer.data.navigationEndpoint.commandMetadata.webCommandMetadata.url = href;

        videoRenderer.data.navigationEndpoint.watchEndpoint = {
            "videoId": reelWatchEndpoint.videoId,
            "playerParams": reelWatchEndpoint.playerParams,
            "params": reelWatchEndpoint.params
        };
        delete videoRenderer.data.navigationEndpoint.reelWatchEndpoint;

        //var _ = videoRenderer.data; videoRenderer.data = {}; videoRenderer.data = _;

        // Sometimes the old school data cycle trick fails,
        // however this always works.
        var _ = videoRenderer.cloneNode();
        _.data = videoRenderer.data;
        for (var i in videoRenderer.properties)
        {
            _[i] = videoRenderer[i];
        }
        videoRenderer.insertAdjacentElement("afterend", _);
        videoRenderer.remove();
    }
}

/**
 * Sometimes elements are reused on page updates, so fix that
 *
 * @return {void}
 */
function onDataUpdate()
{
    var videos = document.querySelectorAll("ytd-video-renderer, ytd-grid-video-renderer");

    for (var i = 0, l = videos.length; i < l; i++) if (ShortsTools.isShortsRenderer(videos[i]))
    {
        ShortsTools.transformShortsRenderer(videos[i]);
    }
}

/**
 * I hope she makes lotsa spaghetti :D
 * @async @return {Promise<void>}
 */
async function main()
{
    // If not Polymer, nothing happens
    if (await YtdTools.isPolymer())
    {
        ShortsTools.watchForShorts();

        document.addEventListener("yt-page-data-updated", onDataUpdate);
    }
}

main();

})();

// CSS tweaks to be applied (including removal of "Video paused. Continue watching?" popup)
(function() {
ApplyCSS();

function ApplyCSS() {
var styles = document.createElement("style");
styles.innerHTML=`
/* Hide Shorts button in sidebar */
#endpoint.yt-simple-endpoint.ytd-guide-entry-renderer.style-scope[title="Shorts"] {
  display: none !important;
}

a.yt-simple-endpoint.style-scope.ytd-mini-guide-entry-renderer[title="Shorts"] {
  display: none !important;
}

/* Hide Shorts on related videos (including remixes) */
ytd-reel-shelf-renderer.style-scope.ytd-item-section-renderer {
  display: none !important;
}

ytd-reel-shelf-renderer.ytd-structured-description-content-renderer {
  display: none !important;
}

/* Remove ambient light on watch page + YouTube TV popup */
cinematics.ytd-watch-flexy {
  display: none !important;
}
 
ytd-popup-container > tp-yt-paper-dialog > ytd-mealbar-promo-renderer, ytd-popup-container > tp-yt-paper-dialog > yt-mealbar-promo-renderer:has-text(/Claim Offer|Join now|Not Now|YouTube TV|live TV|Live TV|movies|sports|Try it free|unlimited DVR|watch NFL/) {
display: none !important;
}`
document.head.appendChild(styles);
}
})();

Object.defineProperties(document, { 'hidden': {value: false}, 'webkitHidden': {value: false}, 'visibilityState': {value: 'visible'}, 'webkitVisibilityState': {value: 'visible'} });
 
setInterval(function(){
    document.dispatchEvent( new KeyboardEvent( 'keyup', { bubbles: true, cancelable: true, keyCode: 143, which: 143 } ) );
}, 60000);
 

QingJ © 2025

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