DuckDuckGo Image Link Extractor (Iterate Tiles)

Iterates through div.tile--img, clicks, extracts, closes (fixed)

Tendrás que instalar una extensión para tu navegador como Tampermonkey, Greasemonkey o Violentmonkey si quieres utilizar este script.

Necesitarás instalar una extensión como Tampermonkey o Violentmonkey para instalar este script.

Necesitarás instalar una extensión como Tampermonkey o Violentmonkey para instalar este script.

Necesitarás instalar una extensión como Tampermonkey o Userscripts para instalar este script.

Necesitará instalar una extensión como Tampermonkey para instalar este script.

Necesitarás instalar una extensión para administrar scripts de usuario si quieres instalar este script.

(Ya tengo un administrador de scripts de usuario, déjame instalarlo)

Necesitará instalar una extensión como Stylus para instalar este estilo.

Necesitará instalar una extensión como Stylus para instalar este estilo.

Necesitará instalar una extensión como Stylus para instalar este estilo.

Necesitará instalar una extensión del gestor de estilos de usuario para instalar este estilo.

Necesitará instalar una extensión del gestor de estilos de usuario para instalar este estilo.

Necesitará instalar una extensión del gestor de estilos de usuario para instalar este estilo.

(Ya tengo un administrador de estilos de usuario, déjame instalarlo)

// ==UserScript==
// @name        DuckDuckGo Image Link Extractor (Iterate Tiles)
// @namespace   yournamespace
// @match       https://duckduckgo.com/*images*
// @grant       none
// @version     1.7
// @license     GNU General Public License v3.0
// @description Iterates through div.tile--img, clicks, extracts, closes (fixed)
// ==/UserScript==

