Automatic Material Dark-Mode for YouTube

A low-tech solution to a high-tech problem! Automatically clicks YouTube's "Dark Mode" button if dark mode isn't already active.

当前为 2017-09-13 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Automatic Material Dark-Mode for YouTube
  3. // @namespace SteveJobzniak
  4. // @version 1.2
  5. // @description A low-tech solution to a high-tech problem! Automatically clicks YouTube's "Dark Mode" button if dark mode isn't already active.
  6. // @author SteveJobzniak
  7. // @match *://www.youtube.com/*
  8. // @exclude *://www.youtube.com/tv*
  9. // @exclude *://www.youtube.com/embed/*
  10. // @run-at document-end
  11. // @grant none
  12. // @noframes
  13. // ==/UserScript==
  14.  
  15. (function() {
  16. 'use strict';
  17.  
  18. function enableDark() {
  19. // Wait a moment so that the whole page menu is loaded and the "dark mode state" is updated...
  20. var attempt = 0, maxAttempts = 8, waitDelay = 250; // 8*250ms = Max 2 seconds of retries.
  21. var menuWaitTimer = setInterval( function() {
  22. // We must find the "settings" menu, otherwise YouTube hasn't fully loaded yet...
  23. var menuButtons = document.querySelectorAll( 'yt-icon.style-scope.ytd-topbar-menu-button-renderer' );
  24.  
  25. // If we've reached max attempts or found success, we must now stop the interval timer.
  26. if( ++attempt >= maxAttempts || menuButtons.length === 2 ) {
  27. clearInterval( menuWaitTimer );
  28. }
  29.  
  30. // Failed to find the menu bar. Skip this attempt...
  31. if( menuButtons.length !== 2 ) {
  32. return;
  33. }
  34.  
  35. // Check the dark mode state "flag" and only process if dark mode isn't already active.
  36. if( document.body.getAttribute( 'dark' ) !== 'true' ) {
  37. // We MUST open the "settings" menu, otherwise nothing will react to the "toggle dark mode" event!
  38. menuButtons[1].click(); // Click the 2nd menu button.
  39.  
  40. // Wait a moment for the settings-menu to open up after clicking...
  41. setTimeout( function() {
  42. // Next, open the "toggle dark mode" settings sub-page.
  43. var subButtons = document.querySelectorAll( 'div#label.style-scope.ytd-toggle-theme-compact-link-renderer' );
  44. if( subButtons.length === 1 ) {
  45. subButtons[0].click(); // Click the "dark mode" sub-page button.
  46.  
  47. // Wait a moment for the sub-page to switch...
  48. setTimeout( function() {
  49. // Get a reference to the "toggle dark mode" settings sub-page. This only works if we opened the "dark mode" sub-page.
  50. var toggleMenuElem = document.querySelectorAll( 'ytd-toggle-item-renderer.style-scope.ytd-multi-page-menu-renderer' );
  51. toggleMenuElem = (toggleMenuElem.length === 1 ? toggleMenuElem[0] : undefined); // Always missing unless we visit the page...
  52.  
  53. if( toggleMenuElem ) {
  54. // Get a reference to the "activate dark mode" button.
  55. var darkModeButton = toggleMenuElem.querySelectorAll( 'paper-toggle-button.style-scope.ytd-toggle-item-renderer' );
  56. darkModeButton = (darkModeButton.length === 1 ? darkModeButton[0] : undefined);
  57.  
  58. // Now simply click the button to enable dark mode.
  59. darkModeButton.click();
  60.  
  61. // Give keyboard focus to the input serach field.
  62. setTimeout( function() {
  63. var searchField = document.querySelectorAll( 'input#search' );
  64. if( searchField.length === 1 ) {
  65. // Clicking the searchfield first is just done to hide the settings-panel.
  66. searchField[0].click();
  67. searchField[0].focus();
  68. }
  69. }, 150 );
  70. }
  71.  
  72. // Alternative method, which switches using an event instead of clicking the button...
  73. // I decided to disable this method, since it relies on intricate internal details...
  74. // and requires the menu to be open to work anyway, so we may as well just click it...
  75. /*
  76. var ytDebugMenu = document.querySelectorAll('ytd-debug-menu');
  77. ytDebugMenu = (ytDebugMenu.length === 1 ? ytDebugMenu[0] : undefined);
  78. if( ytDebugMenu ) {
  79. ytDebugMenu.fire(
  80. 'yt-action',
  81. {
  82. actionName:'yt-signal-action-toggle-dark-theme-on',
  83. optionalAction:false,
  84. args:[
  85. {signalAction:{signal:'TOGGLE_DARK_THEME_ON'}},
  86. toggleMenuElem,
  87. undefined
  88. ],
  89. returnValue: []
  90. },
  91. {}
  92. );
  93. }
  94. */
  95. }, 250 ); // Delay to wait for the dark mode settings subpage to open.
  96. }
  97. }, 500 ); // Delay to wait for the settings menu to fully open.
  98. }
  99. }, waitDelay ); // Check-interval after page load to ensure the deferred menu topbar and dark mode state are fully loaded.
  100. }
  101.  
  102. if( document.readyState === 'complete' ) {
  103. enableDark();
  104. } else {
  105. document.addEventListener( 'readystatechange', function( evt ) {
  106. if( document.readyState === 'complete' ) {
  107. enableDark();
  108. }
  109. } );
  110. }
  111. })();