ImageSnatcher

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

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey, Greasemonkey или Violentmonkey.

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey или Violentmonkey.

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey или Violentmonkey.

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey или Userscripts.

За да инсталирате скрипта, трябва да инсталирате разширение като Tampermonkey.

За да инсталирате този скрипт, трябва да имате инсталиран скриптов мениджър.

(Вече имам скриптов мениджър, искам да го инсталирам!)

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

(Вече имам инсталиран мениджър на стиловете, искам да го инсталирам!)

// ==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(); 
        }
    });
})();