Greasy Fork

Greasy Fork is available in English.

图片下载助手

支持图片预览和多图批量下载,置顶批量下载按钮

// ==UserScript==
// @name         图片下载助手
// @namespace    http://tampermonkey.net/
// @version      alpha 1.0
// @description  支持图片预览和多图批量下载,置顶批量下载按钮
// @author       Songlll
// @match        https://www.zqy.com/resource/*
// @grant        GM_download
// @grant        GM_xmlhttpRequest
// @license MIT
// @run-at       document-end
// ==/UserScript==

(function() {
    'use strict';

    // 免责声明内容
    const DISCLAIMER = `
        <strong>免责声明:</strong>
        <p>本脚本仅用于个人学习研究,请尊重网站版权。下载内容仅限个人使用,禁止传播或用于商业用途。</p>
        <p>使用本脚本产生的任何法律责任由用户自行承担。请支持正版资源,尊重创作者劳动成果。</p>
    `;

    // 添加样式
    const style = document.createElement('style');
    style.innerHTML = `
        /* 整体布局 */
        .dh-container {
            position: fixed;
            bottom: 20px;
            right: 100px;
            z-index: 10000;
            display: flex;
            flex-direction: column;
            align-items: flex-end;
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
        }

        /* 浮动按钮 */
        .dh-toggle {
            width: 60px;
            height: 60px;
            border-radius: 50%;
            background: linear-gradient(135deg, #6a11cb 0%, #2575fc 100%);
            color: white;
            display: flex;
            align-items: center;
            justify-content: center;
            font-size: 24px;
            cursor: pointer;
            box-shadow: 0 6px 18px rgba(0, 0, 0, 0.2);
            transition: all 0.3s ease;
            border: none;
        }

        .dh-toggle:hover {
            transform: scale(1.1) rotate(90deg);
            box-shadow: 0 8px 25px rgba(74, 108, 247, 0.6);
        }

        /* 控制面板 */
        .dh-panel {
            width: 360px;
            background: white;
            border-radius: 16px;
            box-shadow: 0 12px 40px rgba(0, 0, 0, 0.15);
            overflow: hidden;
            margin-bottom: 15px;
            display: none;
            max-height: 80vh;
        }

        .dh-panel.active {
            display: block;
        }

        /* 面板头部 */
        .dh-header {
            background: linear-gradient(135deg, #6a11cb 0%, #2575fc 100%);
            color: white;
            padding: 15px 20px;
            display: flex;
            justify-content: space-between;
            align-items: center;
            position: sticky;
            top: 0;
            z-index: 10;
        }

        .dh-title {
            font-size: 18px;
            font-weight: 600;
        }

        .dh-close {
            background: rgba(255, 255, 255, 0.2);
            border: none;
            width: 32px;
            height: 32px;
            border-radius: 50%;
            color: white;
            cursor: pointer;
            display: flex;
            align-items: center;
            justify-content: center;
            transition: all 0.2s ease;
            font-size: 18px;
        }

        .dh-close:hover {
            background: rgba(255, 255, 255, 0.3);
            transform: rotate(90deg);
        }

        /* 面板内容 */
        .dh-content {
            padding: 20px;
            max-height: calc(80vh - 150px);
            overflow-y: auto;
        }

        /* 免责声明 */
        .dh-disclaimer {
            background: #fff8e1;
            border-left: 4px solid #ffc107;
            padding: 12px 15px;
            margin-bottom: 20px;
            border-radius: 4px;
            font-size: 13px;
            line-height: 1.5;
        }

        /* 统计信息 */
        .dh-stats {
            display: flex;
            justify-content: space-between;
            margin-bottom: 15px;
            background: #f8fafc;
            padding: 15px;
            border-radius: 12px;
        }

        .dh-stat-item {
            text-align: center;
        }

        .dh-stat-value {
            font-size: 24px;
            font-weight: 700;
            color: #4a6cf7;
            line-height: 1;
        }

        .dh-stat-label {
            color: #64748b;
            font-size: 14px;
        }

        /* 操作按钮 */
        .dh-action-buttons {
            display: grid;
            grid-template-columns: 1fr;
            gap: 12px;
            margin-bottom: 20px;
        }

        .dh-action-btn {
            padding: 14px;
            border-radius: 12px;
            border: none;
            font-weight: 600;
            cursor: pointer;
            display: flex;
            align-items: center;
            justify-content: center;
            gap: 10px;
            transition: all 0.2s ease;
            font-size: 16px;
        }

        .dh-download-all {
            background: linear-gradient(to right, #00c9a7, #00b09b);
            color: white;
            position: sticky;
            top: 60px;
            z-index: 5;
        }

        .dh-download-all:hover {
            transform: translateY(-3px);
            box-shadow: 0 6px 15px rgba(0, 201, 167, 0.4);
        }

        /* 单张图片下载按钮 */
        .dh-single-download {
            background: #4A90E2;
            color: white;
            padding: 6px 12px;
            border-radius: 6px;
            border: none;
            cursor: pointer;
            display: inline-flex;
            align-items: center;
            gap: 5px;
            font-size: 13px;
            margin-left: 10px;
            transition: all 0.2s;
        }

        .dh-single-download:hover {
            background: #3a7bc8;
        }

        /* 进度条 */
        .dh-progress-container {
            background: #f1f5f9;
            border-radius: 12px;
            overflow: hidden;
            margin-bottom: 15px;
        }

        .dh-progress-header {
            display: flex;
            justify-content: space-between;
            padding: 10px 15px;
            font-size: 14px;
            color: #64748b;
        }

        .dh-progress-bar {
            height: 8px;
            background: #e2e8f0;
            position: relative;
        }

        .dh-progress-fill {
            height: 100%;
            background: linear-gradient(to right, #ff9a9e, #fad0c4);
            width: 0%;
            transition: width 0.5s ease;
            border-radius: 0 4px 4px 0;
        }

        /* 当前文件信息 */
        .dh-current-file {
            padding: 15px;
            background: #f8fafc;
            border-radius: 12px;
            font-size: 14px;
            margin-top: 15px;
            display: none;
        }

        .dh-current-file.active {
            display: block;
        }

        .dh-filename {
            font-weight: 600;
            color: #4a6cf7;
            white-space: nowrap;
            overflow: hidden;
            text-overflow: ellipsis;
            margin-bottom: 5px;
        }

        /* 状态文本 */
        .dh-status {
            font-size: 14px;
            color: #4a5568;
            text-align: center;
            margin: 10px 0;
            font-weight: 500;
        }

        /* 图片预览区域 */
        .dh-preview-container {
            margin-top: 20px;
            border-top: 1px solid #eee;
            padding-top: 15px;
        }

        .dh-preview-title {
            font-weight: 600;
            margin-bottom: 10px;
            color: #4a5568;
            display: flex;
            align-items: center;
            justify-content: space-between;
        }

        .dh-preview-grid {
            display: grid;
            grid-template-columns: repeat(3, 1fr);
            gap: 10px;
        }

        .dh-preview-item {
            border-radius: 8px;
            overflow: hidden;
            cursor: pointer;
            position: relative;
            aspect-ratio: 4/3;
            background: #f5f7fa;
            border: 1px solid #e2e8f0;
            transition: all 0.2s ease;
        }

        .dh-preview-item:hover {
            transform: translateY(-3px);
            box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
            border-color: #4a6cf7;
        }

        .dh-preview-item img {
            width: 100%;
            height: 100%;
            object-fit: cover;
            display: block;
        }

        .dh-preview-number {
            position: absolute;
            top: 5px;
            right: 5px;
            background: rgba(0, 0, 0, 0.6);
            color: white;
            width: 22px;
            height: 22px;
            border-radius: 50%;
            display: flex;
            align-items: center;
            justify-content: center;
            font-size: 12px;
        }

        /* 预览弹窗 */
        .dh-preview-modal {
            position: fixed;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background: rgba(0, 0, 0, 0.9);
            z-index: 20000;
            display: flex;
            align-items: center;
            justify-content: center;
            opacity: 0;
            pointer-events: none;
            transition: opacity 0.3s ease;
        }

        .dh-preview-modal.active {
            opacity: 1;
            pointer-events: all;
        }

        .dh-preview-content {
            position: relative;
            max-width: 90%;
            max-height: 90%;
            text-align: center;
        }

        .dh-preview-content img {
            max-width: 100%;
            max-height: 80vh;
            border-radius: 8px;
            box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3);
        }

        .dh-preview-nav {
            position: absolute;
            top: 50%;
            transform: translateY(-50%);
            width: 50px;
            height: 50px;
            background: rgba(255, 255, 255, 0.2);
            border-radius: 50%;
            display: flex;
            align-items: center;
            justify-content: center;
            color: white;
            font-size: 24px;
            cursor: pointer;
            transition: all 0.3s ease;
        }

        .dh-preview-nav:hover {
            background: rgba(255, 255, 255, 0.3);
        }

        .dh-preview-prev {
            left: 20px;
        }

        .dh-preview-next {
            right: 20px;
        }

        .dh-preview-close {
            position: absolute;
            top: 20px;
            right: 20px;
            background: rgba(255, 255, 255, 0.2);
            width: 40px;
            height: 40px;
            border-radius: 50%;
            display: flex;
            align-items: center;
            justify-content: center;
            color: white;
            font-size: 24px;
            cursor: pointer;
            transition: all 0.3s ease;
        }

        .dh-preview-close:hover {
            background: rgba(255, 255, 255, 0.3);
            transform: rotate(90deg);
        }

        .dh-preview-info {
            color: white;
            margin-top: 15px;
            font-size: 16px;
        }

        /* VIP提示 */
        .vip-protection-notice {
            position: fixed;
            top: 10px;
            right: 10px;
            background-color: #4CAF50;
            color: white;
            padding: 8px 15px;
            border-radius: 4px;
            z-index: 9999;
            box-shadow: 0 2px 10px rgba(0,0,0,0.2);
            animation: fadeInOut 5s forwards;
        }

        @keyframes fadeInOut {
            0% { opacity: 0; transform: translateY(-20px); }
            10% { opacity: 1; transform: translateY(0); }
            90% { opacity: 1; transform: translateY(0); }
            100% { opacity: 0; transform: translateY(-20px); }
        }

        /* 网站原有样式覆盖 */
        .cont_one {
            position: relative;
            margin-bottom: 25px;
            padding: 15px;
            background: white;
            border-radius: 12px;
            box-shadow: 0 4px 8px rgba(0,0,0,0.08);
            transition: transform 0.3s ease;
        }

        .cont_one:hover {
            transform: translateY(-5px);
            box-shadow: 0 6px 12px rgba(0,0,0,0.1);
        }

        .cont_one p {
            display: flex;
            align-items: center;
            margin-top: 10px;
        }

        .cont_one em {
            font-weight: bold;
            color: #6a11cb;
        }
    `;
    document.head.appendChild(style);

    // 创建浮动控制面板容器
    const container = document.createElement('div');
    container.className = 'dh-container';
    document.body.appendChild(container);

    // 创建浮动按钮
    const toggleBtn = document.createElement('button');
    toggleBtn.className = 'dh-toggle';
    toggleBtn.innerHTML = '⇩';
    container.appendChild(toggleBtn);

    // 创建控制面板
    const panel = document.createElement('div');
    panel.className = 'dh-panel';
    container.appendChild(panel);

    // 面板头部
    const header = document.createElement('div');
    header.className = 'dh-header';
    header.innerHTML = `
        <div class="dh-title">图片下载助手</div>
        <button class="dh-close">×</button>
    `;
    panel.appendChild(header);

    // 面板内容
    const content = document.createElement('div');
    content.className = 'dh-content';
    content.innerHTML = `
        <div class="dh-disclaimer">${DISCLAIMER}</div>
        <div class="dh-stats">
            <div class="dh-stat-item">
                <div class="dh-stat-value" id="dh-image-count">0</div>
                <div class="dh-stat-label">图片数量</div>
            </div>
            <div class="dh-stat-item">
                <div class="dh-stat-value" id="dh-vip-status">VIP</div>
                <div class="dh-stat-label">账户状态</div>
            </div>
        </div>
        <div class="dh-action-buttons">
            <button class="dh-action-btn dh-download-all" id="dh-download-all">
                <svg width="16" height="16" viewBox="0 0 24 24" fill="white">
                    <path d="M19 9h-4V3H9v6H5l7 7 7-7zM5 18v2h14v-2H5z"/>
                </svg>
                批量下载所有图片
            </button>
        </div>
        <div class="dh-preview-container">
            <div class="dh-preview-title">
                <span>图片预览</span>
                <small>(点击预览大图)</small>
            </div>
            <div class="dh-preview-grid" id="dh-preview-grid"></div>
        </div>
        <div class="dh-progress-container">
            <div class="dh-progress-header">
                <span>下载进度</span>
                <span id="dh-progress-percent">0%</span>
            </div>
            <div class="dh-progress-bar">
                <div class="dh-progress-fill" id="dh-progress-fill"></div>
            </div>
        </div>
        <div class="dh-status" id="dh-status">就绪,等待操作</div>
        <div class="dh-current-file" id="dh-current-file">
            <div class="dh-filename" id="dh-filename">当前下载文件</div>
            <div id="dh-fileinfo">等待开始...</div>
        </div>
    `;
    panel.appendChild(content);

    // 创建预览弹窗
    const previewModal = document.createElement('div');
    previewModal.className = 'dh-preview-modal';
    previewModal.innerHTML = `
        <div class="dh-preview-close">×</div>
        <div class="dh-preview-nav dh-preview-prev">❮</div>
        <div class="dh-preview-content">
            <img id="dh-preview-large" src="" alt="预览图">
            <div class="dh-preview-info" id="dh-preview-info"></div>
        </div>
        <div class="dh-preview-nav dh-preview-next">❯</div>
    `;
    document.body.appendChild(previewModal);

    // 获取DOM元素
    const closeBtn = header.querySelector('.dh-close');
    const downloadAllBtn = document.getElementById('dh-download-all');
    const progressFill = document.getElementById('dh-progress-fill');
    const progressPercent = document.getElementById('dh-progress-percent');
    const statusText = document.getElementById('dh-status');
    const currentFile = document.getElementById('dh-current-file');
    const filename = document.getElementById('dh-filename');
    const fileInfo = document.getElementById('dh-fileinfo');
    const imageCount = document.getElementById('dh-image-count');
    const vipStatus = document.getElementById('dh-vip-status');
    const previewGrid = document.getElementById('dh-preview-grid');
    const previewLarge = document.getElementById('dh-preview-large');
    const previewInfo = document.getElementById('dh-preview-info');
    const modalClose = previewModal.querySelector('.dh-preview-close');
    const modalPrev = previewModal.querySelector('.dh-preview-prev');
    const modalNext = previewModal.querySelector('.dh-preview-next');

    // 按钮事件
    toggleBtn.addEventListener('click', () => {
        panel.classList.toggle('active');
    });

    closeBtn.addEventListener('click', () => {
        panel.classList.remove('active');
    });

    modalClose.addEventListener('click', () => {
        previewModal.classList.remove('active');
    });

    // 修复下载功能
    function downloadImage(imgUrl) {
        // 从URL中提取文件名
        const urlParts = imgUrl.split('/');
        let filename = urlParts[urlParts.length - 1];

        // 确保文件名有效
        if (!filename || !filename.includes('.')) {
            filename = `image_${Date.now()}.jpg`;
        }

        // 使用GM_download API确保下载行为
        if (typeof GM_download !== 'undefined') {
            GM_download({
                url: imgUrl,
                name: filename,
                onload: function() {
                    console.log('图片下载成功:', filename);
                },
                onerror: function(e) {
                    console.error('下载失败:', e);
                    fallbackDownload(imgUrl, filename);
                }
            });
        } else {
            // 回退方案
            fallbackDownload(imgUrl, filename);
        }

        return filename;
    }

    // 回退下载方法
    function fallbackDownload(imgUrl, filename) {
        // 创建隐藏的下载链接
        const a = document.createElement('a');
        a.href = imgUrl;
        a.download = filename;
        a.style.display = 'none';
        document.body.appendChild(a);
        a.click();
        document.body.removeChild(a);
    }

    // 更新进度条
    function updateProgress(current, total) {
        const percent = Math.round((current / total) * 100);
        progressFill.style.width = `${percent}%`;
        progressPercent.textContent = `${percent}%`;
        statusText.textContent = `下载中: ${current}/${total}`;
        imageCount.textContent = total;
    }

    // 批量下载所有图片
    downloadAllBtn.addEventListener('click', function() {
        const images = document.querySelectorAll('.cont_one img.image_1');
        const total = images.length;

        if (total === 0) {
            statusText.textContent = "未找到图片!";
            return;
        }

        // 禁用按钮
        downloadAllBtn.disabled = true;
        downloadAllBtn.innerHTML = `
            <svg width="16" height="16" viewBox="0 0 24 24" fill="white">
                <path d="M12 4V1L8 5l4 4V6c3.31 0 6 2.69 6 6 0 1.01-.25 1.97-.7 2.8l1.46 1.46A7.93 7.93 0 0020 12c0-4.42-3.58-8-8-8zm0 14c-3.31 0-6-2.69-6-6 0-1.01.25-1.97.7-2.8L5.24 7.74A7.93 7.93 0 004 12c0 4.42 3.58 8 8 8v3l4-4-4-4v3z"/>
            </svg>
            下载中...
        `;
        currentFile.classList.add('active');

        let downloaded = 0;

        const downloadNext = () => {
            if (downloaded >= total) {
                statusText.textContent = "下载完成!";
                downloadAllBtn.disabled = false;
                downloadAllBtn.innerHTML = `
                    <svg width="16" height="16" viewBox="0 0 24 24" fill="white">
                        <path d="M19 9h-4V3H9v6H5l7 7 7-7zM5 18v2h14v-2H5z"/>
                    </svg>
                    批量下载所有图片
                `;
                currentFile.classList.remove('active');
                return;
            }

            const img = images[downloaded];
            const imgUrl = img.src;

            // 显示当前下载文件信息
            filename.textContent = `文件 ${downloaded + 1}/${total}`;
            fileInfo.textContent = `正在下载: ${imgUrl.split('/').pop().substring(0, 20)}...`;

            // 下载图片
            const filenameDownloaded = downloadImage(imgUrl);

            downloaded++;
            updateProgress(downloaded, total);

            // 间隔1.5秒下载下一张(避免浏览器限制)
            setTimeout(downloadNext, 1500);
        };

        downloadNext();
    });

    // 图片预览功能
    function createPreviewItems() {
        previewGrid.innerHTML = '';
        const images = document.querySelectorAll('.cont_one img.image_1');

        images.forEach((img, index) => {
            const previewItem = document.createElement('div');
            previewItem.className = 'dh-preview-item';
            previewItem.innerHTML = `
                <img src="${img.src}" alt="预览图 ${index + 1}">
                <div class="dh-preview-number">${index + 1}</div>
            `;

            previewItem.addEventListener('click', () => {
                previewLarge.src = img.src;
                previewInfo.textContent = `图片 ${index + 1}/${images.length} - ${img.src.split('/').pop()}`;
                previewModal.classList.add('active');
                currentPreviewIndex = index;
            });

            previewGrid.appendChild(previewItem);
        });
    }

    // 预览导航
    let currentPreviewIndex = 0;

    modalNext.addEventListener('click', () => {
        const images = document.querySelectorAll('.cont_one img.image_1');
        currentPreviewIndex = (currentPreviewIndex + 1) % images.length;
        updatePreviewImage();
    });

    modalPrev.addEventListener('click', () => {
        const images = document.querySelectorAll('.cont_one img.image_1');
        currentPreviewIndex = (currentPreviewIndex - 1 + images.length) % images.length;
        updatePreviewImage();
    });

    function updatePreviewImage() {
        const images = document.querySelectorAll('.cont_one img.image_1');
        if (images.length > 0 && currentPreviewIndex < images.length) {
            const img = images[currentPreviewIndex];
            previewLarge.src = img.src;
            previewInfo.textContent = `图片 ${currentPreviewIndex + 1}/${images.length} - ${img.src.split('/').pop()}`;
        }
    }

    // 为每张图片添加下载按钮
    function addDownloadButtons() {
        const containers = document.querySelectorAll('.cont_one');

        containers.forEach(container => {
            // 检查是否已添加过按钮
            if (container.querySelector('.dh-single-download')) return;

            const img = container.querySelector('img.image_1');
            if (!img) return;

            const pElement = container.querySelector('p');
            if (pElement) {
                const downloadBtn = document.createElement('button');
                downloadBtn.className = 'dh-single-download';
                downloadBtn.innerHTML = `
                    <svg width="14" height="14" viewBox="0 0 24 24" fill="white">
                        <path d="M19 9h-4V3H9v6H5l7 7 7-7zM5 18v2h14v-2H5z"/>
                    </svg>
                    下载
                `;

                downloadBtn.addEventListener('click', function(e) {
                    e.stopPropagation();

                    // 显示控制面板
                    panel.classList.add('active');

                    // 更新状态
                    statusText.textContent = "正在下载单张图片...";
                    currentFile.classList.add('active');

                    const imgUrl = img.src;
                    const filename = downloadImage(imgUrl);

                    filename.textContent = "单张图片下载";
                    fileInfo.textContent = `已下载: ${filename}`;

                    // 2秒后恢复状态
                    setTimeout(() => {
                        statusText.textContent = "单张图片下载完成";
                        setTimeout(() => {
                            statusText.textContent = "就绪,等待操作";
                            currentFile.classList.remove('active');
                        }, 2000);
                    }, 500);
                });

                pElement.appendChild(downloadBtn);
            }
        });

        // 更新图片计数
        imageCount.textContent = containers.length;

        // 创建预览项
        createPreviewItems();

        // 模拟VIP状态检测
        vipStatus.textContent = document.querySelector('.vip-indicator') ? 'VIP' : '普通用户';
    }

    // 初始添加按钮
    addDownloadButtons();

    // 监听DOM变化(处理动态加载内容)
    const observer = new MutationObserver(function(mutations) {
        mutations.forEach(() => {
            addDownloadButtons();
        });
    });

    const targetNode = document.getElementById('allList');
    if (targetNode) {
        observer.observe(targetNode, {
            childList: true,
            subtree: true
        });
    }

    // 添加VIP保护提示
    const vipNotice = document.createElement('div');
    vipNotice.className = 'vip-protection-notice';
    vipNotice.innerHTML = 'VIP权益已受保护';
    document.body.appendChild(vipNotice);

    // 5秒后隐藏提示
    setTimeout(() => {
        vipNotice.style.display = 'none';
    }, 5000);
})();

