Enlarges YouTube chat profile pictures on mouse over, shows HD version, Caches HD images for faster display using Tampermonkey caching.
// ==UserScript== // @name Enlarge YouTube Chat Profile Pictures (HD Version with Caching) // @namespace typpi.online // @version 2.1 // @description Enlarges YouTube chat profile pictures on mouse over, shows HD version, Caches HD images for faster display using Tampermonkey caching. // @author Nick2bad4u // @match https://www.youtube.com/* // @grant GM_setValue // @grant GM_getValue // @grant GM_addStyle // @icon https://www.google.com/s2/favicons?sz=64&domain=youtube.com // @license UnLicense // @tag youtube // ==/UserScript== (function () { 'use strict'; let debounceTimeout; const CACHE_TTL_MS = 24 * 60 * 60 * 1000; // 24 hours in milliseconds const preloadedImages = new Map(); // Load cache from Tampermonkey storage function loadCache() { const cache = GM_getValue('profilePicCache', {}); const now = Date.now(); // Clear out expired cache entries Object.keys(cache).forEach((key) => { if (now - cache[key].timestamp > CACHE_TTL_MS) { delete cache[key]; // Remove expired entry } }); GM_setValue('profilePicCache', cache); // Update cache after removing expired entries return cache; } // Save cache to Tampermonkey storage function saveCache(cache) { GM_setValue('profilePicCache', cache); } let cache = loadCache(); // Load the cache once when the script runs // Preload HD image function preloadHDImage(src) { const hdSrc = src.replace(/=s32-c/, '=s800-c'); // Adjust as needed for HD if (!preloadedImages.has(hdSrc)) { if (cache[hdSrc]) { // If in persistent cache, load directly from cache preloadedImages.set(hdSrc, cache[hdSrc].url); } else { // Preload HD image and store in cache const img = new Image(); img.src = hdSrc; preloadedImages.set(hdSrc, hdSrc); // Store in memory cache[hdSrc] = { url: hdSrc, timestamp: Date.now(), }; // Cache with timestamp saveCache(cache); // Save the updated cache } } } // Function to enlarge profile pictures, show HD image, add black outline, and shift to the right function enlargeProfilePic(event) { clearTimeout(debounceTimeout); debounceTimeout = setTimeout(() => { const img = event.target; const originalSrc = img.src; const hdSrc = originalSrc.replace(/=s32-c/, '=s800-c'); // Increase the size to 800px img.dataset.originalSrc = originalSrc; // Store the original src // Swap in the HD version, check preloadedImages cache img.src = preloadedImages.get(hdSrc) || hdSrc; img.style.transform = 'scale(6) translateX(20px)'; img.style.transition = 'transform 0.2s ease'; img.style.border = '1px solid black'; img.style.zIndex = '9999'; img.style.position = 'relative'; // Reset after 3 seconds setTimeout(() => { resetProfilePic(img); }, 3000); }, 100); } // Function to reset profile pictures to original size and source function resetProfilePic(img) { img.src = img.dataset.originalSrc || img.src; // Restore the original src if it was replaced img.style.transform = 'scale(1) translateX(0)'; img.style.border = 'none'; img.style.zIndex = 'auto'; img.style.position = 'static'; } // Add event listeners to profile pictures function addEventListeners() { const profilePics = document.querySelectorAll( '.h-5.w-5.inline.align-middle.rounded-full.flex-none', ); profilePics.forEach((pic) => { preloadHDImage(pic.src); // Preload HD image pic.addEventListener('mouseover', enlargeProfilePic); }); } // Observe changes in the chat to dynamically add event listeners const observer = new MutationObserver(() => { addEventListeners(); }); observer.observe(document.body, { childList: true, subtree: true, }); // Initial call to add event listeners addEventListeners(); })();