YouTube Settings Rearranger

Rearranges "Playback Speed" and "Quality" settings in YouTube video settings for improved accessibility.

  1. // ==UserScript==
  2. // @name YouTube Settings Rearranger
  3. // @namespace http://tampermonkey.net/
  4. // @version 1.0
  5. // @description Rearranges "Playback Speed" and "Quality" settings in YouTube video settings for improved accessibility.
  6. // @author malordin
  7. // @match https://www.youtube.com/*
  8. // @grant none
  9. // @license MIT
  10. // ==/UserScript==
  11.  
  12. (function() {
  13. 'use strict';
  14.  
  15. // Debug mode flag
  16. const DEBUG = true;
  17.  
  18. /**
  19. * Logs messages to the console with a custom tag.
  20. * @param {string} message - The message to log.
  21. * @param {string} type - The type of log ('log', 'warn', 'error').
  22. */
  23. function log(message, type = 'log') {
  24. if (!DEBUG) return;
  25. const LOG_TAG = '[YouTubeSettingsRearranger]';
  26. switch(type) {
  27. case 'log':
  28. console.log(`${LOG_TAG} ${message}`);
  29. break;
  30. case 'warn':
  31. console.warn(`${LOG_TAG} ${message}`);
  32. break;
  33. case 'error':
  34. console.error(`${LOG_TAG} ${message}`);
  35. break;
  36. default:
  37. console.log(`${LOG_TAG} ${message}`);
  38. }
  39. }
  40.  
  41. // Flag to track if rearrangement has been done for the current menu open
  42. let rearranged = false;
  43.  
  44. // SVG path prefixes for identifying menu items
  45. const PLAYBACK_SPEED_SVG_PREFIX = "M10,8v8l6-4L10,8L10,8z";
  46. const QUALITY_SVG_PREFIX = "M15,17h6v1h-6V17z";
  47.  
  48. /**
  49. * Identifies the "Playback Speed" and "Quality" menu items based on their SVG icons.
  50. * @returns {Object} An object containing the playbackSpeedItem and qualityItem elements.
  51. */
  52. function identifyMenuItems() {
  53. const menuItems = document.querySelectorAll('.ytp-panel-menu .ytp-menuitem');
  54. log(`Found ${menuItems.length} menu items`);
  55.  
  56. let playbackSpeedItem = null;
  57. let qualityItem = null;
  58.  
  59. menuItems.forEach((item, index) => {
  60. const svgPath = item.querySelector('.ytp-menuitem-icon svg path');
  61. if (svgPath) {
  62. const dAttribute = svgPath.getAttribute('d');
  63. if (dAttribute.startsWith(PLAYBACK_SPEED_SVG_PREFIX)) {
  64. playbackSpeedItem = item;
  65. log('Found "Playback Speed"');
  66. }
  67. if (dAttribute.startsWith(QUALITY_SVG_PREFIX)) {
  68. qualityItem = item;
  69. log('Found "Quality"');
  70. }
  71. } else {
  72. log(`Item ${index + 1} does not contain an SVG path`, 'warn');
  73. }
  74. });
  75.  
  76. return { playbackSpeedItem, qualityItem, menuItems };
  77. }
  78.  
  79. /**
  80. * Rearranges the "Playback Speed" menu item to be immediately before the "Quality" menu item.
  81. */
  82. function rearrangeSettingsMenu() {
  83. log('Initializing menu rearrangement');
  84.  
  85. const { playbackSpeedItem, qualityItem, menuItems } = identifyMenuItems();
  86.  
  87. // Check if both items are found
  88. if (playbackSpeedItem && qualityItem) {
  89. // Check if "Playback Speed" is already immediately before "Quality"
  90. const nextSibling = playbackSpeedItem.nextElementSibling;
  91. if (nextSibling === qualityItem) {
  92. log('Rearrangement not needed, items are already in the correct order');
  93. return;
  94. }
  95.  
  96. try {
  97. // Move "Playback Speed" before "Quality"
  98. qualityItem.parentNode.insertBefore(playbackSpeedItem, qualityItem);
  99. log('Rearrangement successful');
  100. rearranged = true; // Set the flag to prevent repeated rearrangement
  101. } catch (error) {
  102. log(`Error during rearrangement: ${error}`, 'error');
  103. }
  104. } else {
  105. if (!playbackSpeedItem) {
  106. log('"Playback Speed" not found', 'warn');
  107. }
  108. if (!qualityItem) {
  109. log('"Quality" not found', 'warn');
  110. }
  111. }
  112. }
  113.  
  114. // MutationObserver to watch for changes in the DOM
  115. const observer = new MutationObserver((mutations, obs) => {
  116. // Check if the settings menu is visible
  117. const settingsMenu = document.querySelector('.ytp-panel-menu');
  118. const isMenuVisible = settingsMenu && settingsMenu.offsetParent !== null;
  119.  
  120. if (isMenuVisible && !rearranged) {
  121. log('Settings menu opened');
  122. // Add a slight delay to ensure the menu is fully loaded
  123. setTimeout(() => {
  124. rearrangeSettingsMenu();
  125. }, 200); // Delay in milliseconds
  126. }
  127.  
  128. if (!isMenuVisible && rearranged) {
  129. log('Settings menu closed');
  130. rearranged = false; // Reset the flag when the menu is closed
  131. }
  132. });
  133.  
  134. // Start observing the document body for changes
  135. observer.observe(document.body, { childList: true, subtree: true });
  136. log('Script activated and observing DOM changes');
  137.  
  138. })();