Instagram Link Collector

Collect Instagram post URLs during auto-scroll. Edge hover UI, idle-stop, clipboard copy, .txt export.

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

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

Tendrás que 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.

Tendrás que instalar una extensión como Tampermonkey antes de poder 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)

Tendrás que instalar una extensión como Stylus antes de poder instalar este script.

Tendrás que instalar una extensión como Stylus antes de poder instalar este script.

Tendrás que instalar una extensión como Stylus antes de poder instalar este script.

Para poder instalar esto tendrás que instalar primero una extensión de estilos de usuario.

Para poder instalar esto tendrás que instalar primero una extensión de estilos de usuario.

Para poder instalar esto tendrás que instalar primero una extensión de estilos de usuario.

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

// ==UserScript==
// @name         Instagram Link Collector
// @namespace    https://www.instagram.com/
// @version      1.1.5
// @description  Collect Instagram post URLs during auto-scroll. Edge hover UI, idle-stop, clipboard copy, .txt export.
// @author       Gemini 3 Pro (previously ChatGPT 5.2 Thinking)
// @icon         https://web.archive.org/web/20171228000551im_/https://www.instagram.com/static/images/ico/favicon.ico/dfa85bb1fd63.ico
// @match        https://www.instagram.com/*
// @grant        GM_addStyle
// @grant        GM_setClipboard
// @license      MIT
// ==/UserScript==

