Greasy Fork is available in English.

Link Copier with Redirects

Copies all links on a page to the clipboard, including redirects

/* jshint esversion: 8 */

// ==UserScript==
// @name         Link Copier with Redirects
// @namespace    http://tampermonkey.net/
// @version      1.0
// @description  Copies all links on a page to the clipboard, including redirects
// @match        *://*/*
// @grant        GM_setClipboard
// @grant        GM.xmlHttpRequest
// @grant        GM_notification
// @license      MIT
// ==/UserScript==

(async function() {
    'use strict';

    // Handle keydown events
    window.addEventListener('keydown', handleKeydown);

    // Function to handle keydown events
    function handleKeydown(event) {
        if (event.altKey && event.key === '1') {
            console.log('Alt+1 detected, starting to copy links to clipboard.');
            startLinkCopying();
        }
    }

    // Function to start the link copying process
    function startLinkCopying() {
        console.log('Showing start notification...');
        GM_notification({
            title: 'Link Copier with Redirects',
            text: 'Link copying process started!',
            timeout: 2500,
        });

        copyLinksToClipboard();
    }

    // Function to copy links to clipboard
    async function copyLinksToClipboard() {
        console.log('Collecting links from the page...');
        const links = Array.from(document.getElementsByTagName('a'));
        const uniqueLinks = Array.from(new Set(links.map(link => link.href)));
        console.log(`Found ${uniqueLinks.length} unique links.`);

        console.log('Following redirects for each link...');
        const linkPromises = uniqueLinks.map(async (link) => followRedirect(link));

        const linkResults = await Promise.all(linkPromises);
        console.log('All redirect chains followed.');

        console.log('Preparing text for the clipboard...');
        const clipboardText = linkResults.map((redirectChain) => {
            const originalLink = redirectChain[0];
            const redirectText = redirectChain.slice(1).map(link => `  - ${link}`).join('\n');
            return `${originalLink}\n${redirectText}`;
        }).join('\n').trim().replace(/\n\n+/g, '\n');

        console.log('Setting clipboard content...');
        GM_setClipboard(clipboardText);

        console.log('Showing completion notification...');
        GM_notification({
            title: 'Link Copier with Redirects',
            text: 'Links successfully copied to clipboard!',
            timeout: 2500,
        });
    }

    // Function to follow redirects
    async function followRedirect(url, maxRedirects = 5) {
        const seenUrls = [url];
        let currentUrl = url;
        console.log(`Starting to follow redirects for: ${url}`);

        while (maxRedirects > 0) {
            try {
                const response = await fetchUrl(currentUrl);

                if (response.finalUrl === currentUrl || seenUrls.includes(response.finalUrl)) {
                    break;
                }

                seenUrls.push(response.finalUrl);
                currentUrl = response.finalUrl;
                maxRedirects--;

            } catch (error) {
                console.error(`Error following redirect for link: ${currentUrl}`, error);
                break;
            }
        }

        console.log(`Redirect chain for ${url}:`, seenUrls);
        return seenUrls;
    }

    // Function to fetch a URL and handle redirects
    async function fetchUrl(url) {
        return new Promise((resolve, reject) => {
            GM.xmlHttpRequest({
                method: "GET",
                url: url,
                onload: (response) => resolve(response),
                onerror: (error) => reject(error)
            });
        });
    }

    window.addEventListener('load', () => {
        console.log('Link Copier with Redirects script loaded. Press Alt+1 to copy links.');
    });
})();