Disable YouTube Hotkeys with Modern Settings Page

Disable various YouTube hotkeys, including frame skip, with a modern settings page

  1. // ==UserScript==
  2. // @name Disable YouTube Hotkeys with Modern Settings Page
  3. // @namespace http://tampermonkey.net/
  4. // @version 1.4
  5. // @description Disable various YouTube hotkeys, including frame skip, with a modern settings page
  6. // @author You
  7. // @match *://www.youtube.com/*
  8. // @grant GM_registerMenuCommand
  9. // @grant GM_setValue
  10. // @grant GM_getValue
  11. // @grant GM_addStyle
  12. // @license MIT
  13. // ==/UserScript==
  14.  
  15.  
  16. (function() {
  17. 'use strict';
  18.  
  19. // Load saved settings or default to enabling all keys
  20. let settings = GM_getValue('hotkeySettings', {
  21. disableNumericKeys: false,
  22. disableSpacebar: false,
  23. disableArrowKeys: false,
  24. disableFKey: false,
  25. disableMKey: false,
  26. disableSpeedControl: false,
  27. disableFrameSkip: false
  28. });
  29.  
  30. // Function to handle keydown events and disable selected hotkeys
  31. window.addEventListener('keydown', function(e) {
  32. // Disable numeric keys (0-9)
  33. if (settings.disableNumericKeys && e.key >= '0' && e.key <= '9') {
  34. e.stopPropagation();
  35. e.preventDefault();
  36. }
  37.  
  38. // Disable spacebar
  39. if (settings.disableSpacebar && e.code === 'Space') {
  40. e.stopPropagation();
  41. e.preventDefault();
  42. }
  43.  
  44. // Disable arrow keys (left, right, up, down)
  45. if (settings.disableArrowKeys && ['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown'].includes(e.code)) {
  46. e.stopPropagation();
  47. e.preventDefault();
  48. }
  49.  
  50. // Disable F (for fullscreen)
  51. if (settings.disableFKey && e.key.toLowerCase() === 'f') {
  52. e.stopPropagation();
  53. e.preventDefault();
  54. }
  55.  
  56. // Disable M (for mute)
  57. if (settings.disableMKey && e.key.toLowerCase() === 'm') {
  58. e.stopPropagation();
  59. e.preventDefault();
  60. }
  61.  
  62. // Disable speed control (Shift + > or Shift + <)
  63. if (settings.disableSpeedControl && (e.shiftKey && (e.key === '>' || e.key === '<'))) {
  64. e.stopPropagation();
  65. e.preventDefault();
  66. }
  67.  
  68. // Disable frame skip (`,` for backward, `.` for forward)
  69. if (settings.disableFrameSkip && (e.key === ',' || e.key === '.')) {
  70. e.stopPropagation();
  71. e.preventDefault();
  72. }
  73. }, true);
  74.  
  75. // Create and display the settings modal using safe DOM manipulation
  76. function openSettings() {
  77. // Remove any existing modal or overlay
  78. let existingModal = document.getElementById('yt-hotkey-settings-modal');
  79. let existingOverlay = document.getElementById('yt-hotkey-settings-overlay');
  80. if (existingModal) existingModal.remove();
  81. if (existingOverlay) existingOverlay.remove();
  82.  
  83. // Create the modal container
  84. let modal = document.createElement('div');
  85. modal.id = 'yt-hotkey-settings-modal';
  86. modal.className = 'modal-card';
  87.  
  88. // Create modal header
  89. let header = document.createElement('div');
  90. header.className = 'modal-header';
  91.  
  92. let title = document.createElement('h2');
  93. title.textContent = 'YouTube Hotkey Settings';
  94. header.appendChild(title);
  95.  
  96. let closeButton = document.createElement('span');
  97. closeButton.id = 'closeSettingsBtn';
  98. closeButton.className = 'close-btn';
  99. closeButton.textContent = '×';
  100. header.appendChild(closeButton);
  101.  
  102. modal.appendChild(header);
  103.  
  104. // Create modal content (checkboxes)
  105. let content = document.createElement('div');
  106. content.className = 'modal-content';
  107.  
  108. let checkboxes = [
  109. { id: 'disableNumericKeys', label: 'Disable Numeric Keys (0-9)', checked: settings.disableNumericKeys },
  110. { id: 'disableSpacebar', label: 'Disable Spacebar (Play/Pause)', checked: settings.disableSpacebar },
  111. { id: 'disableArrowKeys', label: 'Disable Arrow Keys (Rewind/FF, Volume)', checked: settings.disableArrowKeys },
  112. { id: 'disableFKey', label: 'Disable F Key (Fullscreen)', checked: settings.disableFKey },
  113. { id: 'disableMKey', label: 'Disable M Key (Mute)', checked: settings.disableMKey },
  114. { id: 'disableSpeedControl', label: 'Disable Speed Control (Shift + > / <)', checked: settings.disableSpeedControl },
  115. { id: 'disableFrameSkip', label: 'Disable Frame Skip (`,` and `.`)', checked: settings.disableFrameSkip }
  116. ];
  117.  
  118. checkboxes.forEach(hotkey => {
  119. let label = document.createElement('label');
  120. label.className = 'custom-checkbox';
  121.  
  122. let checkbox = document.createElement('input');
  123. checkbox.type = 'checkbox';
  124. checkbox.id = hotkey.id;
  125. checkbox.checked = hotkey.checked;
  126.  
  127. let checkmark = document.createElement('span');
  128. checkmark.className = 'checkmark';
  129.  
  130. label.appendChild(checkbox);
  131. label.appendChild(checkmark);
  132. label.appendChild(document.createTextNode(` ${hotkey.label}`));
  133. content.appendChild(label);
  134. });
  135.  
  136. modal.appendChild(content);
  137.  
  138. // Create modal footer (save button)
  139. let footer = document.createElement('div');
  140. footer.className = 'modal-footer';
  141.  
  142. let saveButton = document.createElement('button');
  143. saveButton.id = 'saveSettingsBtn';
  144. saveButton.className = 'primary-btn';
  145. saveButton.textContent = 'Save Settings';
  146. footer.appendChild(saveButton);
  147.  
  148. modal.appendChild(footer);
  149.  
  150. // Append modal to the document body
  151. document.body.appendChild(modal);
  152.  
  153. // Create the overlay (dark background behind modal)
  154. let overlay = document.createElement('div');
  155. overlay.id = 'yt-hotkey-settings-overlay';
  156. overlay.className = 'modal-overlay';
  157. document.body.appendChild(overlay);
  158.  
  159. // Close modal on clicking the close button or overlay
  160. closeButton.addEventListener('click', closeSettings);
  161. overlay.addEventListener('click', closeSettings);
  162.  
  163. // Save settings on clicking the save button
  164. saveButton.addEventListener('click', function() {
  165. settings.disableNumericKeys = document.getElementById('disableNumericKeys').checked;
  166. settings.disableSpacebar = document.getElementById('disableSpacebar').checked;
  167. settings.disableArrowKeys = document.getElementById('disableArrowKeys').checked;
  168. settings.disableFKey = document.getElementById('disableFKey').checked;
  169. settings.disableMKey = document.getElementById('disableMKey').checked;
  170. settings.disableSpeedControl = document.getElementById('disableSpeedControl').checked;
  171. settings.disableFrameSkip = document.getElementById('disableFrameSkip').checked;
  172. GM_setValue('hotkeySettings', settings);
  173.  
  174. // Show a success message and close modal after a short delay
  175. showNotification('Settings saved successfully!', modal);
  176. setTimeout(closeSettings, 1500);
  177. });
  178.  
  179. // Function to close the settings modal
  180. function closeSettings() {
  181. modal.remove();
  182. overlay.remove();
  183. }
  184. }
  185.  
  186. // Function to show a notification banner
  187. function showNotification(message, parentElement) {
  188. let banner = document.createElement('div');
  189. banner.className = 'notification-banner';
  190. banner.textContent = message;
  191. parentElement.appendChild(banner);
  192.  
  193. setTimeout(() => banner.remove(), 3000);
  194. }
  195.  
  196. // Register the settings menu command
  197. GM_registerMenuCommand('YouTube Hotkey Settings', openSettings);
  198.  
  199. // Add styles for the modal and modern UI
  200. GM_addStyle(`
  201. /* General Modal Styling */
  202. .modal-card {
  203. position: fixed;
  204. top: 50%;
  205. left: 50%;
  206. transform: translate(-50%, -50%);
  207. background-color: #fff;
  208. border-radius: 10px;
  209. box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
  210. width: 400px;
  211. max-width: 90%;
  212. z-index: 10001;
  213. overflow: hidden;
  214. animation: slide-down 0.3s ease-out;
  215. }
  216.  
  217. .modal-overlay {
  218. position: fixed;
  219. top: 0;
  220. left: 0;
  221. width: 100%;
  222. height: 100%;
  223. background-color: rgba(0, 0, 0, 0.5);
  224. z-index: 10000;
  225. }
  226.  
  227. .modal-header {
  228. display: flex;
  229. justify-content: space-between;
  230. align-items: center;
  231. background-color: #007bff;
  232. color: white;
  233. padding: 15px;
  234. }
  235.  
  236. .modal-content {
  237. padding: 20px;
  238. }
  239.  
  240. .modal-footer {
  241. display: flex;
  242. justify-content: flex-end;
  243. padding: 10px;
  244. border-top: 1px solid #ddd;
  245. }
  246.  
  247. /* Checkbox Styling */
  248. .custom-checkbox {
  249. display: flex;
  250. align-items: center;
  251. margin-bottom: 10px;
  252. font-size: 16px;
  253. }
  254.  
  255. .custom-checkbox input[type="checkbox"] {
  256. display: none;
  257. }
  258.  
  259. .custom-checkbox .checkmark {
  260. display: inline-block;
  261. width: 18px;
  262. height: 18px;
  263. border: 2px solid #007bff;
  264. border-radius: 3px;
  265. margin-right: 10px;
  266. transition: all 0.2s;
  267. }
  268.  
  269. .custom-checkbox input[type="checkbox"]:checked + .checkmark {
  270. background-color: #007bff;
  271. border-color: #007bff;
  272. }
  273.  
  274. /* Button Styling */
  275. .primary-btn {
  276. background-color: #007bff;
  277. color: white;
  278. border: none;
  279. border-radius: 5px;
  280. padding: 10px 20px;
  281. cursor: pointer;
  282. transition: background-color 0.2s;
  283. }
  284.  
  285. .primary-btn:hover {
  286. background-color: #0056b3;
  287. }
  288.  
  289. /* Close Button */
  290. .close-btn {
  291. font-size: 24px;
  292. color: white;
  293. cursor: pointer;
  294. padding: 0 10px;
  295. }
  296.  
  297. /* Notification Banner */
  298. .notification-banner {
  299. position: absolute;
  300. top: 0;
  301. left: 0;
  302. width: 100%;
  303. padding: 10px;
  304. background-color: #28a745;
  305. color: white;
  306. text-align: center;
  307. animation: fade-in-out 3s ease-out;
  308. }
  309.  
  310. /* Animations */
  311. @keyframes slide-down {
  312. from { transform: translate(-50%, -60%); opacity: 0; }
  313. to { transform: translate(-50%, -50%); opacity: 1; }
  314. }
  315.  
  316. @keyframes fade-in-out {
  317. 0%, 100% { opacity: 0; }
  318. 20%, 80% { opacity: 1; }
  319. }
  320. `);
  321. })();