Greasy Fork

Greasy Fork is available in English.

alt+左键保存图片 (可自定义文件夹)

使用 Alt+左键 下载图片。已对 Instagram 优化。可通过 Tampermonkey 菜单设置下载子文件夹。

当前为 2025-10-29 提交的版本,查看 最新版本

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

You will need to install an extension such as Tampermonkey to install this script.

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         alt+左键保存图片 (可自定义文件夹)
// @namespace    http://tampermonkey.net/
// @version      3.0
// @description  使用 Alt+左键 下载图片。已对 Instagram 优化。可通过 Tampermonkey 菜单设置下载子文件夹。
// @author       yiha
// @match        *://*/*
// @grant        GM_download
// @grant        GM_notification
// @grant        GM_registerMenuCommand
// @grant        GM_setValue
// @grant        GM_getValue
// @connect      *
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';

    // --- 配置区域 ---
    const STORAGE_KEY = 'customDownloadFolder'; // 用于存储文件夹名称的键
    const DEFAULT_FOLDER = 'TM_Downloads';    // 默认的文件夹名称

    // --- 注册菜单命令 ---
    // 这会在 Tampermonkey 插件菜单中添加一个选项
    GM_registerMenuCommand('设置下载子文件夹', () => {
        const currentFolder = GM_getValue(STORAGE_KEY, DEFAULT_FOLDER);
        const newFolder = prompt('请输入要保存到的下载子文件夹名称:', currentFolder);

        // 如果用户输入了内容并且没有点“取消”
        if (newFolder && newFolder.trim() !== '') {
            GM_setValue(STORAGE_KEY, newFolder.trim());
            GM_notification({
                title: '设置已保存',
                text: `下载文件夹已更新为: ${newFolder.trim()}`,
                timeout: 3000
            });
        } else if (newFolder !== null) {
            // 用户删除了所有内容并点击了确定,我们可以重置为默认值
             GM_setValue(STORAGE_KEY, DEFAULT_FOLDER);
             GM_notification({
                title: '设置已重置',
                text: `下载文件夹已重置为默认值: ${DEFAULT_FOLDER}`,
                timeout: 3000
            });
        }
    });


    // --- 主要下载逻辑 ---
    document.addEventListener('mousedown', function(e) {
        if (!e.altKey || e.button !== 0) {
            return;
        }

        e.preventDefault();
        e.stopPropagation();

        let imageUrl = '';
        const clickedElement = e.target;

        // 策略一:Instagram 专用逻辑
        if (window.location.hostname.includes('instagram.com')) {
            const article = clickedElement.closest('article');
            if (article) {
                const imgElement = article.querySelector('div._aagv img, li div._aagv img');
                if (imgElement) {
                    const srcset = imgElement.srcset;
                    if (srcset) {
                        const sources = srcset.split(',').map(entry => {
                            const parts = entry.trim().split(/\s+/);
                            return { url: parts[0], width: parseInt(parts[1]?.replace('w', ''), 10) || 0 };
                        });
                        if (sources.length > 0) {
                            imageUrl = sources.reduce((max, current) => (current.width > max.width ? current : max)).url;
                        }
                    }
                    if (!imageUrl) {
                        imageUrl = imgElement.src;
                    }
                }
            }
        }

        // 策略二:通用逻辑
        if (!imageUrl) {
            let target = clickedElement;
            if (target.tagName !== 'IMG') {
                target = target.closest('div, a, figure')?.querySelector('img');
            }
            if (target && target.tagName === 'IMG') {
                imageUrl = target.src || target.dataset.src;
            }
        }

        if (!imageUrl) {
            console.log('未能在此次点击中找到可下载的图片URL。');
            return;
        }

        // --- 下载执行 ---
        const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
        let fileExt = 'jpg';

        try {
            const urlPath = new URL(imageUrl).pathname;
            const extensionMatch = urlPath.match(/\.([^.?]+)/);
            if (extensionMatch && extensionMatch[1]) {
                const potentialExt = extensionMatch[1].toLowerCase();
                const validExts = ['jpg', 'jpeg', 'png', 'gif', 'webp', 'svg', 'bmp'];
                if (validExts.includes(potentialExt)) {
                    fileExt = potentialExt;
                }
            }
        } catch (error) { console.error("解析URL时出错: ", error); }

        const fileName = `image_${timestamp}.${fileExt}`;

        // *** 关键改动:从存储中读取文件夹名称 ***
        const downloadFolder = GM_getValue(STORAGE_KEY, DEFAULT_FOLDER);
        const savePath = `${downloadFolder}/${fileName}`;

        console.log(`准备下载到: ${savePath}`);
        GM_download({
            url: imageUrl,
            name: savePath,
            saveAs: false,
            onload: function() {
                console.log(`图片保存成功: ${savePath}`);
            },
            onerror: function(errorDetails) {
                console.error('下载失败:', errorDetails);
                GM_notification({
                    title: '图片保存失败',
                    text: `错误: ${errorDetails.error}`,
                    timeout: 5000
                });
            }
        });

    }, true);
})();