ImageSnatcher

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

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴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(); 
        }
    });
})();