Magnet Link Extractor

Extract and display magnet links with a single click

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

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

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

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

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

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// ==UserScript==
// @name         Magnet Link Extractor
// @namespace    http://tampermonkey.net/
// @version      1.2
// @description  Extract and display magnet links with a single click
// @note         v1.2 updates: 
// @note         - The icon now only appears on pages with magnet links; 
// @note         - Clicking the magnet icon automatically copies all detected magnet links to the clipboard. No additional action is required;
// @note         - The panel now features a selectable design with checkboxes for better user control.
// @match        *://*/*
// @grant        GM_addStyle
// @run-at       document-end
// @license      MIT

// ==/UserScript==

(function () {
    'use strict';

    // Add CSS
    GM_addStyle(`
        #magnet-icon {
            position: fixed;
            top: 50px;
            right: 5px;
            width: 25px;
            height: 25px;
            background: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iaXNvLTg4NTktMSI/Pg0KPCEtLSBVcGxvYWRlZCB0bzogU1ZHIFJlcG8sIHd3dy5zdmdyZXBvLmNvbSwgR2VuZXJhdG9yOiBTVkcgUmVwbyBNaXhlciBUb29scyAtLT4NCjxzdmcgaGVpZ2h0PSI4MDBweCIgd2lkdGg9IjgwMHB4IiB2ZXJzaW9uPSIxLjEiIGlkPSJMYXllcl8xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiANCgkgdmlld0JveD0iMCAwIDUxMi4wMTkgNTEyLjAxOSIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+DQo8cGF0aCBzdHlsZT0iZmlsbDojREE0NDUzOyIgZD0iTTM2NC40MzcsMjIzLjAyNkwyMDguMzczLDM3OS4wNTljLTIwLjc4MSwyMC44MTItNTQuNjI1LDIwLjgxMi03NS40MDYsMA0KCWMtMjAuODEzLTIwLjc4MS0yMC44MTMtNTQuNjI1LDAtNzUuNDA3bDE1Ni4wMzEtMTU2LjA2M0wyMDYuMDMsNjQuNjM1TDQ5Ljk5NywyMjAuNjgybDAsMGMtNjYuNjU3LDY2LjY1Ny02Ni42NTcsMTc0LjcwNSwwLDI0MS4zNDUNCgljNjYuNjU3LDY2LjY1NywxNzQuNzIsNjYuNjU3LDI0MS4zNDUsMGwxNTYuMDY0LTE1Ni4wMzJMMzY0LjQzNywyMjMuMDI2eiIvPg0KPHBvbHlnb24gc3R5bGU9ImZpbGw6I0NDRDFEOTsiIHBvaW50cz0iMTMwLjYyMywxNDAuMDU3IDIxMy41OTIsMjIzLjAyNiAyODguOTk4LDE0Ny41ODggMjA2LjAzLDY0LjYzNSAiLz4NCjxnPg0KCTxwYXRoIHN0eWxlPSJmaWxsOiNBQUIyQkQ7IiBkPSJNNDY2LjIxOSwyMTkuMjQ1YzEuOTM4LTEuOTM4LDMuMTI1LTQuNjA5LDMuMTI1LTcuNTYyYzAtNS44OTEtNC43ODEtMTAuNjU2LTEwLjY1Ni0xMC42NTYNCgkJYy0yLjkzOCwwLTUuNjI1LDEuMTg4LTcuNTYyLDMuMTI1bC0zMC4xNTYsMzAuMTU2bDAsMGMtMS45MzgsMS45MzgtMy4xMjUsNC41OTQtMy4xMjUsNy41NDZjMCw1Ljg5MSw0Ljc4MSwxMC42NzIsMTAuNjU2LDEwLjY3Mg0KCQljMi45NjksMCw1LjYyNS0xLjIwMyw3LjU2Mi0zLjE0MUw0NjYuMjE5LDIxOS4yNDV6Ii8+DQoJPHBhdGggc3R5bGU9ImZpbGw6I0FBQjJCRDsiIGQ9Ik01MDguODc1LDI2NC40OGMtMi4wNjItMi4wNzgtNC44MTItMy4xMjUtNy41MzEtMy4xMjVsMCwwaC00Mi42NTZsMCwwDQoJCWMtMi43MTksMC01LjQ2OSwxLjA0Ny03LjUzMSwzLjEyNWMtNC4xODgsNC4xNzItNC4xODgsMTAuOTIyLDAsMTUuMDc4YzIuMDYyLDIuMDk0LDQuODEyLDMuMTI1LDcuNTMxLDMuMTI1aDQyLjY1Ng0KCQljMi43MTksMCw1LjQ2OS0xLjAzMSw3LjUzMS0zLjEyNUM1MTMuMDYzLDI3NS40MDIsNTEzLjA2MywyNjguNjUyLDUwOC44NzUsMjY0LjQ4eiIvPg0KCTxwYXRoIHN0eWxlPSJmaWxsOiNBQUIyQkQ7IiBkPSJNNDA1Ljg3NSwxNjEuNDc5Yy00LjE1Ni00LjE3Mi0xMC45MDYtNC4xNzItMTUuMDYyLDBjLTIuMDk0LDIuMDc4LTMuMTI1LDQuODEyLTMuMTI1LDcuNTQ3djQyLjY1Nw0KCQljMCwyLjcxOSwxLjAzMSw1LjQ2OSwzLjEyNSw3LjUzMWM0LjE1Niw0LjE4OCwxMC45MDYsNC4xODgsMTUuMDYyLDBjMi4wOTQtMi4wNjIsMy4xMjUtNC44MTIsMy4xMjUtNy41MzFsMCwwdi00Mi42NTdsMCwwDQoJCUM0MDksMTY2LjI5MSw0MDcuOTY5LDE2My41NTcsNDA1Ljg3NSwxNjEuNDc5eiIvPg0KCTxwYXRoIHN0eWxlPSJmaWxsOiNBQUIyQkQ7IiBkPSJNMzA3Ljg3Myw2MC44NjhMMzA3Ljg3Myw2MC44NjhjMS45MzgtMS45MzgsMy4xMjYtNC41OTQsMy4xMjYtNy41MzENCgkJYzAtNS45MDYtNC43ODItMTAuNjcyLTEwLjY4OC0xMC42NzJjLTIuOTM4LDAtNS41OTQsMS4yMDMtNy41MzEsMy4xMjV2LTAuMDE2bC0zMC4xNTYsMzAuMTg4bDAsMA0KCQljLTEuOTM4LDEuOTIyLTMuMTI1LDQuNTk0LTMuMTI1LDcuNTMxYzAsNS44OTEsNC43ODEsMTAuNjcyLDEwLjY1NiwxMC42NzJjMi45MzgsMCw1LjYyNS0xLjIwMyw3LjUzMS0zLjEyNWwwLDBMMzA3Ljg3Myw2MC44Njh6Ig0KCQkvPg0KCTxwYXRoIHN0eWxlPSJmaWxsOiNBQUIyQkQ7IiBkPSJNMzUwLjUzMSwxMDYuMTE5Yy0yLjA5NC0yLjA3OC00LjgxMi0zLjEyNS03LjUzMS0zLjEyNWwwLDBoLTQyLjY1N2wwLDANCgkJYy0yLjc1LDAtNS40NjksMS4wNDctNy41NjIsMy4xMjVjLTQuMTU2LDQuMTcyLTQuMTU2LDEwLjkyMiwwLDE1LjA5NGMyLjA5NCwyLjA3OCw0LjgxMiwzLjEyNSw3LjU2MiwzLjEyNUgzNDMNCgkJYzIuNzE5LDAsNS40MzgtMS4wNDcsNy41MzEtMy4xMjVDMzU0LjY4NywxMTcuMDQxLDM1NC42ODcsMTEwLjI5MSwzNTAuNTMxLDEwNi4xMTl6Ii8+DQoJPHBhdGggc3R5bGU9ImZpbGw6I0FBQjJCRDsiIGQ9Ik0yNDcuNTMsMy4xMThjLTQuMTU2LTQuMTU3LTEwLjkwNi00LjE1Ny0xNS4wOTQsMGMtMi4wNjIsMi4wOTQtMy4xMjUsNC44MTIsMy4xMjUsNy41NjJ2NDIuNjI1DQoJCWMwLDIuNzUsMS4wNjIsNS40NjksMy4xMjUsNy41NjJjNC4xODgsNC4xNTcsMTAuOTM4LDQuMTU3LDE1LjA5NCwwYzIuMDk0LTIuMDk0LDMuMTI1LTQuODEyLDMuMTI1LTcuNTQ3bDAsMFYxMC42NjVsMCwwDQoJCUMyNTAuNjU1LDcuOTMxLDI0OS42MjMsNS4yMTIsMjQ3LjUzLDMuMTE4eiIvPg0KPC9nPg0KPHBvbHlnb24gc3R5bGU9ImZpbGw6I0NDRDFEOTsiIHBvaW50cz0iNDQ3LjQwNiwzMDUuOTk1IDM2NC40MzcsMjIzLjAyNiAyODguOTk4LDI5OC40MzMgMzcxLjk2OCwzODEuNDAzICIvPg0KPC9zdmc+') no-repeat center center;
            background-size: cover;
            cursor: grab;
            z-index: 1000;
            display: none; /* Start hidden */
        }

        #magnet-popup {
            display: none;
            position: fixed;
            bottom: 80px;
            right: 20px;
            width: 300px;
            max-height: 400px;
            background: white;
            border: 1px solid #ccc;
            box-shadow: 0px 0px 10px rgba(0,0,0,0.1);
            overflow-y: auto;
            z-index: 1001;
            padding: 10px;
        }

        #select-options {
            display: flex;
            justify-content: space-between;
            margin-bottom: 10px;
        }

        .button-group {
            display: flex;
            gap: 3px; /* Adjust this value to change button spacing */
        }

        button {
            cursor: pointer;
            padding: 4px 8px; /* Adjust padding for button size */
            border: none;
            border-radius: 3px;
            font-size: 12px; /* Adjust font size */
        }

        #select-all-btn {
            background: #4CAF50;
            color: white;
        }

        #select-none-btn {
            background: #f44336;
            color: white;
        }

        #copy-selected-btn {
            background: #2196F3;
            color: white;
            margin-left: auto; /* Move to the right */
        }

        #copy-feedback {
            font-size: 12px;
            color: green;
            margin-bottom: 0px; /* Space between feedback and links */
            text-align: center;
            display: none;
            font-weight: normal;
        }

        .magnet-count {
            text-align: left; /* Align the count to the left */
            font-size: 12px; /* Adjust the text size */
            color: #666; /* Set the color */
            margin-top: 2px; /* Add spacing above the count */
            margin-bottom: 5px; /* Add spacing below the count */
        }

        .magnet-item {
            display: flex;
            align-items: center; /* Center-align the checkbox and label */
            margin: 0; /* Reset margins */
            font-family: sans-serif; /* Unified font */
            font-size: 12px; /* Adjust text size */
            font-weight: normal; /* Ensure the text is not bold */
            text-decoration: none;
            color: #333; /* Unified color */
            white-space: nowrap; /* Prevent line breaking */
        }

        .magnet-checkbox {
            appearance: auto; /* Ensure the checkbox retains its native appearance */
            margin-right: 8px; /* Space between checkbox and label */
            width: 16px; /* Optional: Set a consistent width */
            height: 16px; /* Optional: Set a consistent height */
            vertical-align: middle; 
        }

        .magnet-item label {
            margin-left: 2px; /* Space between checkbox and link text */
            font-family: sans-serif; /* Set desired font */
            font-size: 12px; /* Unified size */
            font-weight: normal; /* Ensure normal weight */
            color: #333; /* Unified color */
            white-space: nowrap; /* Prevent line breaking */
            line-height: 16px; /* Match the checkbox height */
        }
    `);

    // Create the floating icon
    const icon = document.createElement('div');
    icon.id = 'magnet-icon';
    document.body.appendChild(icon);

    // Create the popup
    const popup = document.createElement('div');
    popup.id = 'magnet-popup';
    popup.innerHTML = `
        <div id="select-options" class="button-group">
            <button id="select-all-btn">All</button>
            <button id="select-none-btn">None</button>
            <div id="copy-feedback" style="flex: 1; text-align: center; display: none;">Links copied!</div>
            <button id="copy-selected-btn">Copy</button>
        </div>
        <div class="magnet-count">Counts: <span id="magnet-count">0</span></div>
        <div id="magnet-list"></div>
    `;
    document.body.appendChild(popup);

    // Extract magnet links
    function extractMagnetLinks() {
        const links = new Set();
        document.querySelectorAll('a[href^="magnet:?xt="]').forEach(link => {
            if (link.href.startsWith('magnet:?xt=')) {
                links.add(link.href);
            }
        });

        const magnetList = document.getElementById('magnet-list');
        magnetList.innerHTML = ''; // Clear existing content

        Array.from(links).forEach((magnet, index) => {
            const item = document.createElement('div');
            item.className = 'magnet-item';

            item.innerHTML = `
                <input type="checkbox" class="magnet-checkbox" id="magnet-${index}" data-magnet="${magnet}" checked>
                <label for="magnet-${index}" title="${magnet}">${magnet}</label>
            `;

            magnetList.appendChild(item);
        });

        const linkCount = links.size;
        document.getElementById('magnet-count').textContent = linkCount;

        // Show the icon only if links are found
        if (linkCount > 0) {
            icon.style.display = 'block';
        } else {
            icon.style.display = 'none';
        }
    }

    // Handle Select All button
    document.getElementById('select-all-btn').addEventListener('click', () => {
        document.querySelectorAll('.magnet-checkbox').forEach(cb => cb.checked = true);
    });

    // Handle Select None button
    document.getElementById('select-none-btn').addEventListener('click', () => {
        document.querySelectorAll('.magnet-checkbox').forEach(cb => cb.checked = false);
    });

    // Copy selected magnets
    document.getElementById('copy-selected-btn').addEventListener('click', () => {
        const selectedLinks = Array.from(document.querySelectorAll('.magnet-checkbox:checked'))
            .map(cb => cb.dataset.magnet)
            .join('\n');

        if (selectedLinks) {
            navigator.clipboard.writeText(selectedLinks).then(() => {
                const feedback = document.getElementById('copy-feedback');
                feedback.style.display = 'block';
                setTimeout(() => feedback.style.display = 'none', 2500);
            }).catch(err => {
                console.error('Failed to copy text: ', err);
            });
        }
    });

    // Popup visibility state
    let isPopupVisible = false;

    // Show or hide popup on icon click
    icon.addEventListener('click', () => {
        isPopupVisible = !isPopupVisible;
        popup.style.display = isPopupVisible ? 'block' : 'none';
        if (isPopupVisible) {
            extractMagnetLinks();
            // Default to select all links and copy them
            document.querySelectorAll('.magnet-checkbox').forEach(cb => cb.checked = true);
            document.getElementById('copy-selected-btn').click();
        }
    });

    // Make the icon draggable
    let isDragging = false;
    let offsetX, offsetY;

    icon.addEventListener('mousedown', (e) => {
        isDragging = true;
        offsetX = e.clientX - parseInt(window.getComputedStyle(icon).left || '0');
        offsetY = e.clientY - parseInt(window.getComputedStyle(icon).top || '0');
        icon.style.cursor = 'grabbing';
        document.addEventListener('mousemove', onMouseMove);
        document.addEventListener('mouseup', onMouseUp);
    });

    function onMouseMove(e) {
        if (isDragging) {
            const newX = e.clientX - offsetX;
            const newY = e.clientY - offsetY;

            // Keep icon within viewport bounds
            const maxX = window.innerWidth - icon.offsetWidth;
            const maxY = window.innerHeight - icon.offsetHeight;

            icon.style.left = Math.min(Math.max(0, newX), maxX) + 'px';
            icon.style.top = Math.min(Math.max(0, newY), maxY) + 'px';
        }
    }

    function onMouseUp() {
        isDragging = false;
        icon.style.cursor = 'grab';
        document.removeEventListener('mousemove', onMouseMove);
        document.removeEventListener('mouseup', onMouseUp);
    }

    // Initial extraction
    extractMagnetLinks();
})();