Instagram Gallery Downloader

Scans for images and downloads them from cache

// ==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();
})();