// ==UserScript== // @name Google Image Search - Show Image Dimensions // @name:de Google Bildersuche - Bildabmessungen anzeigen // @name:fr Google Image Search - Afficher les dimensions de l'image // @name:es Búsqueda de imágenes de Google - Mostrar las dimensiones de la imagen // @name:it Ricerca immagini su Google - Mostra le dimensioni delle immagini // @name:pl Wyszukiwanie obrazów Google - Pokaż wymiary obrazu // @name:ru Поиск изображений Google - Показать размеры изображений // @description Displays image dimensions (eg. "1920 × 1080") for each thumbnail on the Google Image Search results page. // @description:de Zeigt die Bildabmessungen (z. B. "1920 × 1080") für jedes Vorschaubild auf der Ergebnisseite der Google-Bildsuche an. // @description:fr Affiche les dimensions de l'image (par exemple, "1920 × 1080") pour chaque miniature sur la page de résultats de Google Image Search. // @description:es Muestra las dimensiones de la imagen (p. ej., "1920 × 1080") para cada miniatura de la página de resultados de Google Image Search. // @description:it Visualizza le dimensioni dell'immagine (ad es. "1920 × 1080") per ogni miniatura nella pagina dei risultati della ricerca immagini di Google. // @description:pl Wyświetla wymiary obrazu (np. "1920 × 1080") dla każdej miniaturki na stronie wyników wyszukiwania obrazów Google. // @description:ru Отображает размеры изображения (например, "1920 × 1080") для каждой миниатюры на странице результатов поиска изображений Google. // @namespace https://github.com/tadwohlrapp // @author Tad Wohlrapp // @version 1.6.0 // @license MIT // @homepageURL https://github.com/tadwohlrapp/google-image-search-show-image-dimensions-userscript // @supportURL https://github.com/tadwohlrapp/google-image-search-show-image-dimensions-userscript/issues // @icon https://github.com/tadwohlrapp/google-image-search-show-image-dimensions-userscript/raw/main/icon.png // @grant unsafeWindow // @include https://*.google.tld/*udm=2* // @include https://*.google.tld/*lns_surface=* // @compatible firefox Tested on Firefox v146 with Violentmonkey v2.31.0, Tampermonkey v5.4.1 and Greasemonkey v4.13.0 // @compatible chrome Tested on Chrome v143 with Violentmonkey v2.31.0 and Tampermonkey v5.4.1 // @compatible edge Tested on Edge v143 with Violentmonkey v2.31.0 and Tampermonkey v5.4.0 // @compatible opera Tested on Opera v125 with Tampermonkey v5.3.6222 // @downloadURL none // ==/UserScript== (function () { 'use strict'; // Check if url parameter 'udm=2' exists (regular Google Image Search) const isImgSearch = () => new URLSearchParams(window.location.search).get('udm') === '2'; // Check if url parameter 'lns_surface' exists (Google Lens mode) const isLens = () => new URLSearchParams(window.location.search).get('lns_surface'); // Selectors for regular Google Image Search const selectors = { 'wrapper': 'div[data-id="mosaic"][data-viewer-group="1"]>div', 'item': 'div[data-attrid="images universal"][jsdata]' }; const handleUrlChange = () => { if (isLens()) { // Different selectors for Google Lens mode ("Search Image with Google Lens" or pasted / uploaded image as Google Search input) selectors.wrapper = 'div[data-id="mosaic"][data-viewer-group="1"]>div>div'; selectors.item = 'div[data-snf][data-snm]'; }; // Abort if neither regular Google Image Search (udm=2) nor Google Lens mode if (!isImgSearch() && !isLens()) return; showDims(); }; const showDims = () => { const wrapper = document.querySelector(selectors.wrapper); if (!wrapper) return; // Find all results & exclude the "data-sid-result" attribute we set below (sid = ShowImageDimensions) const results = wrapper.querySelectorAll(selectors.item + ':not([data-sid-result])'); if (!results) return; // Loop through all results results.forEach((result) => { try { // Get result ID from jsdata attribute const jsdata = result.getAttribute('jsdata') ?? result.querySelector('[jsdata]')?.getAttribute('jsdata'); const resultId = jsdata?.split(';')[2]; if (!resultId) return; // Access "W_jd" in window object const W_jd = unsafeWindow.W_jd; const rawResultData = W_jd[resultId]; const symbols = Object.getOwnPropertySymbols(rawResultData); // Traverse down to the data we want const level0 = rawResultData[symbols[0]]; if (!level0) return; const level1 = Object.values(level0)[0]?.[1]; if (!level1) return; const level2 = Object.values(level1)[0]?.[3]; if (!level2) return; // Extract full res URL, width and height const [imgurl, height, width] = level2; if (!width || !height || !imgurl) return; // Filter out Meta sites & TikTok which prevent direct full res linking. It's brainrot anyways const hn = (new URL(imgurl)).hostname; const goodUrl = !hn.startsWith('lookaside') && !hn.startsWith('www.tiktok.com'); // Create svg icon for full res lightbox const icon = createSVG('svg', { viewBox: '0 0 24 24', width: '16', height: '16' }, [ createSVG('path', { d: 'M19 21H5l-2-2V5l2-2h7v2H5v14h14v-7h2v7l-2 2z' }), createSVG('path', { d: 'M21 10h-2V5h-5V3h7z' }), createSVG('path', { d: 'M8 14 19 3l2 2-11 11z' }) ]); // Create element for dimensions text const dimensionsElement = document.createElement(goodUrl ? 'a' : 'p'); dimensionsElement.textContent = ''; if (goodUrl) dimensionsElement.append(icon); dimensionsElement.append(`${width} × ${height}`); dimensionsElement.classList.add('sid-dims'); // Create full res link if (goodUrl) { dimensionsElement.href = imgurl; dimensionsElement.onclick = (e) => { if (!isKeyPressed) { e.preventDefault(); showLightbox(imgurl); }; }; }; // Append everything to the thumbnail const thumbnail = isLens() ? result.querySelector('img').closest('[jsdata]').parentElement : result.querySelector('h3'); thumbnail.style.position = 'relative'; thumbnail.append(dimensionsElement); // Add "data-sid-result" attribute to result result.dataset.sidResult = ''; } catch (error) { console.warn('Show Image Dimensions UserScript:', error); }; }); }; // Run script on document ready handleUrlChange(); // Run script when title changes const titleObserver = new MutationObserver(() => { handleUrlChange(); }); // Run script with minimal delay when grid changes const contentObserver = new MutationObserver(() => { setTimeout(() => { showDims() }, 100); }); // Run MutationObservers titleObserver.observe(document.querySelector('title'), { subtree: true, characterData: true, childList: true }); contentObserver.observe(document.querySelector('div#rso'), { childList: true, subtree: true }); // Create lightbox for full res images const lightboxBackdrop = document.createElement('div'); lightboxBackdrop.classList.add('sid-backdrop'); const lightboxWrap = document.createElement('div'); lightboxWrap.classList.add('sid-wrap'); const lightboxImg = document.createElement('img'); lightboxWrap.append(lightboxImg); lightboxBackdrop.append(lightboxWrap); document.body.append(lightboxBackdrop); let isKeyPressed = false; document.addEventListener('keydown', () => isKeyPressed = true); document.addEventListener('keyup', () => isKeyPressed = false); // Show full res image const showLightbox = imgurl => { lightboxBackdrop.classList.add('show'); lightboxImg.src = imgurl; lightboxImg.onload = () => { lightboxWrap.classList.add('show'); }; lightboxBackdrop.onclick = (e) => { hideLightbox(e); }; lightboxImg.onclick = (e) => { window.open(imgurl, '_blank'); }; document.addEventListener('keydown', closeOnEsc); }; // Hide full res image const hideLightbox = () => { lightboxBackdrop.classList.remove('show'); lightboxWrap.classList.remove('show'); lightboxImg.removeAttribute('src'); delete lightboxImg.dataset.ilt; document.removeEventListener('keydown', closeOnEsc); }; const closeOnEsc = (e) => { if (e.key === 'Escape') hideLightbox(e); }; // Helper function to generate small svg icon const createSVG = (type, attrs = {}, children = []) => { const el = document.createElementNS('http://www.w3.org/2000/svg', type); for (const [key, value] of Object.entries(attrs)) { el.setAttribute(key, value); }; if (children.length > 0) el.append(...children); return el; }; // Add CSS function const addStyles = css => { const style = document.createElement('style'); style.textContent = css; document.head.appendChild(style); }; // Styles for dimensions and full res lightbox addStyles(` .sid-dims { display: flex; gap: 3px; position: absolute; bottom: 0; right: 0; margin: 0; padding: 4px; color: #f1f3f4 !important; background-color: rgba(0, 0, 0, 0.6); border-radius: 2px 0 12px 0; font-family: Roboto-Medium, Roboto, Arial, sans-serif; font-size: 10px; line-height: 12px; text-decoration: none !important; } .sid-dims svg { fill: #f1f3f4; height: 12px; width: 12px; opacity: 0.4; pointer-events: none; } .sid-dims:hover svg { opacity: 1; } .sid-backdrop { position: fixed; display: flex; pointer-events: none; opacity: 0; transition: all 0.2s ease-in-out; top: 0; left: 0; z-index: 1010; justify-content: center; align-items: center; } .sid-backdrop.show { pointer-events: all; bottom: 0; right: 0; opacity: 1; background-color: rgba(0, 0, 0, 0.6); } .sid-backdrop.show .sid-wrap { z-index: 1011; border-radius: 12px; cursor: pointer; display: block; position: relative; overflow: hidden; transition: all 0.2s ease-in-out; background-color: rgba(255, 255, 255, 0.33); box-shadow: inset 0px 0px 50px 10px rgba(0, 0, 0, 0.66); } .sid-backdrop.show .sid-wrap img { position: relative; display: block; z-index: 1012; max-width: 95vw; max-height: 95vh; opacity: 0; transition: all 0.2s ease-in-out; } .sid-backdrop.show .sid-wrap.show { background-color: #fff; box-shadow: 0px 0px 100px 20px rgba(0, 0, 0, 0.66); } .sid-backdrop.show .sid-wrap.show img { opacity: 1; } `); })();