NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript==
// @name Youtube like/dislike video and skip ad keyboard shortcuts
// @namespace youtube
// @author nascent
// @include https://www.youtube.com/*
// @description Adds keyboard shortcuts [ and ] for liking and disliking videos, and s for skipping pre-video and banner ads. Option for browser notifications
// @version 1.8
// @grant GM_getValue
// @grant GM_setValue
// @grant GM_registerMenuCommand
// @run-at document-end
// @icon https://www.google.com/s2/favicons?domain=youtube.com&sz=128
// @license GPL-3.0-or-later
// ==/UserScript==
/* jshint esversion: 6 */
(function () {
"use strict";
// Retrieve the saved setting; if not set, default to true.
let browserNotifications = GM_getValue("browserNotifications", true);
const NOTIFICATION_DURATION_MILLIS = 3000; // duration for toast notification
let lastToastElement = null;
// Menu command for toggling browser notifications.
function toggleBrowserNotifications() {
browserNotifications = !browserNotifications;
GM_setValue("browserNotifications", browserNotifications);
alert(`Browser notifications are now ${browserNotifications ? "enabled" : "disabled"}.`);
}
GM_registerMenuCommand("Toggle Browser Notifications", toggleBrowserNotifications);
// Function to show a toast notification.
function showNotification(message) {
if (!browserNotifications) {
return;
}
// If a toast is already visible, remove it.
if (lastToastElement !== null) {
lastToastElement.remove();
lastToastElement = null;
}
// Create a toast element (YouTube uses tp-yt-paper-toast for notifications).
const toast = document.createElement('tp-yt-paper-toast');
toast.innerText = message;
toast.classList.add('toast-open');
// Set up inline style properties for the toast.
const styleProps = {
outline: 'none',
position: 'fixed',
left: '24px',
bottom: '24px',
padding: '16px 24px',
backgroundColor: '#323232',
color: '#f1f1f1',
fontSize: '14px',
borderRadius: '4px',
boxShadow: '0 2px 5px 0 rgba(0,0,0,0.26)',
maxWidth: '297.547px',
maxHeight: '48px',
zIndex: '2202',
opacity: '1',
transition: 'transform 0.3s ease-in-out',
transform: 'translateY(200%)'
};
for (const prop in styleProps) {
toast.style[prop] = styleProps[prop];
}
document.body.appendChild(toast);
lastToastElement = toast;
// Show the toast notification.
setTimeout(() => {
toast.style.display = 'block';
}, 0);
// Ensure the toast animation occurs.
setTimeout(() => {
toast.style.transform = 'none';
}, 10);
// Hide and cleanly remove toast from the DOM after duration
setTimeout(() => {
if (toast) {
toast.style.transform = 'translateY(200%)';
setTimeout(() => {
if (toast.parentNode) {
toast.parentNode.removeChild(toast);
}
if (lastToastElement === toast) {
lastToastElement = null;
}
}, 300); // Wait for CSS transition to finish
}
}, Math.max(0, NOTIFICATION_DURATION_MILLIS));
}
// Listen for keydown events.
window.addEventListener("keydown", (e) => {
// Only run on video watch pages.
if (!/^\/watch/.test(location.pathname)) {
return;
}
// Avoid interfering with input fields or editable content.
const tag = e.target.tagName.toLowerCase();
if (e.target.isContentEditable || tag === "input" || tag === "textarea") {
return;
}
// Ignore when modifier keys are held.
if (e.ctrlKey || e.altKey || e.metaKey || e.shiftKey) {
return;
}
// "[" key to toggle like.
if (e.code === "BracketLeft") {
// Find the button dynamically at the exact moment of the keypress
const likeBtn = document.querySelector("like-button-view-model button") ||
document.querySelector('button[aria-label*="like this video" i]');
if (likeBtn) {
likeBtn.click();
if (browserNotifications) {
setTimeout(() => {
const action =
likeBtn.getAttribute("aria-pressed") === "true"
? "Video Liked ❤️"
: "Like Removed";
showNotification(action);
}, 100);
}
}
}
// "]" key to toggle dislike.
else if (e.code === "BracketRight") {
// Find the button dynamically at the exact moment of the keypress
const dislikeBtn = document.querySelector("dislike-button-view-model button") ||
document.querySelector('button[aria-label*="dislike this video" i]');
if (dislikeBtn) {
dislikeBtn.click();
if (browserNotifications) {
setTimeout(() => {
const action =
dislikeBtn.getAttribute("aria-pressed") === "true"
? "Video Disliked 👎"
: "Dislike Removed";
showNotification(action);
}, 100);
}
}
}
// "S" key to skip ads.
else if (e.code === "KeyS") {
const skipAdBtn = document.querySelector(".ytp-ad-skip-button, .ytp-ad-skip-button-modern, .ytp-skip-ad-button");
const skipBannerAdBtn = document.querySelector(".ytp-ad-overlay-close-button");
if (skipAdBtn) {
skipAdBtn.click();
} else if (skipBannerAdBtn) {
skipBannerAdBtn.click();
}
}
});
})();