YouTube Revert Layout

This script aims to revert some of Youtube's recent UI changes, switching everything back around and formatting the video cards back into their original dimensions. It works pretty well on my computer (at least it's better than all the other ones I've tried).

  1. // ==UserScript==
  2. // @name YouTube Revert Layout
  3. // @version 4.0
  4. // @author Garbhj
  5. // @namespace https://github.com/garbhj
  6. // @description This script aims to revert some of Youtube's recent UI changes, switching everything back around and formatting the video cards back into their original dimensions. It works pretty well on my computer (at least it's better than all the other ones I've tried).
  7. // @match *.youtube.com/watch?*
  8. // @run-at document-end
  9. // @icon https://www.google.com/s2/favicons?sz=64&domain=youtube.com
  10. // @license MIT
  11. // ==/UserScript==
  12.  
  13. (function () {
  14. 'use strict';
  15.  
  16. // Modify experiment flags to revert UI changes ***
  17. ytcfg.set('EXPERIMENT_FLAGS', {
  18. ...ytcfg.get('EXPERIMENT_FLAGS'),
  19. "kevlar_watch_comments_panel_button": false,
  20. "kevlar_watch_comments_ep_disable_theater": true,
  21. "kevlar_watch_grid": false,
  22. "kevlar_watch_grid_hide_chips": false,
  23. "kevlar_watch_grid_reduced_top_margin_rich_grid": false,
  24. "optimal_reading_width_comments_ep": false,
  25. "small_avatars_for_comments": false,
  26. "small_avatars_for_comments_ep": false,
  27. "swatcheroo_direct_use_rich_grid": false,
  28. "web_watch_compact_comments": false,
  29. "web_watch_compact_comments_header": false,
  30. "swatcheroo_rich_grid_delay": 0,
  31. "wn_grid_max_item_width": 0,
  32. "wn_grid_min_item_width": 0,
  33. });
  34.  
  35. // Modify page elements (main function)
  36. let timeoutId = null; // With timeout function
  37. function modifyPageElements() {
  38. if (timeoutId) return; // if a timeout is already pending, exit
  39.  
  40. if (window.location.href.includes("youtube.com/watch")) {
  41. const startTime = performance.now();
  42.  
  43. modifySidebarVideos();
  44.  
  45. const midTime = performance.now();
  46.  
  47. adjustVideoPlayerElements();
  48.  
  49. const endTime = performance.now();
  50. const executionTime1 = midTime - startTime;
  51. const executionTime2 = endTime - midTime;
  52. console.log(`modifySidebarVideos() took ${executionTime1.toFixed(2)}ms to execute`);
  53. console.log(`adjustVideoPlayerElements() took ${executionTime2.toFixed(2)}ms to execute`);
  54.  
  55. }
  56.  
  57. timeoutId = setTimeout(() => {
  58. timeoutId = null; // reset the timeout id
  59. }, 50); // 50 ms rate limit
  60. }
  61.  
  62. // Function to properly format sidebar video elements
  63. function modifySidebarVideos() {
  64.  
  65. // ItemsContainer used for performance improvement, no substantial improvement at the end of the day
  66. // Try switcing "document" with "itemsContainer" for all the querySelections below (except for the last one) to try it out.
  67. //const itemsContainer = document.querySelector('#items.style-scope.ytd-watch-next-secondary-results-renderer');
  68.  
  69. // Format thumbnails to specified width
  70. const thumbnailDivs = document.querySelectorAll('ytd-rich-grid-media #thumbnail');
  71. thumbnailDivs.forEach(function(div) {
  72. div.style.cssText = `width: 168px; position: absolute;`;
  73.  
  74. // Additional attempts to modify corner radius with higher specificity (did not work)
  75. // div.style.borderRadius = "2px !important";
  76. // div.style.cssText += "#thumbnail.style-scope.ytd-rich-grid-media { border-radius: 2px !important; }";
  77.  
  78. });
  79.  
  80. // Modify details div spacing: Optimal min spacing was 99px for me - adjust if neccesary
  81. // I just put min-height because one line titles messed up spacing for some reason
  82. // Combined with metadata-line for efficiency
  83. const detailsDivs = document.querySelectorAll('#details');
  84. detailsDivs.forEach(div => {
  85. div.style.cssText = "padding-left:176px; margin-top:-12px; min-height: 101px;";
  86. });
  87.  
  88. // Reformat text size and spacing (view count and relative upload time)
  89. const metadataLineDivs = document.querySelectorAll("div#metadata-line");
  90. metadataLineDivs.forEach(div => div.style.cssText = "font-size: 1.2rem; line-height: 1.2rem;");
  91.  
  92. // Remove margins from contentsDivs (containing both thumbnail and details) to improve spacing
  93. const contentsDivs = document.querySelectorAll('div#contents.ytd-rich-grid-row');
  94. contentsDivs.forEach(div => div.style.cssText = "margin: 0px;");
  95.  
  96. // Sepecify rich-item-renderer div spacing (includes playlist and ad items as well) because it's quite big by default
  97. const richItemDivs = document.querySelectorAll("div#contents.ytd-rich-grid-row > ytd-rich-item-renderer");
  98. richItemDivs.forEach(div => {
  99. div.style.cssText = "margin-left: 0px; margin-right 0px; margin-bottom: 13px;";
  100.  
  101. // Remove channel avatar profile pics
  102. const avatarLink = div.querySelector("a#avatar-link");
  103. if (avatarLink) avatarLink.remove();
  104. });
  105.  
  106. // Modify video title styles (Selecting the yt-formatted-string element within the video-title-link)
  107. const videoTitleElements = document.querySelectorAll("a#video-title-link yt-formatted-string#video-title");
  108. videoTitleElements.forEach(element => {
  109. element.style.fontSize = "1.4rem";
  110. element.style.lineHeight = "2rem";
  111. });
  112.  
  113. // Modify channel name styles (Targeting specific div in channel-name)
  114. const channelNameDivs = document.querySelectorAll("#items.style-scope.ytd-watch-next-secondary-results-renderer ytd-channel-name#channel-name div#container");
  115. channelNameDivs.forEach(div => {
  116. div.style.fontSize = "1.2rem";
  117. div.style.lineHeight = "1.9rem";
  118. });
  119.  
  120. // Remove sponsored thumbnail cards (I included this because UBlock standard filters can't handle them yet)
  121. var sponsoredThumbnails = document.querySelectorAll('div[id="fulfilled-layout"][class*="ytd-ad-slot-renderer"]');
  122. sponsoredThumbnails.forEach(function(thumbnail) {
  123. thumbnail.parentNode.removeChild(thumbnail);
  124. });
  125.  
  126. // Removes weird gap at the top of secondary recommendations
  127. const secondaryDivs = document.querySelectorAll("div#secondary");
  128. secondaryDivs.forEach(div => div.style.cssText = "padding-top: 0px;");
  129.  
  130. }
  131.  
  132.  
  133. // Resize the elements of the video player (otherwise the video player and bottom bar get cut off)
  134. function adjustVideoPlayerElements() {
  135. // Get the container's dimensions
  136. const container = document.getElementById("player");
  137. if (!container) return; // Exit if container is not found
  138.  
  139. // Check if container width is zero (while fullscreen or theatre mode)
  140. if (container.offsetWidth === 0) {
  141. console.log("Skipping resize due to zero-width container.");
  142. return;
  143. }
  144.  
  145. const containerWidth = container.offsetWidth + "px"; // width
  146. const containerHeight = container.offsetHeight + "px"; // height
  147.  
  148. // Adjust main video stream
  149. const videoStream = document.querySelector(".video-stream.html5-main-video");
  150. if (videoStream) {
  151. videoStream.style.width = containerWidth;
  152. videoStream.style.height = container.offsetHeight + "px"; // Set height like this becasue 100% must made the height 0
  153. }
  154.  
  155. // Adjust interactive video content
  156. const ivVideoContent = document.querySelector(".ytp-iv-video-content");
  157. if (ivVideoContent) {
  158. ivVideoContent.style.width = containerWidth;
  159. ivVideoContent.style.height = '100%'; // Set height to 100% of its parent
  160. }
  161.  
  162. // Adjust video controls bar (for some reason didn't sync)
  163. const bottomWidth = (container.offsetWidth - 24) + "px" // Calculate width considering margins
  164. const chromeBottom = document.querySelector(".ytp-chrome-bottom");
  165. if (chromeBottom) {
  166. chromeBottom.style.width = bottomWidth;
  167. chromeBottom.style.left = '12px'; // Align it with a slight gap on the left
  168. }
  169. // Adjust the chapter hover bar within the bottom controls
  170. const chapterHoverBar = document.querySelector(".ytp-chapter-hover-container");
  171. if (chapterHoverBar) {
  172. chapterHoverBar.style.width = bottomWidth;
  173. }
  174. }
  175.  
  176. // Initialize MutationObserver: will run modifyPageElements upon change
  177. const observer = new MutationObserver(modifyPageElements);
  178. observer.observe(document.body, { childList: true, subtree: true });
  179. })();