Disable YouTube Hotkeys with Modern Settings Page

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

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         Disable YouTube Hotkeys with Modern Settings Page
// @namespace    http://tampermonkey.net/
// @version      1.4
// @description  Disable various YouTube hotkeys, including frame skip, with a modern settings page
// @author       You
// @match        *://www.youtube.com/*
// @grant        GM_registerMenuCommand
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_addStyle
// @license      MIT
// ==/UserScript==


(function() {
    'use strict';

    // Load saved settings or default to enabling all keys
    let settings = GM_getValue('hotkeySettings', {
        disableNumericKeys: false,
        disableSpacebar: false,
        disableArrowKeys: false,
        disableFKey: false,
        disableMKey: false,
        disableSpeedControl: false,
        disableFrameSkip: false
    });

    // Function to handle keydown events and disable selected hotkeys
    window.addEventListener('keydown', function(e) {
        // Disable numeric keys (0-9)
        if (settings.disableNumericKeys && e.key >= '0' && e.key <= '9') {
            e.stopPropagation();
            e.preventDefault();
        }

        // Disable spacebar
        if (settings.disableSpacebar && e.code === 'Space') {
            e.stopPropagation();
            e.preventDefault();
        }

        // Disable arrow keys (left, right, up, down)
        if (settings.disableArrowKeys && ['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown'].includes(e.code)) {
            e.stopPropagation();
            e.preventDefault();
        }

        // Disable F (for fullscreen)
        if (settings.disableFKey && e.key.toLowerCase() === 'f') {
            e.stopPropagation();
            e.preventDefault();
        }

        // Disable M (for mute)
        if (settings.disableMKey && e.key.toLowerCase() === 'm') {
            e.stopPropagation();
            e.preventDefault();
        }

        // Disable speed control (Shift + > or Shift + <)
        if (settings.disableSpeedControl && (e.shiftKey && (e.key === '>' || e.key === '<'))) {
            e.stopPropagation();
            e.preventDefault();
        }

        // Disable frame skip (`,` for backward, `.` for forward)
        if (settings.disableFrameSkip && (e.key === ',' || e.key === '.')) {
            e.stopPropagation();
            e.preventDefault();
        }
    }, true);

    // Create and display the settings modal using safe DOM manipulation
    function openSettings() {
        // Remove any existing modal or overlay
        let existingModal = document.getElementById('yt-hotkey-settings-modal');
        let existingOverlay = document.getElementById('yt-hotkey-settings-overlay');
        if (existingModal) existingModal.remove();
        if (existingOverlay) existingOverlay.remove();

        // Create the modal container
        let modal = document.createElement('div');
        modal.id = 'yt-hotkey-settings-modal';
        modal.className = 'modal-card';

        // Create modal header
        let header = document.createElement('div');
        header.className = 'modal-header';

        let title = document.createElement('h2');
        title.textContent = 'YouTube Hotkey Settings';
        header.appendChild(title);

        let closeButton = document.createElement('span');
        closeButton.id = 'closeSettingsBtn';
        closeButton.className = 'close-btn';
        closeButton.textContent = '×';
        header.appendChild(closeButton);

        modal.appendChild(header);

        // Create modal content (checkboxes)
        let content = document.createElement('div');
        content.className = 'modal-content';

        let checkboxes = [
            { id: 'disableNumericKeys', label: 'Disable Numeric Keys (0-9)', checked: settings.disableNumericKeys },
            { id: 'disableSpacebar', label: 'Disable Spacebar (Play/Pause)', checked: settings.disableSpacebar },
            { id: 'disableArrowKeys', label: 'Disable Arrow Keys (Rewind/FF, Volume)', checked: settings.disableArrowKeys },
            { id: 'disableFKey', label: 'Disable F Key (Fullscreen)', checked: settings.disableFKey },
            { id: 'disableMKey', label: 'Disable M Key (Mute)', checked: settings.disableMKey },
            { id: 'disableSpeedControl', label: 'Disable Speed Control (Shift + > / <)', checked: settings.disableSpeedControl },
            { id: 'disableFrameSkip', label: 'Disable Frame Skip (`,` and `.`)', checked: settings.disableFrameSkip }
        ];

        checkboxes.forEach(hotkey => {
            let label = document.createElement('label');
            label.className = 'custom-checkbox';

            let checkbox = document.createElement('input');
            checkbox.type = 'checkbox';
            checkbox.id = hotkey.id;
            checkbox.checked = hotkey.checked;

            let checkmark = document.createElement('span');
            checkmark.className = 'checkmark';

            label.appendChild(checkbox);
            label.appendChild(checkmark);
            label.appendChild(document.createTextNode(` ${hotkey.label}`));
            content.appendChild(label);
        });

        modal.appendChild(content);

        // Create modal footer (save button)
        let footer = document.createElement('div');
        footer.className = 'modal-footer';

        let saveButton = document.createElement('button');
        saveButton.id = 'saveSettingsBtn';
        saveButton.className = 'primary-btn';
        saveButton.textContent = 'Save Settings';
        footer.appendChild(saveButton);

        modal.appendChild(footer);

        // Append modal to the document body
        document.body.appendChild(modal);

        // Create the overlay (dark background behind modal)
        let overlay = document.createElement('div');
        overlay.id = 'yt-hotkey-settings-overlay';
        overlay.className = 'modal-overlay';
        document.body.appendChild(overlay);

        // Close modal on clicking the close button or overlay
        closeButton.addEventListener('click', closeSettings);
        overlay.addEventListener('click', closeSettings);

        // Save settings on clicking the save button
        saveButton.addEventListener('click', function() {
            settings.disableNumericKeys = document.getElementById('disableNumericKeys').checked;
            settings.disableSpacebar = document.getElementById('disableSpacebar').checked;
            settings.disableArrowKeys = document.getElementById('disableArrowKeys').checked;
            settings.disableFKey = document.getElementById('disableFKey').checked;
            settings.disableMKey = document.getElementById('disableMKey').checked;
            settings.disableSpeedControl = document.getElementById('disableSpeedControl').checked;
            settings.disableFrameSkip = document.getElementById('disableFrameSkip').checked;
            GM_setValue('hotkeySettings', settings);

            // Show a success message and close modal after a short delay
            showNotification('Settings saved successfully!', modal);
            setTimeout(closeSettings, 1500);
        });

        // Function to close the settings modal
        function closeSettings() {
            modal.remove();
            overlay.remove();
        }
    }

    // Function to show a notification banner
    function showNotification(message, parentElement) {
        let banner = document.createElement('div');
        banner.className = 'notification-banner';
        banner.textContent = message;
        parentElement.appendChild(banner);

        setTimeout(() => banner.remove(), 3000);
    }

    // Register the settings menu command
    GM_registerMenuCommand('YouTube Hotkey Settings', openSettings);

    // Add styles for the modal and modern UI
    GM_addStyle(`
        /* General Modal Styling */
        .modal-card {
            position: fixed;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            background-color: #fff;
            border-radius: 10px;
            box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
            width: 400px;
            max-width: 90%;
            z-index: 10001;
            overflow: hidden;
            animation: slide-down 0.3s ease-out;
        }

        .modal-overlay {
            position: fixed;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background-color: rgba(0, 0, 0, 0.5);
            z-index: 10000;
        }

        .modal-header {
            display: flex;
            justify-content: space-between;
            align-items: center;
            background-color: #007bff;
            color: white;
            padding: 15px;
        }

        .modal-content {
            padding: 20px;
        }

        .modal-footer {
            display: flex;
            justify-content: flex-end;
            padding: 10px;
            border-top: 1px solid #ddd;
        }

        /* Checkbox Styling */
        .custom-checkbox {
            display: flex;
            align-items: center;
            margin-bottom: 10px;
            font-size: 16px;
        }

        .custom-checkbox input[type="checkbox"] {
            display: none;
        }

        .custom-checkbox .checkmark {
            display: inline-block;
            width: 18px;
            height: 18px;
            border: 2px solid #007bff;
            border-radius: 3px;
            margin-right: 10px;
            transition: all 0.2s;
        }

        .custom-checkbox input[type="checkbox"]:checked + .checkmark {
            background-color: #007bff;
            border-color: #007bff;
        }

        /* Button Styling */
        .primary-btn {
            background-color: #007bff;
            color: white;
            border: none;
            border-radius: 5px;
            padding: 10px 20px;
            cursor: pointer;
            transition: background-color 0.2s;
        }

        .primary-btn:hover {
            background-color: #0056b3;
        }

        /* Close Button */
        .close-btn {
            font-size: 24px;
            color: white;
            cursor: pointer;
            padding: 0 10px;
        }

        /* Notification Banner */
        .notification-banner {
            position: absolute;
            top: 0;
            left: 0;
            width: 100%;
            padding: 10px;
            background-color: #28a745;
            color: white;
            text-align: center;
            animation: fade-in-out 3s ease-out;
        }

        /* Animations */
        @keyframes slide-down {
            from { transform: translate(-50%, -60%); opacity: 0; }
            to { transform: translate(-50%, -50%); opacity: 1; }
        }

        @keyframes fade-in-out {
            0%, 100% { opacity: 0; }
            20%, 80% { opacity: 1; }
        }
    `);
})();