// ==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);