(function() {
    'use strict';

    // 创建样式以修复移除工具栏后的布局
    const style = document.createElement('style');
    style.innerHTML = `
    `;
    document.head.appendChild(style);

    // 移除悬浮工具栏
    function removeSlideTool() {
        const slideTool = document.querySelector('.slide-tool.bg-white');
        if (slideTool) {
            slideTool.remove();
            console.log('悬浮工具栏已移除');

            // 显示移除通知
            showRemovalNotice();
        }
    }

    // 显示移除通知
    function showRemovalNotice() {
        const notice = document.createElement('div');
        notice.innerHTML = `
        `;

        document.body.appendChild(notice);

        // 5秒后移除通知
        setTimeout(() => {
            notice.style.opacity = '0';
            notice.style.transform = 'translateY(20px)';
            setTimeout(() => notice.remove(), 500);
        }, 5000);
    }

    // 初始尝试移除
    removeSlideTool();

    // 设置MutationObserver监听DOM变化
    const observer = new MutationObserver(function(mutations) {
        mutations.forEach(function(mutation) {
            if (mutation.addedNodes && mutation.addedNodes.length > 0) {
                // 检查是否有新增的悬浮工具栏
                const slideTool = document.querySelector('.slide-tool.bg-white');
                if (slideTool) {
                    slideTool.remove();
                    console.log('动态添加的悬浮工具栏已移除');
                }
            }
        });
    });

    // 开始观察整个文档
    observer.observe(document.body, {
        childList: true,
        subtree: true
    });

    // 10秒后停止观察
    setTimeout(() => {
        observer.disconnect();
        console.log('已停止观察DOM变化');
    }, 10000);
})();