Greasy Fork

来自缓存

Greasy Fork is available in English.

即梦下载无水印原图

为即梦添加按钮,可以直接下载无水印原图

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         即梦下载无水印原图
// @namespace    http://tampermonkey.net/
// @version      3.1
// @description  为即梦添加按钮,可以直接下载无水印原图
// @author       psdoc烛光
// @match        https://jimeng.jianying.com/*
// @icon         https://favicon.im/jimeng.jianying.com?larger=true&t=1755764200795
// @grant        GM_download
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';

    // 核心配置
    const CONFIG = {
        maxRetry: 3,
        retryDelay: 500,
        debounceTime: 1000,
        selectors: [
            'div.operation-area-uoDdSN',
            'div.action-buttons-wrapper-ibCKz2',
            'div.operation-buttons-ita9Ow'
        ]
    };

    // 状态管理
    let state = {
        isAdding: false,
        lastAddTime: 0,
        retryCount: 0,
        observer: null
    };

    /**
     * 查找目标图片
     */
    function findTargetImage() {
        const selectors = [
            'img[crossorigin="anonymous"]:not([src*="avatar"]):not([src*="profile"])',
            'img[src*="jimeng"]:not([src*="avatar"]):not([src*="profile"])',
            'img[src*="byteimg"]:not([src*="avatar"]):not([src*="profile"])',
            'img[src*=".jpg"]:not([src*="avatar"]):not([src*="profile"])',
            'img[src*=".png"]:not([src*="avatar"]):not([src*="profile"])'
        ];

        const images = [];
        selectors.forEach(selector => {
            document.querySelectorAll(selector).forEach(img => {
                if (img.src && img.src.includes('http')) {
                    const area = (img.naturalWidth || img.width) * (img.naturalHeight || img.height);
                    if (area > 10000) {
                        images.push({ element: img, src: img.src, area });
                    }
                }
            });
        });

        return images.sort((a, b) => b.area - a.area)[0];
    }

    /**
     * 下载图片
     */
    function downloadImage() {
        const target = findTargetImage();
        if (!target) {
            console.log('未找到可下载的图片');
            return;
        }

        const url = target.src;
        const filename = url.split('/').pop().split('?')[0] || 'image.jpg';

        GM_download({
            url,
            name: filename,
            saveAs: true,
            onload: () => console.log('下载成功'),
            onerror: (error) => {
                console.log('GM_download失败,尝试降级方案');
                fallbackDownload(url, filename);
            }
        });
    }

    /**
     * 降级下载方案
     */
    function fallbackDownload(url, filename) {
        try {
            const link = document.createElement('a');
            link.href = url;
            link.download = filename;
            link.click();
        } catch (error) {
            window.open(url, '_blank');
        }
    }

    /**
     * 创建下载按钮
     */
    function createButton() {
        const button = document.createElement('div');
        button.className = 'operation-button-PU0Wce jimeng-download-btn';
        button.innerHTML = `
            <svg width="1em" height="1em" viewBox="0 0 24 24" fill="none">
                <path d="M12 2a1 1 0 0 1 1 1v10.312l4.023-4.021a1 1 0 0 1 1.414 1.414l-5.73 5.728a1 1 0 0 1-1.414 0l-5.73-5.728A1 1 0 1 1 6.977 9.29L11 13.312V3a1 1 0 0 1 1-1ZM3 20.002a1 1 0 0 1 1-1L20 19a1 1 0 0 1 0 2l-16 .002a1 1 0 0 1-1-1Z" fill="currentColor"/>
            </svg>
            <span>下载原图</span>
        `;

        button.style.cssText = `
            display: inline-flex;
            align-items: center;
            gap: 4px;
            padding: 8px 12px;
            background: #1890ff;
            color: white;
            border-radius: 8px;
            cursor: pointer;
            font-size: 14px;
            margin: 4px;
            transition: all 0.3s;
        `;

        button.addEventListener('mouseenter', () => button.style.background = '#40a9ff');
        button.addEventListener('mouseleave', () => button.style.background = '#1890ff');
        button.addEventListener('click', downloadImage);

        return button;
    }

    /**
     * 查找并添加按钮到容器
     */
    function addButtons() {
        if (state.isAdding || Date.now() - state.lastAddTime < CONFIG.debounceTime) {
            return false;
        }

        // 检查是否已经有按钮存在
        if (document.querySelector('.jimeng-download-btn')) {
            return false;
        }

        state.isAdding = true;
        let added = 0;

        try {
            // 按优先级添加:先尝试在操作按钮组添加,再尝试其他位置
            const prioritySelectors = [
                'div.operation-buttons-ita9Ow',  // 操作按钮组(优先级最高)
                'div.action-buttons-wrapper-ibCKz2',  // 动作按钮包装器
                'div.operation-area-uoDdSN'  // 操作区域
            ];

            for (const selector of prioritySelectors) {
                const containers = document.querySelectorAll(selector);
                for (const container of containers) {
                    if (!container.querySelector('.jimeng-download-btn')) {
                        const button = createButton();

                        // 根据容器类型调整样式
                        if (selector.includes('operation-buttons')) {
                            button.style.margin = '0';
                            button.style.padding = '8px';
                            button.style.minWidth = 'auto';
                        }

                        container.appendChild(button);
                        added++;
                        // 只添加一个按钮,避免重复
                        return true;
                    }
                }
            }
        } finally {
            state.isAdding = false;
            state.lastAddTime = Date.now();
        }

        return added > 0;
    }

    /**
     * 删除所有按钮
     */
    function removeButtons() {
        document.querySelectorAll('.jimeng-download-btn').forEach(btn => btn.remove());
    }

    /**
     * 初始化
     */
    function init() {
        if (document.querySelector('.jimeng-download-btn')) return;

        if (!addButtons() && state.retryCount < CONFIG.maxRetry) {
            state.retryCount++;
            setTimeout(init, CONFIG.retryDelay * state.retryCount);
        }
    }

    /**
     * 设置观察者
     */
    function setupObserver() {
        state.observer = new MutationObserver(() => {
            // 检查是否已经有按钮存在
            if (document.querySelector('.jimeng-download-btn')) {
                return;
            }

            // 检查是否有合适的容器但没有按钮
            const hasEmptyContainers = CONFIG.selectors.some(selector =>
                Array.from(document.querySelectorAll(selector))
                    .some(container => !container.querySelector('.jimeng-download-btn'))
            );

            if (hasEmptyContainers) {
                setTimeout(addButtons, 100);
            }
        });

        state.observer.observe(document.body, {
            childList: true,
            subtree: true
        });
    }

    /**
     * 设置快捷键
     */
    function setupShortcuts() {
        document.addEventListener('keydown', (e) => {
            if (e.ctrlKey && e.shiftKey) {
                if (e.key === 'D') {
                    e.preventDefault();
                    removeButtons();
                } else if (e.key === 'A') {
                    e.preventDefault();
                    addButtons();
                }
            }
        });
    }

    // 启动脚本
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', () => {
            init();
            setupObserver();
            setupShortcuts();
        });
    } else {
        init();
        setupObserver();
        setupShortcuts();
    }

})();