Configure YouTube features

This script (based on YouTube Patch Collection) allows you to configure YouTube features & additional ones that are on a rollout, such as the new modern tabs, rounded player, you tab & more using yt.config flags!

  1. // ==UserScript==
  2. // @name Configure YouTube features
  3. // @version 2023.10.26
  4. // @description This script (based on YouTube Patch Collection) allows you to configure YouTube features & additional ones that are on a rollout, such as the new modern tabs, rounded player, you tab & more using yt.config flags!
  5. // @author Alpharetzy
  6. // @license MIT
  7. // @match www.youtube.com/*
  8. // @icon https://www.youtube.com/favicon.ico
  9. // @run-at document-start
  10. // @grant none
  11. // @namespace https://greasyfork.org/users/1094802
  12. // ==/UserScript==
  13.  
  14. // Attributes to remove from <html>
  15. const ATTRS = [
  16. // "system-icons",
  17. // "typography",
  18. // "typography-spacing"
  19. ];
  20.  
  21. // Regular config keys.
  22. const CONFIGS = {
  23. BUTTON_REWORK: true
  24. };
  25.  
  26. // Experiment flags.
  27. const EXPFLAGS = {
  28. enable_channel_page_header_profile_section: true,
  29. enable_header_channel_handler_ui: true,
  30. kevlar_unavailable_video_error_ui_client: true,
  31. kevlar_refresh_on_theme_change: true,
  32. kevlar_modern_sd_v2: true,
  33. kevlar_watch_cinematics: true,
  34. kevlar_watch_comments_panel_button: true,
  35. //kevlar_watch_grid: true,
  36. //kevlar_watch_grid_hide_chips: true,
  37. kevlar_watch_metadata_refresh: true,
  38. kevlar_watch_metadata_refresh_no_old_secondary_data: true,
  39. kevlar_watch_modern_metapanel: true,
  40. kevlar_watch_modern_panels: true,
  41. kevlar_watch_panel_height_matches_player: true,
  42. smartimation_background: true,
  43. web_amsterdam_playlists: true,
  44. web_animated_actions: true,
  45. kevlar_system_icons: true,
  46. web_animated_like: true,
  47. web_button_rework: true,
  48. web_button_rework_with_live: true,
  49. web_darker_dark_theme: true,
  50. web_enable_youtab: true,
  51. web_guide_ui_refresh: true,
  52. web_modern_ads: true,
  53. web_modern_buttons: true,
  54. web_modern_chips: true,
  55. web_modern_collections_v2: true,
  56. web_modern_dialogs: true,
  57. web_modern_playlists: true,
  58. web_modern_subscribe: true,
  59. web_modern_tabs: true,
  60. web_modern_typography: true,
  61. web_rounded_containers: true,
  62. web_rounded_thumbnails: true,
  63. web_searchbar_style: "default",
  64. web_segmented_like_dislike_button: true,
  65. web_sheets_ui_refresh: true,
  66. web_snackbar_ui_refresh: true,
  67. web_watch_rounded_player_large: true
  68. }
  69.  
  70. // Player flags
  71. // !!! USE STRINGS FOR VALUES !!!
  72. // For example: "true" instead of true
  73. const PLYRFLAGS = {
  74. web_player_move_autonav_toggle: "true",
  75. web_settings_menu_icons: "true",
  76. web_rounded_containers: "true",
  77. web_rounded_thumbnails: "true"
  78. };
  79.  
  80. class YTP {
  81. static observer = new MutationObserver(this.onNewScript);
  82.  
  83. static _config = {};
  84.  
  85. static isObject(item) {
  86. return (item && typeof item === "object" && !Array.isArray(item));
  87. }
  88. static mergeDeep(target, ...sources) {
  89. if (!sources.length) return target;
  90. const source = sources.shift();
  91. if (this.isObject(target) && this.isObject(source)) {
  92. for (const key in source) {
  93. if (this.isObject(source[key])) {
  94. if (!target[key]) Object.assign(target, { [key]: {} });
  95. this.mergeDeep(target[key], source[key]);
  96. } else {
  97. Object.assign(target, { [key]: source[key] });
  98. }
  99. }
  100. }
  101. return this.mergeDeep(target, ...sources);
  102. }
  103.  
  104. static onNewScript(mutations) {
  105. for (var mut of mutations) {
  106. for (var node of mut.addedNodes) {
  107. YTP.bruteforce();
  108. }
  109. }
  110. }
  111.  
  112. static start() {
  113. this.observer.observe(document, {childList: true, subtree: true});
  114. }
  115. static stop() {
  116. this.observer.disconnect();
  117. }
  118.  
  119. static bruteforce() {
  120. if (!window.yt) return;
  121. if (!window.yt.config_) return;
  122.  
  123. this.mergeDeep(window.yt.config_, this._config);
  124. }
  125.  
  126. static setCfg(name, value) {
  127. this._config[name] = value;
  128. }
  129.  
  130. static setCfgMulti(configs) {
  131. this.mergeDeep(this._config, configs);
  132. }
  133.  
  134. static setExp(name, value) {
  135. if (!("EXPERIMENT_FLAGS" in this._config)) this._config.EXPERIMENT_FLAGS = {};
  136.  
  137. this._config.EXPERIMENT_FLAGS[name] = value;
  138. }
  139.  
  140. static setExpMulti(exps) {
  141. if (!("EXPERIMENT_FLAGS" in this._config)) this._config.EXPERIMENT_FLAGS = {};
  142.  
  143. this.mergeDeep(this._config.EXPERIMENT_FLAGS, exps);
  144. }
  145.  
  146. static decodePlyrFlags(flags) {
  147. var obj = {},
  148. dflags = flags.split("&");
  149.  
  150. for (var i = 0; i < dflags.length; i++) {
  151. var dflag = dflags[i].split("=");
  152. obj[dflag[0]] = dflag[1];
  153. }
  154. return obj;
  155. }
  156.  
  157. static encodePlyrFlags(flags) {
  158. var keys = Object.keys(flags),
  159. response = "";
  160.  
  161. for (var i = 0; i < keys.length; i++) {
  162. if (i > 0) {
  163. response += "&";
  164. }
  165. response += keys[i] + "=" + flags[keys[i]];
  166. }
  167.  
  168. return response;
  169. }
  170.  
  171. static setPlyrFlags(flags) {
  172. if (!window.yt) return;
  173. if (!window.yt.config_) return;
  174. if (!window.yt.config_.WEB_PLAYER_CONTEXT_CONFIGS) return;
  175. var conCfgs = window.yt.config_.WEB_PLAYER_CONTEXT_CONFIGS;
  176. if (!("WEB_PLAYER_CONTEXT_CONFIGS" in this._config)) this._config.WEB_PLAYER_CONTEXT_CONFIGS = {};
  177.  
  178. for (var cfg in conCfgs) {
  179. var dflags = this.decodePlyrFlags(conCfgs[cfg].serializedExperimentFlags);
  180. this.mergeDeep(dflags, flags);
  181. this._config.WEB_PLAYER_CONTEXT_CONFIGS[cfg] = {
  182. serializedExperimentFlags: this.encodePlyrFlags(dflags)
  183. }
  184. }
  185. }
  186. }
  187.  
  188. window.addEventListener("yt-page-data-updated", function tmp() {
  189. YTP.stop();
  190. for (i = 0; i < ATTRS.length; i++) {
  191. document.getElementsByTagName("html")[0].removeAttribute(ATTRS[i]);
  192. }
  193. window.removeEventListener("yt-page-date-updated", tmp);
  194. });
  195.  
  196. YTP.start();
  197.  
  198. YTP.setCfgMulti(CONFIGS);
  199. YTP.setExpMulti(EXPFLAGS);
  200. YTP.setPlyrFlags(PLYRFLAGS);