YouTube Key Navigation

Add fast, customizable keyboard shortcuts to YouTube. Instantly open Home, Watch Later, and History, or perform actions like Like, Dislike, Subscribe, Toggle Notifications, and Voice Search — all without refreshing the page. Fully configurable keybindings for seamless YouTube navigation and control.

Aby zainstalować ten skrypt, wymagana jest instalacje jednego z następujących rozszerzeń: Tampermonkey, Greasemonkey lub Violentmonkey.

Aby zainstalować ten skrypt, wymagana jest instalacje jednego z następujących rozszerzeń: Tampermonkey, Violentmonkey.

Aby zainstalować ten skrypt, wymagana jest instalacje jednego z następujących rozszerzeń: Tampermonkey, Violentmonkey.

Aby zainstalować ten skrypt, wymagana będzie instalacja rozszerzenia Tampermonkey lub Userscripts.

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

Aby zainstalować ten skrypt, musisz zainstalować rozszerzenie menedżera skryptów użytkownika.

(Mam już menedżera skryptów użytkownika, pozwól mi to zainstalować!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

Będziesz musiał zainstalować rozszerzenie menedżera stylów użytkownika, aby zainstalować ten styl.

Będziesz musiał zainstalować rozszerzenie menedżera stylów użytkownika, aby zainstalować ten styl.

Musisz zainstalować rozszerzenie menedżera stylów użytkownika, aby zainstalować ten styl.

(Mam już menedżera stylów użytkownika, pozwól mi to zainstalować!)

// ==UserScript==
// @name         YouTube Key Navigation
// @namespace    http://tampermonkey.net/
// @version      1.1
// @description  Add fast, customizable keyboard shortcuts to YouTube. Instantly open Home, Watch Later, and History, or perform actions like Like, Dislike, Subscribe, Toggle Notifications, and Voice Search — all without refreshing the page. Fully configurable keybindings for seamless YouTube navigation and control.
// @icon               https://www.google.com/s2/favicons?sz=64&domain=youtube.com

// @author       Anurag Kashyap
// @match        *://www.youtube.com/*
// @match        *://youtube.com/*
// @grant        none
// @license      MIT
// ==/UserScript==

(function () {
    'use strict';

    /**********************************************************
     * 🛠 KEYBIND CONFIG - EDIT THIS PART ONLY
     *
     * Just change the keys in the arrays below.
     * Use lowercase for convenience; script will match both
     * lowercase and uppercase automatically.
     **********************************************************/
    const KEYBINDS = {
        home: ['u'],           // h → go to Home
        watchLater: ['q'],     // . → go to Watch Later
        history: ['h'],        // q → go to History
        notifications: ['z'],  // z → toggle notifications
        voiceSearch: ['x'],    // x → search with your voice
        like: ['b'],           // v → like video
        dislike: ['n'],        // n → dislike video
        subscribe: ['e'],      // b → subscribe to channel
    };
    /**********************************************************/

    // Utility: Check if user is typing in an input/textarea/contenteditable
    function isTypingInInput(e) {
        const target = e.target;
        if (!target) return false;

        const tag = target.tagName;
        const editable = target.isContentEditable;

        return (
            editable ||
            tag === 'INPUT' ||
            tag === 'TEXTAREA' ||
            tag === 'SELECT'
        );
    }

    // Utility: Try multiple selectors until one works
    function clickFirstSelector(selectors) {
        for (const selector of selectors) {
            const el = document.querySelector(selector);
            if (el) {
                el.click();
                return true;
            }
        }
        return false;
    }

    // NAVIGATION HELPERS
    function goHome() {
        const success = clickFirstSelector([
            'a[title="Home"]',
            'a[aria-label="Home"]',
            'a[endpoint*="FEwhat_to_watch"]',
            'a[href="/"]'
        ]);

        if (!success) console.warn('Home link not found.');
    }

    function goWatchLater() {
        const success = clickFirstSelector([
            'a[title="Watch later"]',
            'a[aria-label^="Watch later"]',
            'a[href*="&list=WL"]',
            'a[href*="playlist?list=WL"]'
        ]);

        if (!success) console.warn('Watch later link not found.');
    }

    function goHistory() {
        const success = clickFirstSelector([
            'a[title="History"]',
            'a[aria-label="History"]',
            'a[href="/feed/history"]'
        ]);

        if (!success) console.warn('History link not found.');
    }

    // ACTION HELPERS
   function toggleNotifications() {
    // Prefer the top-right notifications bell in the masthead
    let btn = document.querySelector('ytd-notification-topbar-button-renderer button');

    // Fallback: search all "Notifications" buttons but keep only the one in the topbar
    if (!btn) {
        const candidates = Array.from(
            document.querySelectorAll('button[aria-label*="Notification"]')
        );

        btn = candidates.find(el =>
            el.closest('ytd-notification-topbar-button-renderer') ||
            el.closest('ytd-masthead')
        );
    }

    if (btn) {
        btn.click();
    } else {
        console.warn('Topbar notification button not found.');
    }
}


    function voiceSearch() {
        const success = clickFirstSelector([
            'button[aria-label="Search with your voice"]',
            'button[aria-label*="voice"]',
            'ytd-microphone-button button'
        ]);

        if (!success) console.warn('Voice search button not found.');
    }

    function likeVideo() {
        const success = clickFirstSelector([
            'ytd-segmented-like-dislike-button-renderer yt-button-shape:nth-of-type(1) button',
            'button[aria-label^="Like this video"]',
            'button[aria-pressed][aria-label^="like this video" i]'
        ]);

        if (!success) console.warn('Like button not found.');
    }

    function dislikeVideo() {
        const success = clickFirstSelector([
            'ytd-segmented-like-dislike-button-renderer yt-button-shape:nth-of-type(2) button',
            'button[aria-label^="Dislike this video"]',
            'button[aria-pressed][aria-label^="dislike this video" i]'
        ]);

        if (!success) console.warn('Dislike button not found.');
    }

    function subscribeChannel() {
        const btn = document.querySelector(
            'ytd-subscribe-button-renderer tp-yt-paper-button,' +
            'ytd-subscribe-button-renderer button,' +
            'button[aria-label^="Subscribe"],' +
            'button[aria-label^="Subscribed"]'
        );

        if (!btn) {
            console.warn('Subscribe button not found.');
            return;
        }

        const pressed = btn.getAttribute('aria-pressed');
        if (pressed === 'true') {
            console.log('Already subscribed (aria-pressed=true). No action taken.');
            return;
        }

        btn.click();
    }

    // Build key → action map from KEYBINDS config
    const keyToAction = {};
    function registerKeys() {
        const actions = {
            home: goHome,
            watchLater: goWatchLater,
            history: goHistory,
            notifications: toggleNotifications,
            voiceSearch: voiceSearch,
            like: likeVideo,
            dislike: dislikeVideo,
            subscribe: subscribeChannel,
        };

        for (const [actionName, keys] of Object.entries(KEYBINDS)) {
            const handler = actions[actionName];
            if (!handler) continue;

            keys.forEach(k => {
                if (!k) return;
                const key = String(k).toLowerCase();
                keyToAction[key] = handler;
            });
        }
    }

    registerKeys();

    // KEY LISTENER
    window.addEventListener('keydown', function (e) {
        if (!/\.youtube\.com$/.test(location.hostname)) return;
        if (e.altKey || e.ctrlKey || e.metaKey) return;
        if (isTypingInInput(e)) return;

        const key = e.key.toLowerCase();
        const action = keyToAction[key];

        if (action) {
            e.preventDefault();
            action();
        }
    });
})();