ImageSnatcher

Quickly download images by hovering over them and pressing the S key.

Dovrai installare un'estensione come Tampermonkey, Greasemonkey o Violentmonkey per installare questo script.

Dovrai installare un'estensione come Tampermonkey o Violentmonkey per installare questo script.

Dovrai installare un'estensione come Tampermonkey o Violentmonkey per installare questo script.

Dovrai installare un'estensione come Tampermonkey o Userscripts per installare questo script.

Dovrai installare un'estensione come ad esempio Tampermonkey per installare questo script.

Dovrai installare un gestore di script utente per installare questo script.

(Ho già un gestore di script utente, lasciamelo installare!)

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

(Ho già un gestore di stile utente, lasciamelo installare!)

// ==UserScript==
// @name         ImageSnatcher
// @name:es      ImageSnatcher
// @version      1.2.4
// @description  Quickly download images by hovering over them and pressing the S key.
// @description:es Descarga imágenes rápidamente pasando el cursor sobre ellas y presionando la tecla S.
// @author       Adam Jensen
// @match        *://*/*
// @grant        GM_download
// @license      MIT
// @namespace https://greasyfork.org/en/scripts/518034-imagesnatcher
// ==/UserScript==

(function () {
    'use strict';

    const validExtensions = /\.(jpe?g|png|gif|webp|bmp|svg|ico|tiff?|avif|jxl|heic|heif|dds|apng|pjpeg|pjpg|webm)$/i;
    const preprocessedImages = []; // Array to store the last 10 preprocessed images
    const maxHistory = 10; // Maximum number of images to keep in the history
    const downloadQueue = []; // Queue of images to be downloaded
    let isDownloading = false; // Flag to track the download state
    let lastHoveredImage = null; // Variable to store the last hovered image details

    let lastProcessedUrl = null; 
    function preprocessImage(target) {
        if (!target) return;

        let imageUrl;
        let imageTitle = '';
        let potentialBetterUrl = null;

        const parentAnchor = target.closest('a');
        if (parentAnchor && parentAnchor.href && validExtensions.test(parentAnchor.href)) {
            potentialBetterUrl = parentAnchor.href;
        }

        if (target.tagName.toLowerCase() === 'img') {
            imageUrl = target.src;
            imageTitle = target.alt.trim() || '';
        } else if (target.style.backgroundImage) {
            imageUrl = target.style.backgroundImage.replace(/url\(["']?([^"']+)["']?\)/, '$1');
        }

        if (potentialBetterUrl) {
            imageUrl = potentialBetterUrl;
        }

        if (imageUrl === lastProcessedUrl) {
            return; 
        }
        lastProcessedUrl = imageUrl;

        const ytMatch = imageUrl.match(/\/vi\/([a-zA-Z0-9_-]{11})\//);
        if (ytMatch) {
            imageUrl = `https://i.ytimg.com/vi/${ytMatch[1]}/maxresdefault.jpg`;
            console.log(`YouTube thumbnail detected. Switched to maxresdefault: ${imageUrl}`);
        }

        if (!imageTitle) {
            const parentAnchor = target.closest('a');
            if (parentAnchor && parentAnchor.href) {
                imageTitle = parentAnchor.href.split('/').pop().split('?')[0];
            }
        }

        if (!imageTitle) {
            imageTitle = imageUrl ? imageUrl.split('/').pop().split('?')[0] : 'unknown_image';
        }

        imageTitle = imageTitle.replace(/[\/:*?"<>|]/g, '_'); // Make filename safe

        if (!validExtensions.test(imageTitle)) {
            const extensionMatch = imageUrl ? imageUrl.match(validExtensions) : null;
            const extension = extensionMatch ? extensionMatch[0] : '.jpg'; // Default to .jpg
            imageTitle += extension;
        }

        const existingImage = preprocessedImages.find(img => img.url === imageUrl);
        if (!existingImage) {
            preprocessedImages.push({ url: imageUrl, title: imageTitle });
            if (preprocessedImages.length > maxHistory) {
                preprocessedImages.shift(); 
            }
            console.log(`Preprocessed image: URL=${imageUrl}, Title=${imageTitle}`);
        }

        lastHoveredImage = { url: imageUrl, title: imageTitle };
    }

    document.addEventListener('mousemove', function (event) {
        if (event.target.tagName.toLowerCase() === 'img' || event.target.style.backgroundImage) {
            preprocessImage(event.target); 
        }
    });

    function setCursorLoading(isLoading) {
        if (isLoading) {
            document.body.style.cursor = 'progress'; 
        } else {
            document.body.style.cursor = ''; 
        }
    }

    function processDownloadQueue() {
        if (isDownloading || downloadQueue.length === 0) return; 

        const nextImage = downloadQueue.shift();
        isDownloading = true;
        setCursorLoading(true); 

        GM_download({
            url: nextImage.url,
            name: nextImage.title,
            onerror: function (err) {
                console.error('Failed to download the image:', err);
                isDownloading = false; 
                setCursorLoading(false); 
                processDownloadQueue(); 
            },
            onload: function () {
                console.log(`Downloaded image: ${nextImage.title}`);
                isDownloading = false; 
                setCursorLoading(false); 
                processDownloadQueue(); 
            }
        });
    }


    document.addEventListener('keydown', function (event) {
        // Ignore the keypress if the target is an input or textarea
        const activeElement = document.activeElement;
        const isInputField = activeElement.tagName.toLowerCase() === 'input' ||
                             activeElement.tagName.toLowerCase() === 'textarea' ||
                             activeElement.isContentEditable;

        if (isInputField) return; // Do not download if the user is typing in an input field

        if (event.key.toLowerCase() === 's' && lastHoveredImage) {
            downloadQueue.push(lastHoveredImage);
            console.log(`Added image to queue: ${lastHoveredImage.title}`);
            processDownloadQueue(); 
        }
    });
})();