YouTube Classic UI Restorer (Trusted Types Safe)

Reverts YouTube UI to classic layout with security compliance

  1. // ==UserScript==
  2. // @name YouTube Classic UI Restorer (Trusted Types Safe)
  3. // @namespace x0t.youtubeuirevert
  4. // @version 1.4.0
  5. // @description Reverts YouTube UI to classic layout with security compliance
  6. // @author HAMZA
  7. // @match *://*.youtube.com/*
  8. // @run-at document-start
  9. // @grant none
  10. // @license MIT
  11. // ==/UserScript==
  12.  
  13. (function() {
  14. 'use strict';
  15.  
  16. // =============================================
  17. // SAFE CSS INJECTION (Trusted Types compliant)
  18. // =============================================
  19. const cssStyles = `
  20. /* Player controls restoration */
  21. .ytp-chrome-bottom {
  22. width: calc(100% - 24px) !important;
  23. left: 12px !important;
  24. }
  25. .ytp-progress-bar-container {
  26. bottom: 49px !important;
  27. }
  28. .ytp-chrome-controls {
  29. padding: 0 12px !important;
  30. }
  31. /* Classic layout restoration */
  32. ytd-rich-grid-row {
  33. display: block !important;
  34. }
  35. #contents.ytd-rich-grid-row {
  36. justify-content: flex-start !important;
  37. }
  38. ytd-video-renderer {
  39. width: 300px !important;
  40. margin: 12px !important;
  41. }
  42. /* Hide new UI elements */
  43. ytd-enforcement-message-view-model,
  44. div[is-shared-heimdall] {
  45. display: none !important;
  46. }
  47. `;
  48.  
  49. // Create and append style element safely
  50. const injectCSS = () => {
  51. if (document.head) {
  52. const style = document.createElement('style');
  53. style.textContent = cssStyles; // Safe method (uses textContent)
  54. document.head.appendChild(style);
  55. return true;
  56. }
  57. return false;
  58. };
  59.  
  60. // Retry until successful (DOM might not be ready immediately)
  61. const cssInterval = setInterval(() => {
  62. if (injectCSS()) clearInterval(cssInterval);
  63. }, 100);
  64.  
  65. // =============================================
  66. // EXPERIMENT FLAGS CONFIGURATION
  67. // =============================================
  68. const requiredFlags = {
  69. web_player_enable_featured_product_banner_exclusives_on_desktop: false,
  70. kevlar_watch_comments_ep_disable_theater: true,
  71. kevlar_watch_comments_panel_button: true,
  72. kevlar_watch_flexy_metadata_height: 136,
  73. kevlar_watch_grid: false,
  74. web_watch_theater_chat: false,
  75. // ... add other flags as needed
  76. };
  77.  
  78. const configureFlags = () => {
  79. try {
  80. if (typeof ytcfg !== 'undefined' && ytcfg.get('EXPERIMENT_FLAGS')) {
  81. const currentFlags = ytcfg.get('EXPERIMENT_FLAGS');
  82. // Only update if needed to minimize DOM thrashing
  83. let needsUpdate = false;
  84. for (const [key, value] of Object.entries(requiredFlags)) {
  85. if (currentFlags[key] !== value) {
  86. currentFlags[key] = value;
  87. needsUpdate = true;
  88. }
  89. }
  90.  
  91. if (needsUpdate) {
  92. ytcfg.set('EXPERIMENT_FLAGS', currentFlags);
  93. }
  94. }
  95. } catch (e) {
  96. console.debug('[Classic UI] Flag configuration error:', e);
  97. }
  98. };
  99.  
  100. // =============================================
  101. // MUTATION OBSERVER FOR DYNAMIC CONTENT
  102. // =============================================
  103. const observer = new MutationObserver(mutations => {
  104. mutations.forEach(mutation => {
  105. if (mutation.addedNodes.length) {
  106. configureFlags();
  107. }
  108. });
  109. });
  110.  
  111. // =============================================
  112. // INITIALIZATION SEQUENCE
  113. // =============================================
  114. const initialize = () => {
  115. // Initial configuration
  116. configureFlags();
  117.  
  118. // Set up periodic checks (less aggressive than 1s)
  119. const configInterval = setInterval(configureFlags, 3000);
  120. // Start observing document body
  121. if (document.body) {
  122. observer.observe(document.body, {
  123. childList: true,
  124. subtree: true
  125. });
  126. }
  127.  
  128. // Cleanup on page unload
  129. window.addEventListener('beforeunload', () => {
  130. clearInterval(configInterval);
  131. observer.disconnect();
  132. });
  133. };
  134.  
  135. // Start initialization when DOM is ready
  136. if (document.readyState === 'loading') {
  137. document.addEventListener('DOMContentLoaded', initialize);
  138. } else {
  139. initialize();
  140. }
  141. })();

QingJ © 2025

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