(function() {
    'use strict';

    let isProcessing = false;
    let stopProcessing = false;
    let processedCount = 0;
    let totalCount = 0;

    async function processImageTiles() {
        if (isProcessing) return;
        
        isProcessing = true;
        stopProcessing = false;
        processedCount = 0;
        
        const imageTiles = document.querySelectorAll('div.tile--img');
        totalCount = imageTiles.length;
        
        updateProgress(0, totalCount);
        
        for (const tile of imageTiles) {
            if (stopProcessing) break;
            
            const imgElement = tile.querySelector('.tile--img__img');
            const dimensions = tile.querySelector('.tile--img__dimensions');
            
            if (imgElement && dimensions) {
                // Parse dimensions (format: "2560 × 1677")
                const [width, height] = dimensions.textContent.split(' × ').map(Number);
                
                // Calculate total pixels and compare to 1920x1080 (2,073,600 pixels)
                const totalPixels = width * height;
                if (totalPixels < 2073600) {
                    processedCount++; // Count as processed
                    updateProgress(processedCount, totalCount);
                    continue;
                }
                try {
                    imgElement.click();
                    const detailViewSuccess = await waitForDetailView();
                    if (detailViewSuccess) {
                        await extractAndClose();
                        processedCount++;
                        updateProgress(processedCount, totalCount);
                    }
                } catch (error) {
                    console.error('Error processing tile:', error);
                    // Continue with next tile even if one fails
                }
            }
            await new Promise(resolve => setTimeout(resolve, 500)); // Increased delay between tiles
        }

        isProcessing = false;
        showPopup(stopProcessing ? "Extraction stopped" : "Extraction complete");
        
        // Show select all button
        const selectAllBtn = document.querySelector('#extractedLinksList + div button');
        if (selectAllBtn) {
            selectAllBtn.style.display = 'block';
        }
    }

    async function waitForDetailView() {
        return new Promise(resolve => {
            const checkInterval = setInterval(() => {
                const detailBtn = document.querySelector('a.c-detail__btn, .detail__media, .detail__content');
                if (detailBtn) {
                    clearInterval(checkInterval);
                    resolve(true);
                }
            }, 200);
            setTimeout(() => {
                clearInterval(checkInterval);
                resolve(false);
            }, 3000);
        });
    }

    async function extractAndClose() {
        const linkElement = document.querySelector('a.c-detail__btn');
        if (linkElement) {
            const href = linkElement.getAttribute('href');
            if (href) {
                displaySingleLink(href);
            }
        }
        const closeButton = document.querySelector('a.detail__close');
        if (closeButton) {
            closeButton.click();
            await new Promise(resolve => setTimeout(resolve, 500)); // Increased delay after close
            // await waitForClose(); //removed waitForClose function
        }
    }

    function displaySingleLink(link) {
        let linkList = document.getElementById('extractedLinksList');
        if (!linkList) {
            createLinkLists();
            linkList = document.getElementById('extractedLinksList');
        }

        if (!linkList) {
            linkList = document.createElement('div');
            linkList.id = 'extractedLinksList';
            linkList.style.position = 'fixed';
            linkList.style.top = '10px';
            linkList.style.left = '10px';
            linkList.style.backgroundColor = '#f5f5f5';
            linkList.style.border = '1px solid #ddd';
            linkList.style.padding = '10px';
            linkList.style.zIndex = '1000';
            linkList.style.maxHeight = '33vh';
            linkList.style.overflowY = 'auto';
            linkList.style.width = '300px';
            linkList.style.borderRadius = '4px';
            linkList.style.boxShadow = '0 2px 4px rgba(0,0,0,0.1)';
            document.body.appendChild(linkList);
        }

        const ul = document.createElement('ul');
        ul.style.listStyle = 'none';
        ul.style.margin = '0';
        ul.style.padding = '0';
        
        const li = document.createElement('li');
        li.style.margin = '5px 0';
        li.style.padding = '8px';
        li.style.backgroundColor = 'white';
        li.style.border = '1px solid #eee';
        li.style.borderRadius = '3px';
        
        const a = document.createElement('a');
        a.href = link;
        a.textContent = link;
        a.target = "_blank";
        a.style.color = '#0077cc';
        a.style.textDecoration = 'none';
        a.style.wordBreak = 'break-all';
        a.style.display = 'block';
        
        a.addEventListener('mouseover', () => {
            a.style.textDecoration = 'underline';
        });
        a.addEventListener('mouseout', () => {
            a.style.textDecoration = 'none';
        });

        li.appendChild(a);
        ul.appendChild(li);
        linkList.appendChild(ul);
        
        // Update link counter
        const linkCounter = document.getElementById('linkCounter');
        if (linkCounter) {
            const linkCount = linkList.querySelectorAll('ul').length;
            linkCounter.textContent = `${linkCount} link${linkCount !== 1 ? 's' : ''}`;
        }
    }

    function showPopup(message) {
        const popup = document.createElement('div');
        popup.textContent = message;
        popup.style.position = 'fixed';
        popup.style.top = '50%';
        popup.style.left = '50%';
        popup.style.transform = 'translate(-50%, -50%)';
        popup.style.backgroundColor = 'white';
        popup.style.border = '1px solid black';
        popup.style.padding = '10px';
        popup.style.zIndex = '1001';

        document.body.appendChild(popup);

        setTimeout(() => {
            document.body.removeChild(popup);
        }, 2000);
    }

    function createLinkLists() {
        const container = document.createElement('div');
        container.style.position = 'fixed';
        container.style.top = '10px';
        container.style.left = '10px';
        container.style.zIndex = '1000';
        container.style.display = 'flex';
        container.style.flexDirection = 'column';
        container.style.gap = '10px';

        // Create select all button
        const selectAllBtn = document.createElement('button');
        selectAllBtn.textContent = 'Select All';
        selectAllBtn.style.position = 'absolute';
        selectAllBtn.style.left = '320px'; // Position to the right of the link boxes
        selectAllBtn.style.top = '10px'; // Align with top of first box
        selectAllBtn.style.zIndex = '1001';
        selectAllBtn.style.padding = '4px 8px';
        selectAllBtn.style.borderRadius = '3px';
        selectAllBtn.style.border = '1px solid #ccc';
        selectAllBtn.style.backgroundColor = '#f5f5f5';
        selectAllBtn.style.cursor = 'pointer';
        
        selectAllBtn.addEventListener('click', () => {
            const linkList = document.getElementById('extractedLinksList');
            if (linkList) {
                const range = document.createRange();
                range.selectNodeContents(linkList);
                const selection = window.getSelection();
                selection.removeAllRanges();
                selection.addRange(range);
            }
        });

        // Create good links list
        const goodList = document.createElement('div');
        goodList.id = 'extractedLinksList';
        goodList.style.backgroundColor = '#f5f5f5';
        goodList.style.border = '1px solid #ddd';
        goodList.style.padding = '10px';
        goodList.style.maxHeight = '33vh';
        goodList.style.overflowY = 'auto';
        goodList.style.width = '300px';
        goodList.style.borderRadius = '4px';
        goodList.style.boxShadow = '0 2px 4px rgba(0,0,0,0.1)';
        goodList.style.position = 'relative'; // Needed for absolute positioning of counter

        // Create link counter
        const linkCounter = document.createElement('div');
        linkCounter.id = 'linkCounter';
        linkCounter.style.position = 'absolute';
        linkCounter.style.bottom = '5px';
        linkCounter.style.right = '10px';
        linkCounter.style.backgroundColor = 'rgba(255,255,255,0.8)';
        linkCounter.style.padding = '2px 6px';
        linkCounter.style.borderRadius = '3px';
        linkCounter.style.fontSize = '0.8em';
        linkCounter.style.color = '#666';
        linkCounter.textContent = '0 links';
        goodList.appendChild(linkCounter);

        container.appendChild(goodList);
        container.appendChild(selectAllBtn);
        document.body.appendChild(container);
    }


    function createControlPanel() {
        const panel = document.createElement('div');
        panel.style.position = 'fixed';
        panel.style.top = '10px';
        panel.style.right = '10px';
        panel.style.zIndex = '1000';
        panel.style.display = 'flex';
        panel.style.gap = '5px';
        panel.style.backgroundColor = 'white';
        panel.style.padding = '5px';
        panel.style.border = '1px solid #ccc';
        panel.style.borderRadius = '4px';

        const startButton = document.createElement('button');
        startButton.textContent = 'Start Extraction';
        startButton.addEventListener('click', processImageTiles);

        const stopButton = document.createElement('button');
        stopButton.textContent = 'Stop';
        stopButton.style.backgroundColor = '#ff4444';
        stopButton.addEventListener('click', () => {
            stopProcessing = true;
        });

        const progress = document.createElement('div');
        progress.id = 'extractionProgress';
        progress.style.marginLeft = '10px';
        progress.style.padding = '5px 10px';

        panel.appendChild(startButton);
        panel.appendChild(stopButton);
        panel.appendChild(progress);
        document.body.appendChild(panel);
    }

    function updateProgress(current, total) {
        const progress = document.getElementById('extractionProgress');
        if (progress) {
            progress.textContent = `${current}/${total} (${Math.round((current/total)*100)}%)`;
            progress.style.color = current === total ? 'green' : 'black';
        }
    }

    createControlPanel();
})();