NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript== // @name YouTube Auto Liker // @namespace http://tampermonkey.net/ // @version 1.3 // @description Auto-like YouTube videos once a user-defined percentage is watched if not already liked/disliked. // @author nascent // @match https://www.youtube.com/* // @icon https://www.google.com/s2/favicons?domain=youtube.com&sz=128 // @updateURL https://openuserjs.org/meta/nascent/YouTube_Auto_Liker.meta.js // @downloadURL https://openuserjs.org/install/nascent/YouTube_Auto_Liker.user.js // @copyright 2025, nascent (https://openuserjs.org/users/nascent) // @license MIT // @grant GM_getValue // @grant GM_setValue // @grant GM_registerMenuCommand // ==/UserScript== (function() { "use strict"; // =================================================== // Global configuration for notifications // =================================================== let notificationsEnabled = GM_getValue("notificationsEnabled", true); const NOTIFICATION_DURATION_MILLIS = 3000; // Duration for toast notification let lastToastElement = null; // Function to show a toast notification. function showNotification(message) { if (!notificationsEnabled) { 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: '0', bottom: '12px', maxWidth: '297.547px', maxHeight: '48px', zIndex: '2202', opacity: '1', }; 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); setTimeout(() => { toast.style.transform = 'translateY(200%)'; }, Math.max(0, NOTIFICATION_DURATION_MILLIS)); } // =================================================== // Configurable Threshold Using GM_getValue/GM_setValue // =================================================== const defaultThreshold = 70; // Default percentage threshold. let percentage = GM_getValue("percentage", defaultThreshold); // Option: Only auto-like if subscribed. let onlyAutoLikeIfSubscribed = GM_getValue("onlyAutoLikeIfSubscribed", false); // =================================================== // Register Menu Commands // =================================================== GM_registerMenuCommand("Set Auto-Like Percentage", setThreshold); GM_registerMenuCommand("Toggle Notifications", toggleNotifications); GM_registerMenuCommand("Toggle Only Auto-Like If Subscribed", toggleOnlyAutoLikeIfSubscribed); function setThreshold() { const input = prompt("Enter new auto-like percentage (0-100):", percentage); // Remove extra whitespace input = input.trim(); if (input !== null) { const newPercentage = Number(input); if (isNaN(newPercentage) || newPercentage < 0 || newPercentage > 100) { alert("Invalid value. Please enter a number between 0 and 100."); return; } percentage = newPercentage; GM_setValue("percentage", percentage); alert(`Auto-Like percentage threshold updated to ${percentage}%.`); } const newPercentage = Number(input); // Validate that the new percentage is an integer and between 0 (inclusive) and 100 (exclusive). if (!Number.isInteger(newPercentage) || newPercentage < 0 || newPercentage >= 100) { alert("Invalid value. Please enter an integer number between 0 and 99."); return; } percentage = newPercentage; GM_setValue("percentage", percentage); alert(`Auto-Like percentage threshold updated to ${percentage}%.`); } function toggleNotifications() { notificationsEnabled = !notificationsEnabled; GM_setValue("notificationsEnabled", notificationsEnabled); alert(`Notifications have been ${notificationsEnabled ? "enabled" : "disabled"}.`); } function toggleOnlyAutoLikeIfSubscribed() { onlyAutoLikeIfSubscribed = !onlyAutoLikeIfSubscribed; GM_setValue("onlyAutoLikeIfSubscribed", onlyAutoLikeIfSubscribed); alert(`Auto-like only if subscribed is now ${onlyAutoLikeIfSubscribed ? "enabled" : "disabled"}.`); } // =================================================== // Auto-Like Script Logic // =================================================== // Ensure we only attempt auto-like once per video. function getVideoId() { const urlParams = new URLSearchParams(window.location.search); return urlParams.get("v"); } // Store the current video ID and liked state. let currentVideoId = getVideoId(); let wasLiked = false; const checkInterval = 10000; // Check every 10 seconds. setInterval(() => { // ----------------------------------------------------- // Ad detection: skip auto-like if an ad is playing. // ----------------------------------------------------- const player = document.getElementById("movie_player"); if (player && player.classList.contains("ad-showing")) { console.log("YouTube Auto Liker: Ad is playing. Skipping auto-like."); return; } // ----------------------------------------------------- // Check if a new video has loaded (for inline playlists) // ----------------------------------------------------- const newVideoId = getVideoId(); if (newVideoId && newVideoId !== currentVideoId) { console.debug(`YouTube Auto Liker: Detected new video. Previous video id: ${currentVideoId}, New video id: ${newVideoId}`); currentVideoId = newVideoId; wasLiked = false; // reset auto-like flag for the new video } // Get the video element on the page. const video = document.querySelector("video"); if (!video) { console.debug("YouTube Auto Liker: Video element not found yet."); return; } // Ensure we have a valid video duration. if (!video.duration) { console.debug("YouTube Auto Liker: Video duration not available yet."); return; } // Calculate the percentage watched. const progress = (video.currentTime / video.duration) * 100; console.debug(`YouTube Auto Liker: Video progress: ${progress.toFixed(2)}%`); // When the progress reaches the threshold and the video hasn't been processed yet... if (progress >= percentage) { // If the "only auto-like if subscribed" option is enabled, check subscription. if (onlyAutoLikeIfSubscribed) { const subscribeButton = document.querySelector("ytd-subscribe-button-renderer"); if (subscribeButton) { const subscribeText = subscribeButton.textContent || ""; if (subscribeText.trim().includes("SubscribedSubscribed")) { console.debug("YouTube Auto Liker: User is subscribed to the channel."); } else { console.log("YouTube Auto Liker: User is not subscribed to the channel. Skipping auto-like."); return; } } else { console.log("YouTube Auto Liker: Subscribe button not found. Skipping auto-like."); return; } } console.debug("YouTube Auto Liker: progress >= percentage:" + (progress >= percentage)); console.debug("YouTube Auto Liker: wasLiked:" + wasLiked); if (!wasLiked) { // Locate the like and dislike buttons using the updated selectors. const likeButton = document.querySelector( "segmented-like-dislike-button-view-model like-button-view-model button[aria-pressed]" ); const dislikeButton = document.querySelector( "segmented-like-dislike-button-view-model dislike-button-view-model button[aria-pressed]" ); // Check if the buttons were found if (!likeButton || !dislikeButton) { console.error("YouTube Auto Liker: Like/Dislike buttons not found. Aborting check."); return; } // Check the active state by reading the aria-pressed attribute. const isLiked = likeButton.getAttribute("aria-pressed") === "true"; const isDisliked = dislikeButton.getAttribute("aria-pressed") === "true"; console.debug("YouTube Auto Liker: isLiked: " + isLiked); console.debug("YouTube Auto Liker: isDisliked: " + isDisliked); if (!isLiked && !isDisliked) { console.log(`YouTube Auto Liker: Video ${currentVideoId} has reached ${progress.toFixed(2)}% and is neither liked nor disliked. Liking now...`); likeButton.click(); // Show the "Video liked" notification. showNotification("Video auto-liked ❤️"); } else { console.log("YouTube Auto Liker: Video " + currentVideoId + " is already liked/disliked. No action taken."); } // Mark that the video has been processed to avoid repeated action. wasLiked = true; } else { console.log("YouTube Auto Liker: Video " + currentVideoId + " has already been liked/disliked. No action taken."); } } }, checkInterval); })();