Greasy Fork

批量图片提取与下载

提取图片后支持全选、取消全选、一键下载全部图片和下载选中图片为 ZIP 文件,UI 美化版,带链接输入界面。

目前为 2025-01-06 提交的版本。查看 最新版本

// ==UserScript==
// @name         批量图片提取与下载
// @namespace    http://tampermonkey.net/
// @version      1.4.3
// @description  提取图片后支持全选、取消全选、一键下载全部图片和下载选中图片为 ZIP 文件,UI 美化版,带链接输入界面。
// @author       vicwang
// @match        *://*/*
// @grant        none
// @require      https://cdnjs.cloudflare.com/ajax/libs/jszip/3.10.1/jszip.min.js
// @license MIT
// ==/UserScript==

(function () {
    'use strict';

    // 创建“开始”按钮
    const startButton = document.createElement('button');
    startButton.textContent = '开始';
    startButton.style.position = 'fixed';
    startButton.style.top = '10px';
    startButton.style.right = '10px';
    startButton.style.zIndex = '9999';
    startButton.style.padding = '10px 20px';
    startButton.style.backgroundColor = '#007BFF';
    startButton.style.color = '#fff';
    startButton.style.border = 'none';
    startButton.style.borderRadius = '8px';
    startButton.style.cursor = 'pointer';
    startButton.style.boxShadow = '0 4px 6px rgba(0, 0, 0, 0.1)';
    startButton.style.fontSize = '14px';
    startButton.style.fontWeight = '500';
    startButton.style.transition = 'background-color 0.3s ease';
    document.body.appendChild(startButton);

    // 按钮悬停效果
    startButton.addEventListener('mouseenter', () => {
        startButton.style.backgroundColor = '#0056b3';
    });
    startButton.addEventListener('mouseleave', () => {
        startButton.style.backgroundColor = '#007BFF';
    });

    // 点击“开始”按钮
    startButton.addEventListener('click', () => {
        // 创建模态对话框
        const modal = document.createElement('div');
        modal.style.position = 'fixed';
        modal.style.top = '0';
        modal.style.left = '0';
        modal.style.width = '100%';
        modal.style.height = '100%';
        modal.style.backgroundColor = 'rgba(0, 0, 0, 0.8)';
        modal.style.zIndex = '10000';
        modal.style.display = 'flex';
        modal.style.justifyContent = 'center';
        modal.style.alignItems = 'center';
        document.body.appendChild(modal);

        // 创建对话框内容
        const dialog = document.createElement('div');
        dialog.style.backgroundColor = '#fff';
        dialog.style.padding = '20px';
        dialog.style.borderRadius = '10px';
        dialog.style.boxShadow = '0 4px 10px rgba(0, 0, 0, 0.2)';
        dialog.style.width = '400px';
        dialog.style.maxWidth = '90%';
        modal.appendChild(dialog);

        // 创建标题
        const title = document.createElement('h3');
        title.textContent = '请输入图片链接';
        title.style.marginTop = '0';
        title.style.fontSize = '18px';
        title.style.color = '#333';
        dialog.appendChild(title);

        // 创建输入框
        const inputBox = document.createElement('textarea');
        inputBox.placeholder = '每行一个链接';
        inputBox.style.width = '100%';
        inputBox.style.height = '150px';
        inputBox.style.padding = '10px';
        inputBox.style.border = '1px solid #ccc';
        inputBox.style.borderRadius = '5px';
        inputBox.style.fontSize = '14px';
        inputBox.style.marginBottom = '15px';
        inputBox.style.boxSizing = 'border-box';
        dialog.appendChild(inputBox);

        // 创建按钮容器
        const buttonContainer = document.createElement('div');
        buttonContainer.style.display = 'flex';
        buttonContainer.style.justifyContent = 'flex-end';
        buttonContainer.style.gap = '10px';
        dialog.appendChild(buttonContainer);

        // 创建确认按钮
        const confirmButton = document.createElement('button');
        confirmButton.textContent = '确认';
        confirmButton.style.padding = '8px 16px';
        confirmButton.style.backgroundColor = '#28A745';
        confirmButton.style.color = '#fff';
        confirmButton.style.border = 'none';
        confirmButton.style.borderRadius = '5px';
        confirmButton.style.cursor = 'pointer';
        confirmButton.style.fontSize = '14px';
        confirmButton.style.transition = 'background-color 0.3s ease';
        buttonContainer.appendChild(confirmButton);

        // 确认按钮悬停效果
        confirmButton.addEventListener('mouseenter', () => {
            confirmButton.style.backgroundColor = '#218838';
        });
        confirmButton.addEventListener('mouseleave', () => {
            confirmButton.style.backgroundColor = '#28A745';
        });

        // 创建取消按钮
        const cancelButton = document.createElement('button');
        cancelButton.textContent = '取消';
        cancelButton.style.padding = '8px 16px';
        cancelButton.style.backgroundColor = '#FF4C4C';
        cancelButton.style.color = '#fff';
        cancelButton.style.border = 'none';
        cancelButton.style.borderRadius = '5px';
        cancelButton.style.cursor = 'pointer';
        cancelButton.style.fontSize = '14px';
        cancelButton.style.transition = 'background-color 0.3s ease';
        buttonContainer.appendChild(cancelButton);

        // 取消按钮悬停效果
        cancelButton.addEventListener('mouseenter', () => {
            cancelButton.style.backgroundColor = '#cc0000';
        });
        cancelButton.addEventListener('mouseleave', () => {
            cancelButton.style.backgroundColor = '#FF4C4C';
        });

        // 确认按钮点击事件
        confirmButton.addEventListener('click', () => {
            const links = inputBox.value.split('\n').map(link => link.trim()).filter(link => link);
            if (links.length === 0) {
                alert('请输入至少一个有效链接');
                return;
            }
            modal.remove(); // 关闭对话框
            processLinks(links); // 处理链接
        });

        // 取消按钮点击事件
        cancelButton.addEventListener('click', () => {
            modal.remove(); // 关闭对话框
        });
    });

    // 处理链接
    function processLinks(links) {
        // 创建图片展示容器
        const container = document.createElement('div');
        container.style.position = 'fixed';
        container.style.top = '0';
        container.style.left = '0';
        container.style.width = '100%';
        container.style.height = '100%';
        container.style.backgroundColor = 'rgba(0, 0, 0, 0.95)';
        container.style.overflowY = 'scroll';
        container.style.zIndex = '10000';
        container.style.display = 'flex';
        container.style.flexWrap = 'wrap';
        container.style.gap = '15px';
        container.style.padding = '20px';
        container.style.boxSizing = 'border-box';
        document.body.appendChild(container);

        // 创建关闭按钮
        const closeButton = document.createElement('button');
        closeButton.textContent = '关闭';
        closeButton.style.position = 'fixed';
        closeButton.style.top = '20px';
        closeButton.style.right = '20px';
        closeButton.style.zIndex = '10001';
        closeButton.style.padding = '10px 20px';
        closeButton.style.backgroundColor = '#FF4C4C';
        closeButton.style.color = '#fff';
        closeButton.style.border = 'none';
        closeButton.style.borderRadius = '8px';
        closeButton.style.cursor = 'pointer';
        closeButton.style.boxShadow = '0 4px 6px rgba(0, 0, 0, 0.1)';
        closeButton.style.fontSize = '14px';
        closeButton.style.fontWeight = '500';
        closeButton.style.transition = 'background-color 0.3s ease';
        container.appendChild(closeButton);

        // 关闭按钮悬停效果
        closeButton.addEventListener('mouseenter', () => {
            closeButton.style.backgroundColor = '#cc0000';
        });
        closeButton.addEventListener('mouseleave', () => {
            closeButton.style.backgroundColor = '#FF4C4C';
        });

        closeButton.addEventListener('click', () => {
            container.remove();
        });

        // 图片列表
        const imageList = [];
        const selectedImages = new Set(); // 记录选中的图片

        // 检查链接是否为图片
        const isImageLink = (url) => {
            return /\.(jpg|jpeg|png|gif|bmp|webp|svg)$/i.test(url);
        };

        // 提取每个链接中的图片
        const promises = links.map(link => {
            if (isImageLink(link)) {
                imageList.push(link);
                return Promise.resolve();
            } else {
                return fetch(link)
                    .then(response => response.text())
                    .then(html => {
                        const tempDiv = document.createElement('div');
                        tempDiv.innerHTML = html;
                        const images = Array.from(tempDiv.querySelectorAll('img')).map(img => img.src);
                        imageList.push(...images);
                    })
                    .catch(() => console.error(`无法加载链接:${link}`));
            }
        });

        // 等待所有提取完成
        Promise.all(promises).then(() => {
            if (imageList.length === 0) {
                alert('未找到任何图片');
                return;
            }

            // 显示所有图片
            const imageElements = [];
            imageList.forEach((src, index) => {
                const imgWrapper = document.createElement('div');
                imgWrapper.style.flex = '1 1 calc(25% - 15px)';
                imgWrapper.style.display = 'flex';
                imgWrapper.style.alignItems = 'center';
                imgWrapper.style.justifyContent = 'center';
                imgWrapper.style.backgroundColor = '#fff';
                imgWrapper.style.borderRadius = '8px';
                imgWrapper.style.overflow = 'hidden';
                imgWrapper.style.position = 'relative';
                imgWrapper.style.cursor = 'pointer';
                imgWrapper.style.transition = 'transform 0.2s ease, box-shadow 0.2s ease';

                // 图片悬停效果
                imgWrapper.addEventListener('mouseenter', () => {
                    imgWrapper.style.transform = 'scale(1.02)';
                    imgWrapper.style.boxShadow = '0 4px 10px rgba(0, 0, 0, 0.2)';
                });
                imgWrapper.addEventListener('mouseleave', () => {
                    imgWrapper.style.transform = 'scale(1)';
                    imgWrapper.style.boxShadow = 'none';
                });

                const img = document.createElement('img');
                img.src = src;
                img.style.width = '100%';
                img.style.height = 'auto';
                img.style.borderRadius = '8px';
                img.alt = '图片';

                // 选中效果
                const overlay = document.createElement('div');
                overlay.style.position = 'absolute';
                overlay.style.top = '0';
                overlay.style.left = '0';
                overlay.style.width = '100%';
                overlay.style.height = '100%';
                overlay.style.backgroundColor = 'rgba(0, 123, 255, 0.5)';
                overlay.style.display = 'none';
                overlay.style.borderRadius = '8px';
                imgWrapper.appendChild(overlay);

                imgWrapper.appendChild(img);
                container.appendChild(imgWrapper);
                imageElements.push({ src, wrapper: imgWrapper, overlay });

                // 点击图片进行选中/取消
                imgWrapper.addEventListener('click', () => {
                    if (selectedImages.has(src)) {
                        selectedImages.delete(src);
                        overlay.style.display = 'none';
                    } else {
                        selectedImages.add(src);
                        overlay.style.display = 'block';
                    }
                });
            });

            // 全选按钮
            const selectAllButton = document.createElement('button');
            selectAllButton.textContent = '全选';
            selectAllButton.style.position = 'fixed';
            selectAllButton.style.bottom = '100px';
            selectAllButton.style.right = '20px';
            selectAllButton.style.zIndex = '10001';
            selectAllButton.style.padding = '10px 20px';
            selectAllButton.style.backgroundColor = '#007BFF';
            selectAllButton.style.color = '#fff';
            selectAllButton.style.border = 'none';
            selectAllButton.style.borderRadius = '8px';
            selectAllButton.style.cursor = 'pointer';
            selectAllButton.style.boxShadow = '0 4px 6px rgba(0, 0, 0, 0.1)';
            selectAllButton.style.fontSize = '14px';
            selectAllButton.style.fontWeight = '500';
            selectAllButton.style.transition = 'background-color 0.3s ease';
            container.appendChild(selectAllButton);

            // 全选按钮悬停效果
            selectAllButton.addEventListener('mouseenter', () => {
                selectAllButton.style.backgroundColor = '#0056b3';
            });
            selectAllButton.addEventListener('mouseleave', () => {
                selectAllButton.style.backgroundColor = '#007BFF';
            });

            selectAllButton.addEventListener('click', () => {
                imageElements.forEach(({ src, overlay }) => {
                    selectedImages.add(src);
                    overlay.style.display = 'block';
                });
            });

            // 取消全选按钮
            const deselectAllButton = document.createElement('button');
            deselectAllButton.textContent = '取消全选';
            deselectAllButton.style.position = 'fixed';
            deselectAllButton.style.bottom = '60px';
            deselectAllButton.style.right = '20px';
            deselectAllButton.style.zIndex = '10001';
            deselectAllButton.style.padding = '10px 20px';
            deselectAllButton.style.backgroundColor = '#FF5722';
            deselectAllButton.style.color = '#fff';
            deselectAllButton.style.border = 'none';
            deselectAllButton.style.borderRadius = '8px';
            deselectAllButton.style.cursor = 'pointer';
            deselectAllButton.style.boxShadow = '0 4px 6px rgba(0, 0, 0, 0.1)';
            deselectAllButton.style.fontSize = '14px';
            deselectAllButton.style.fontWeight = '500';
            deselectAllButton.style.transition = 'background-color 0.3s ease';
            container.appendChild(deselectAllButton);

            // 取消全选按钮悬停效果
            deselectAllButton.addEventListener('mouseenter', () => {
                deselectAllButton.style.backgroundColor = '#e64a19';
            });
            deselectAllButton.addEventListener('mouseleave', () => {
                deselectAllButton.style.backgroundColor = '#FF5722';
            });

            deselectAllButton.addEventListener('click', () => {
                imageElements.forEach(({ src, overlay }) => {
                    selectedImages.delete(src);
                    overlay.style.display = 'none';
                });
            });

            // 下载选中图片按钮
            const downloadSelectedButton = document.createElement('button');
            downloadSelectedButton.textContent = '下载选中图片(ZIP)';
            downloadSelectedButton.style.position = 'fixed';
            downloadSelectedButton.style.bottom = '20px';
            downloadSelectedButton.style.right = '20px';
            downloadSelectedButton.style.zIndex = '10001';
            downloadSelectedButton.style.padding = '10px 20px';
            downloadSelectedButton.style.backgroundColor = '#28A745';
            downloadSelectedButton.style.color = '#fff';
            downloadSelectedButton.style.border = 'none';
            downloadSelectedButton.style.borderRadius = '8px';
            downloadSelectedButton.style.cursor = 'pointer';
            downloadSelectedButton.style.boxShadow = '0 4px 6px rgba(0, 0, 0, 0.1)';
            downloadSelectedButton.style.fontSize = '14px';
            downloadSelectedButton.style.fontWeight = '500';
            downloadSelectedButton.style.transition = 'background-color 0.3s ease';
            container.appendChild(downloadSelectedButton);

            // 下载按钮悬停效果
            downloadSelectedButton.addEventListener('mouseenter', () => {
                downloadSelectedButton.style.backgroundColor = '#218838';
            });
            downloadSelectedButton.addEventListener('mouseleave', () => {
                downloadSelectedButton.style.backgroundColor = '#28A745';
            });

            downloadSelectedButton.addEventListener('click', async () => {
                if (selectedImages.size === 0) {
                    alert('请先选择图片!');
                    return;
                }

                const zip = new JSZip();
                const selectedArray = Array.from(selectedImages);

                for (let i = 0; i < selectedArray.length; i++) {
                    const src = selectedArray[i];
                    const filename = `image_${i + 1}.${src.split('.').pop().split('?')[0]}`;
                    const response = await fetch(src);
                    const blob = await response.blob();
                    zip.file(filename, blob);
                }

                const content = await zip.generateAsync({ type: 'blob' });
                const link = document.createElement('a');
                link.href = URL.createObjectURL(content);
                link.download = 'selected_images.zip';
                link.click();
            });
        });
    }
})();