Skip to content

Instantly share code, notes, and snippets.

@mhartington
Created October 27, 2022 13:44
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mhartington/12e28f80700bc2cd5361e6a16b930c05 to your computer and use it in GitHub Desktop.
Save mhartington/12e28f80700bc2cd5361e6a16b930c05 to your computer and use it in GitHub Desktop.
// ==UserScript==
// @name YouTube Patch Collection
// @match https://www.youtube.com/*
// @icon https://www.youtube.com/favicon.ico
// @run-at document-start
// @grant none
// ==/UserScript==
// Attributes to remove from <html>
const ATTRS = [
"system-icons",
"typography",
"typography-spacing"
];
// Regular config keys.
const CONFIGS = {
BUTTON_REWORK: false
}
// Experiment flags.
const EXPFLAGS = {
kevlar_system_icons: false,
kevlar_watch_metadata_refresh: false,
web_modern_buttons: false,
web_rounded_containers: false,
web_rounded_thumbnails: false,
}
// Player flags
// !!! USE STRINGS FOR VALUES !!!
// For example: "true" instead of true
const PLYRFLAGS = {
web_player_move_autonav_toggle: "false"
}
class YTP {
static observer = new MutationObserver(this.onNewScript);
static _config = {};
static isObject(item) {
return (item && typeof item === "object" && !Array.isArray(item));
}
static mergeDeep(target, ...sources) {
if (!sources.length) return target;
const source = sources.shift();
if (this.isObject(target) && this.isObject(source)) {
for (const key in source) {
if (this.isObject(source[key])) {
if (!target[key]) Object.assign(target, { [key]: {} });
this.mergeDeep(target[key], source[key]);
} else {
Object.assign(target, { [key]: source[key] });
}
}
}
return this.mergeDeep(target, ...sources);
}
static onNewScript(mutations) {
for (var mut of mutations) {
for (var node of mut.addedNodes) {
YTP.bruteforce();
}
}
}
static start() {
this.observer.observe(document, {childList: true, subtree: true});
}
static stop() {
this.observer.disconnect();
}
static bruteforce() {
if (!window.yt) return;
if (!window.yt.config_) return;
this.mergeDeep(window.yt.config_, this._config);
}
static setCfg(name, value) {
this._config[name] = value;
}
static setCfgMulti(configs) {
this.mergeDeep(this._config, configs);
}
static setExp(name, value) {
if (!("EXPERIMENT_FLAGS" in this._config)) this._config.EXPERIMENT_FLAGS = {};
this._config.EXPERIMENT_FLAGS[name] = value;
}
static setExpMulti(exps) {
if (!("EXPERIMENT_FLAGS" in this._config)) this._config.EXPERIMENT_FLAGS = {};
this.mergeDeep(this._config.EXPERIMENT_FLAGS, exps);
}
static decodePlyrFlags(flags) {
var obj = {},
dflags = flags.split("&");
for (var i = 0; i < dflags.length; i++) {
var dflag = dflags[i].split("=");
obj[dflag[0]] = dflag[1];
}
return obj;
}
static encodePlyrFlags(flags) {
var keys = Object.keys(flags),
response = "";
for (var i = 0; i < keys.length; i++) {
if (i > 0) {
response += "&";
}
response += keys[i] + "=" + flags[keys[i]];
}
return response;
}
static setPlyrFlags(flags) {
if (!window.yt) return;
if (!window.yt.config_) return;
if (!window.yt.config_.WEB_PLAYER_CONTEXT_CONFIGS) return;
var conCfgs = window.yt.config_.WEB_PLAYER_CONTEXT_CONFIGS;
if (!("WEB_PLAYER_CONTEXT_CONFIGS" in this._config)) this._config.WEB_PLAYER_CONTEXT_CONFIGS = {};
for (var cfg in conCfgs) {
var dflags = this.decodePlyrFlags(conCfgs[cfg].serializedExperimentFlags);
this.mergeDeep(dflags, flags);
this._config.WEB_PLAYER_CONTEXT_CONFIGS[cfg] = {
serializedExperimentFlags: this.encodePlyrFlags(dflags)
}
}
}
}
window.addEventListener("yt-page-data-updated", function tmp() {
YTP.stop();
for (i = 0; i < ATTRS.length; i++) {
document.getElementsByTagName("html")[0].removeAttribute(ATTRS[i]);
}
window.removeEventListener("yt-page-date-updated", tmp);
});
YTP.start();
YTP.setCfgMulti(CONFIGS);
YTP.setExpMulti(EXPFLAGS);
YTP.setPlyrFlags(PLYRFLAGS);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment