// ==UserScript== // @name YTCreme // @namespace http://tampermonkey.net/ // @version 2025-01-22 // @description YouTube better, like an elite. // @author abstraction // @match https://www.youtube.com/* // @icon https://www.google.com/s2/favicons?sz=64&domain=youtube.com // @grant none // ==/UserScript== /** * This script modifies YouTube videos' visibility based on percentage rating or view count. * Companion: https://microsoftedge.microsoft.com/addons/detail/thumbnail-rating-bar-for-/mglepphnjnfcljjafdgafoipiakakbin * If `shouldRemove` is true, it completely removes videos, otherwise, it lowers their opacity. */ // Flag to control whether to remove videos or just lower their opacity let shouldRemove = true; // Set this to `true` to remove the videos, `false` to lower opacity /** * Process all videos by checking their percentage ratings and view counts. * Videos with low ratings or views are either removed or have reduced opacity. */ function processVideos() { try { // Determine if we are on the main video page or the recommendation page const isWatchPage = window.location.pathname.includes('/watch'); // Select the appropriate video item container based on the page const videoItemSelector = isWatchPage ? 'ytd-compact-video-renderer.ytd-item-section-renderer' // For recommendation page : 'ytd-rich-item-renderer.ytd-rich-grid-renderer'; // For main grid page // Get all elements with the 'ytrb-tooltip' containing the percentage const percentageElements = document.querySelectorAll('ytrb-tooltip'); if (!percentageElements.length) return; // Early exit if no percentage elements are found percentageElements.forEach(percentageElement => { // Extract percentage value from tooltip content const percentageText = percentageElement.querySelector('div')?.textContent.trim(); const percentageMatch = percentageText?.match(/(\d+\.?\d*)%/); if (!percentageMatch) return; const percentage = parseFloat(percentageMatch[1]); // Process video if percentage is below 99 if (percentage < 99) { const videoGridItem = percentageElement.closest(videoItemSelector); if (videoGridItem) { if (shouldRemove) { videoGridItem.remove(); } else { videoGridItem.style.opacity = 0.05; } } } // Check if the video has fewer than 1000 views or is a live stream const metadataLine = percentageElement.closest(videoItemSelector)?.querySelector('#metadata-line'); if (metadataLine) { const viewText = metadataLine.textContent.trim(); // If the metadata line does not contain the word 'views', it's likely a live video if (!viewText.includes('views')) { const videoGridItem = percentageElement.closest(videoItemSelector); if (videoGridItem) { if (shouldRemove) { videoGridItem.remove(); } else { videoGridItem.style.opacity = 0.05; } } } else { // Extract and convert view count to numeric value const viewMatch = viewText.match(/(\d+\.?\d*)\s*(k|m|b)?\s*views/i); if (viewMatch) { let viewCount = parseFloat(viewMatch[1]); const multiplier = { k: 1000, m: 1_000_000, b: 1_000_000_000 }; const suffix = viewMatch[2]?.toLowerCase(); if (multiplier[suffix]) { viewCount *= multiplier[suffix]; } // If the video has fewer than 1000 views, apply the toggle logic if (viewCount < 1000) { const videoGridItem = percentageElement.closest(videoItemSelector); if (videoGridItem) { if (shouldRemove) { videoGridItem.remove(); } else { videoGridItem.style.opacity = 0.05; } } } } } } }); } catch (error) { console.error('Error processing videos:', error); } } /** * Initialize the MutationObserver to monitor DOM changes (e.g., infinite scroll). */ const observer = new MutationObserver(debounce(processVideosTwice, 500)); // Debouncing to reduce the frequency of calls // Configure the observer to track added nodes in the DOM observer.observe(document.body, { childList: true, subtree: true }); /** * Debounce function to limit the rate at which a function is invoked. * @param {Function} func - The function to debounce * @param {number} wait - Time in milliseconds to wait before invoking the function * @returns {Function} - A debounced version of the provided function */ function debounce(func, wait) { let timeout; return function() { clearTimeout(timeout); timeout = setTimeout(() => func.apply(this, arguments), wait); }; } function processVideosTwice (){ // Initial processing of videos on page load processVideos(); // Do it one again setTimeout(processVideos, 1000); } processVideosTwice();