(function () {
    "use strict";

    var collected = new Set();
    var intervalId = null;
    var countdownInterval = null;
    var lastHeight = 0;
    var idleCounter = 0;

    var DEFAULT_IDLE_SECONDS = 33;

    var TAB_W = 18;
    var TAB_H = 44;
    var TAB_TEXT_PAD = TAB_W + 9;
    var PANEL_W = 340;

    var hideTimer = null;
    var isShown = false;
    var pinnedUntil = 0;

    function nowMs() { return Date.now(); }

    function pinBriefly(ms) {
        pinnedUntil = nowMs() + (ms || 2000);
        showPanel(true);
    }

    function shouldBlockHide() {
        return nowMs() <= pinnedUntil || intervalId !== null || countdownInterval !== null;
    }

    function normalizeUrl(u) {
        if (!u) return "";
        return u.split("?")[0].split("#")[0].replace(/\/+$/, "");
    }

    function updateOutput() {
        var list = Array.from(collected).sort();
        output.value = list.join("\n");
        counter.textContent = list.length + " links";
        if (!intervalId && status.textContent === "Scrolling...") status.textContent = "Idle";
        if (intervalId && status.textContent !== "Scrolling...") status.textContent = "Scrolling...";
    }

    function isLoaderVisible() {
        return !!document.querySelector('svg[aria-label="Loading..."]');
    }

    function collectLinks() {
        var links = document.querySelectorAll('a[href*="/p/"]');
        var initial = collected.size;

        for (var i = 0; i < links.length; i++) {
            var href = links[i].href;
            if (!href) continue;
            collected.add(normalizeUrl(href));
        }

        if (collected.size !== initial) {
            updateOutput();
            return true;
        }
        return false;
    }

    function stopScroll(reason) {
        if (intervalId) {
            clearInterval(intervalId);
            intervalId = null;
        }
        if (countdownInterval) {
            clearInterval(countdownInterval);
            countdownInterval = null;
        }
        status.textContent = reason || "Stopped";
        updateOutput();
    }

    function autoSave() {
        var urls = Array.from(collected);
        if (!urls.length) return;

        var username = "instagram";
        var pathMatch = window.location.pathname.match(/^\/([a-zA-Z0-9_.]+)/);
        if (pathMatch && pathMatch[1]) {
            var bad = ["p", "explore", "direct", "reels", "stories", "accounts"];
            if (bad.indexOf(pathMatch[1]) === -1) username = pathMatch[1];
        }

        var d = new Date();
        var yyyy = d.getFullYear();
        var mm = String(d.getMonth() + 1).padStart(2, "0");
        var dd = String(d.getDate()).padStart(2, "0");
        var dateStr = yyyy + "-" + mm + "-" + dd;

        var filename = username + "_instagram-urls_" + dateStr + ".txt";

        var blob = new Blob([urls.join("\n")], { type: "text/plain" });
        var url = URL.createObjectURL(blob);
        var a = document.createElement("a");
        a.href = url;
        a.download = filename;
        document.body.appendChild(a);
        a.click();
        document.body.removeChild(a);
        URL.revokeObjectURL(url);
    }

    function autoScrollTick() {
        var currentHeight = document.body.scrollHeight;
        window.scrollBy(0, window.innerHeight);

        var newLinksFound = collectLinks();
        var idleThresholdTicks = Math.max(1, Math.floor(parseInt(idleInput.value, 10) / 2));

        if (newLinksFound || currentHeight > lastHeight || isLoaderVisible()) {
            idleCounter = 0;
            lastHeight = currentHeight;
        } else {
            idleCounter++;
        }

        if (idleCounter >= idleThresholdTicks) {
            stopScroll("Auto-stopped (idle)");
            autoSave();
        }
    }

    function startScroll() {
        if (intervalId || countdownInterval) return;

        var countdown = 3;
        status.textContent = "Starting in " + countdown + "...";
        pinBriefly(3000);

        countdownInterval = setInterval(function () {
            countdown--;
            if (countdown > 0) {
                status.textContent = "Starting in " + countdown + "...";
            } else {
                clearInterval(countdownInterval);
                countdownInterval = null;

                lastHeight = document.body.scrollHeight;
                idleCounter = 0;
                intervalId = setInterval(autoScrollTick, 2000);
                status.textContent = "Scrolling...";
                collectLinks();
                updateOutput();
            }
        }, 1000);
    }

    function copyAll() {
        GM_setClipboard(output.value || "");
        status.textContent = "Copied";
        setTimeout(function () { status.textContent = intervalId ? "Scrolling..." : "Idle"; }, 900);
    }

    function downloadTxt() {
        autoSave();
        status.textContent = "Downloaded";
        setTimeout(function () { status.textContent = intervalId ? "Scrolling..." : "Idle"; }, 900);
    }

    function clearAll() {
        collected.clear();
        updateOutput();
        status.textContent = "Cleared";
        setTimeout(function () { status.textContent = intervalId ? "Scrolling..." : "Idle"; }, 900);
    }

    GM_addStyle(
        '#ig_hover_tab{' +
        'position:fixed;left:0;top:50%;width:' + TAB_W + 'px;height:' + TAB_H + 'px;margin-top:-' + Math.floor(TAB_H / 2) + 'px;' +
        'z-index:100000;cursor:pointer;opacity:0.7;background-color:#bdc5c8;' +
        'border:1px solid #abb0b3;border-left:none;border-radius:0 5px 5px 0;' +
        'display:flex;align-items:center;justify-content:center;' +
        '}' +
        '#ig_hover_tab:hover{opacity:1;}' +
        '#ig_hover_tab svg{display:block;}' +
        '#ig_panel{' +
        'position:fixed;left:0;top:50%;width:' + PANEL_W + 'px;z-index:99999;' +
        'transform:translateX(-100%);transition:transform 140ms linear,opacity 140ms linear;opacity:0.92;' +
        'background:rgba(0,0,0,0.75);color:#fff;padding:10px 10px 10px ' + TAB_TEXT_PAD + 'px;' +
        'border-radius:0 8px 8px 0;font-size:12px;box-shadow:0 2px 10px rgba(0,0,0,0.35);' +
        '}' +
        '#ig_panel.ig_show{transform:translateX(0);opacity:1;}' +
        '#ig_title{font-weight:700;margin:0 0 4px 0;}' +
        '#ig_row{display:flex;align-items:center;justify-content:space-between;gap:8px;margin:0;}' +
        '#ig_status{opacity:0.9;font-size:11px;}' +
        '#ig_actions_row1{display:flex;flex-wrap:wrap;gap:6px;margin-top:8px;margin-bottom:6px;}' +
        '#ig_actions_row2{display:flex;flex-wrap:wrap;gap:6px;margin-bottom:8px;align-items:center;}' +
        '#ig_panel button{' +
        'padding:4px 10px;font-size:12px;cursor:pointer;background:rgba(255,255,255,0.08);color:#fff;' +
        'border:1px solid rgba(255,255,255,0.25);border-radius:999px;line-height:1.2;user-select:none;' +
        '}' +
        '#ig_panel button:hover{background:rgba(255,255,255,0.18);}' +
        '#ig_panel button:active{background:rgba(255,255,255,0.28);}' +
        // CHANGE: Added !important and solid colors to force visibility
        '#ig_start{border:1px solid #4CAF50 !important; box-shadow: 0 0 3px #4CAF50;}' +
        '#ig_stop{border:1px solid #F44336 !important; box-shadow: 0 0 3px #F44336;}' +
        '#ig_clear{border:1px solid #FFEB3B !important; box-shadow: 0 0 3px #FFEB3B; color: #fff;}' +
        '#ig_idle_wrap{display:flex;align-items:center;gap:6px;}' +
        '#ig_idle_wrap span{font-size:11px;opacity:0.9;}' +
        '#ig_idle{width:56px;font-size:12px;padding:2px 6px;border-radius:8px;border:1px solid rgba(255,255,255,0.25);background:rgba(0,0,0,0.35);color:#fff;}' +
        '#ig_output{width:100%;height:180px;margin-top:6px;font-size:11px;resize:vertical;}'
    );

    var tab = document.createElement("div");
    tab.id = "ig_hover_tab";
    tab.innerHTML =
        '<svg width="12" height="12" viewBox="0 0 12 12" aria-hidden="true">' +
        '<path id="ig_arrow_path" d="M4 2 L8 6 L4 10 Z" fill="#2b2f33"></path>' +
        '</svg>';
    document.body.appendChild(tab);

    var arrowPath = document.getElementById("ig_arrow_path");

    var panel = document.createElement("div");
    panel.id = "ig_panel";
    panel.innerHTML =
        '<div id="ig_header">' +
            '<div id="ig_title">Instagram Link Collector</div>' +
            '<div id="ig_row">' +
                '<div id="ig_counter">0 links</div>' +
                '<div id="ig_status">Idle</div>' +
            '</div>' +
        '</div>' +
        '<div id="ig_actions_row1">' +
            '<button id="ig_start" type="button">Start scroll</button>' +
            '<button id="ig_stop" type="button">Stop</button>' +
            '<button id="ig_copy" type="button">Copy</button>' +
            '<button id="ig_clear" type="button">Clear</button>' +
        '</div>' +
        '<div id="ig_actions_row2">' +
            '<button id="ig_txt" type="button">Download .txt</button>' +
            '<div id="ig_idle_wrap"><span>Idle (s)</span><input id="ig_idle" type="number" min="2" value="' + DEFAULT_IDLE_SECONDS + '"></div>' +
        '</div>' +
        '<textarea id="ig_output" readonly></textarea>';
    document.body.appendChild(panel);

    var headerWrap = document.getElementById("ig_header");
    var counter = document.getElementById("ig_counter");
    var status = document.getElementById("ig_status");
    var output = document.getElementById("ig_output");
    var idleInput = document.getElementById("ig_idle");

    function setArrow(open) {
        arrowPath.setAttribute("d", open ? "M8 2 L4 6 L8 10 Z" : "M4 2 L8 6 L4 10 Z");
    }

    function setPanelTopToTab() {
        var tabRect = tab.getBoundingClientRect();
        var headerH = headerWrap.getBoundingClientRect().height;
        var top = Math.round(tabRect.top - headerH - 6);
        panel.style.top = top + "px";
    }

    function showPanel(force) {
        if (hideTimer) {
            clearTimeout(hideTimer);
            hideTimer = null;
        }
        setPanelTopToTab();
        if (isShown && !force) return;
        isShown = true;
        panel.classList.add("ig_show");
        setArrow(true);
    }

    function scheduleHide() {
        if (shouldBlockHide()) return;

        if (hideTimer) clearTimeout(hideTimer);
        hideTimer = setTimeout(function () {
            if (shouldBlockHide()) return;
            isShown = false;
            panel.classList.remove("ig_show");
            setArrow(false);
        }, 140);
    }

    tab.addEventListener("mouseenter", function () { showPanel(false); });
    tab.addEventListener("mouseleave", scheduleHide);
    panel.addEventListener("mouseenter", function () { showPanel(true); });
    panel.addEventListener("mouseleave", scheduleHide);

    window.addEventListener("resize", function () {
        if (isShown) setPanelTopToTab();
    }, { passive: true });

    document.getElementById("ig_start").addEventListener("click", function (e) {
        e.preventDefault();
        pinBriefly(3000);
        startScroll();
    });

    document.getElementById("ig_stop").addEventListener("click", function (e) {
        e.preventDefault();
        pinBriefly(3000);
        stopScroll("Stopped");
    });

    document.getElementById("ig_copy").addEventListener("click", function (e) {
        e.preventDefault();
        pinBriefly(3000);
        copyAll();
    });

    document.getElementById("ig_txt").addEventListener("click", function (e) {
        e.preventDefault();
        pinBriefly(3000);
        downloadTxt();
    });

    document.getElementById("ig_clear").addEventListener("click", function (e) {
        e.preventDefault();
        pinBriefly(3000);
        clearAll();
    });

    setArrow(false);
    collectLinks();
    updateOutput();
})();