Instagram Gallery Downloader

Scans for images and downloads them from cache

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         Instagram Gallery Downloader
// @namespace    http://tampermonkey.net/
// @version      1.2
// @description  Scans for images and downloads them from cache
// @match        https://*.instagram.com/*
// @match        https://instagram.com/*
// @grant        GM_download
// @connect      instagram.com
// @connect      cdninstagram.com
// @license MIT
// ==/UserScript==

(function() {
    'use strict';

    let targetUl = null;
    let foundImages = new Set();
    let scanInterval = null;
    let scanButton = null;
    let isScanning = false;
    let downloadInProgress = false;

    // Create and add floating button
    function createFloatingButton() {
        scanButton = document.createElement('button');
        updateButtonState();
        scanButton.style.cssText = `
            position: fixed;
            bottom: 20px;
            right: 20px;
            z-index: 9999;
            padding: 15px 20px;
            background-color: white;
            color: black;
            border: none;
            border-radius: 50px;
            cursor: pointer;
            min-width: 100px;
            transition: background-color 0.3s;
        `;

        scanButton.addEventListener('click', handleButtonClick);
        document.body.appendChild(scanButton);
    }

    // Handle button click based on current state
    function handleButtonClick() {
        if (isScanning) {
            stopScanning();
        } else if (downloadInProgress) {
            // Do nothing while downloading
            return;
        } else if (foundImages.size > 0) {
            startDownloading();
        } else {
            findLargestImageParentUl();
        }
    }

    // Update button appearance based on state
    function updateButtonState() {
        if (!scanButton) return;

        if (downloadInProgress) {
            scanButton.textContent = 'Downloading...';
        } else if (isScanning) {
            scanButton.textContent = 'Stop Scan';
        } else {
            scanButton.textContent = foundImages.size > 0 ? `Download ${foundImages.size} images` : 'Start scan';
        }
    }

    // Start downloading process
    async function startDownloading() {
        downloadInProgress = true;
        updateButtonState();

        const totalImages = foundImages.size;
        let currentImage = 0;

        for (const imageUrl of foundImages) {
            currentImage++;
            scanButton.textContent = `Downloading ${currentImage}/${totalImages}`;

            try {
                await downloadImageFromCache(imageUrl);
                await new Promise(resolve => setTimeout(resolve, 500)); // Delay between downloads
            } catch (error) {
                console.error('Error downloading image:', imageUrl, error);
            }
        }

        downloadInProgress = false;
        foundImages.clear();
        updateButtonState();
    }

    // Download single image from cache
    async function downloadImageFromCache(imageUrl) {
        return new Promise((resolve, reject) => {
            // First, try to fetch from cache
            fetch(imageUrl, { cache: 'force-cache' })
                .then(response => response.blob())
                .then(blob => {
                    // Create a download link
                    const filename = imageUrl.split('/').pop().split('?')[0] || 'image.jpg';
                    const url = URL.createObjectURL(blob);
                    const a = document.createElement('a');
                    a.href = url;
                    a.download = filename;
                    document.body.appendChild(a);
                    a.click();
                    document.body.removeChild(a);
                    URL.revokeObjectURL(url);
                    resolve();
                })
                .catch(reject);
        });
    }

    // Find largest image and its parent UL
    function findLargestImageParentUl() {
        let maxArea = 0;
        let largestImg = null;

        const images = document.getElementsByTagName('img');

        for (const img of images) {
            const area = img.offsetWidth * img.offsetHeight;
            if (area > maxArea) {
                maxArea = area;
                largestImg = img;
            }
        }

        if (largestImg) {
            let element = largestImg;
            while (element && element.tagName !== 'UL') {
                element = element.parentElement;
            }

            if (element && element.tagName === 'UL') {
                targetUl = element;
                startScanning();
            } else {
                alert('No parent UL found for the largest image');
            }
        }
    }

    // Start periodic scanning
    function startScanning() {
        if (scanInterval) {
            clearInterval(scanInterval);
        }

        isScanning = true;
        updateButtonState();
        scanInterval = setInterval(scanForNewImages, 150);
    }

    // Stop scanning
    function stopScanning() {
        if (scanInterval) {
            clearInterval(scanInterval);
            scanInterval = null;
        }
        isScanning = false;
        updateButtonState();
        console.log('Scanning stopped. Total unique images found:', foundImages.size);
    }

    // Scan for new images in the target UL
    function scanForNewImages() {
        if (!targetUl) return;

        const images = targetUl.getElementsByTagName('img');
        for (const img of images) {
            const src = img.src;
            if (src && !foundImages.has(src)) {
                foundImages.add(src);
                console.log('New image found:', src);
            }
        }
    }

    // Initialize
    createFloatingButton();
})();