NYAA Magnet Extractor with Filter

Extract magnet links from sukebei.nyaa.si with filtering, (de)select-all and single-click init

Dovrai installare un'estensione come Tampermonkey, Greasemonkey o Violentmonkey per installare questo script.

Dovrai installare un'estensione come Tampermonkey o Violentmonkey per installare questo script.

Dovrai installare un'estensione come Tampermonkey o Violentmonkey per installare questo script.

Dovrai installare un'estensione come Tampermonkey o Userscripts per installare questo script.

Dovrai installare un'estensione come ad esempio Tampermonkey per installare questo script.

Dovrai installare un gestore di script utente per installare questo script.

(Ho già un gestore di script utente, lasciamelo installare!)

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

(Ho già un gestore di stile utente, lasciamelo installare!)

// ==UserScript==
// @name         NYAA Magnet Extractor with Filter
// @namespace    http://tampermonkey.net/
// @version      1.0
// @description  Extract magnet links from sukebei.nyaa.si with filtering, (de)select-all and single-click init
// @author       sauterne
// @match        https://sukebei.nyaa.si/*
// @match        https://nyaa.si/*
// @grant        GM_addStyle
// @grant        GM_setClipboard
// @license      GPL-3.0-or-later
// ==/UserScript==

(function () {
    'use strict';

    /* ----------  Styles  ---------- */
    GM_addStyle(`
        #magnetHelperPanel {
            background:#333;
            border:1px solid #555;
            border-radius:8px;
            padding:15px;
            margin-bottom:12px;
            color:#eee;
            font-size:14px;
        }
        #magnetHelperPanel input[type="text"],
        #magnetHelperPanel textarea{
            width:100%;
            padding:6px 8px;
            margin:6px 0 10px;
            border:1px solid #666;
            border-radius:4px;
            background:#444;
            color:#eee;
        }
        #magnetHelperPanel button{
            padding:6px 12px;
            margin-right:8px;
            border:none;
            border-radius:4px;
            cursor:pointer;
            background:#007bff;
            color:#fff;
        }
        #magnetHelperPanel button:hover{ background:#0056b3; }
        .magnet-helper-highlight{ background:#556B2F !important; }
        .magnet-helper-checkbox{ margin-right:6px; vertical-align:middle; }
        #magnetHelperTriggerBtn{
            position:fixed;
            bottom:20px; right:20px;
            z-index:9999;
            padding:10px 15px;
            background:#28a745;
            color:#fff;
            border:none;
            border-radius:5px;
            cursor:pointer;
            box-shadow:0 2px 5px rgba(0,0,0,0.3);
        }
        #magnetHelperTriggerBtn:hover{ background:#218838; }
        #magnetHelperPanel .row{margin-bottom:8px;}
        #magnetHelperPanel label{margin-left:4px;}
    `);

    /* ----------  Globals  ---------- */
    let checkboxesAdded = false;
    let panelAdded     = false;
    let torrentRows    = [];

    /* ----------  Elements ---------- */
    const triggerBtn = document.createElement('button');
    triggerBtn.id = 'magnetHelperTriggerBtn';
    triggerBtn.textContent = 'Filter & Extract';
    document.body.appendChild(triggerBtn);

    let panel, searchInput, searchBtn, extractBtn, copyBtn,
        selectAllBox, resultsArea;

    /* ----------  Helper functions ---------- */
    function addCheckboxes() {
        if (checkboxesAdded) return;

        const tableBody = document.querySelector('.torrent-list > tbody');
        if (!tableBody) return;

        torrentRows = Array.from(tableBody.querySelectorAll('tr'));

        torrentRows.forEach(row => {
            // 跳过已加过的行
            if (row.querySelector('.magnet-helper-checkbox')) return;

            const cb = document.createElement('input');
            cb.type = 'checkbox';
            cb.className = 'magnet-helper-checkbox';
            const firstTd = row.querySelector('td');
            firstTd && firstTd.prepend(cb);
        });

        checkboxesAdded = true;
    }

    function buildPanel() {
        if (panelAdded) return;

        /* --- 容器插入在表格上方 --- */
        panel = document.createElement('div');
        panel.id = 'magnetHelperPanel';
        panel.innerHTML = `
            <div class="row">
                <input type="checkbox" id="magnetSelectAll"><label for="magnetSelectAll">Select All</label>
            </div>
            <div class="row">
                <input type="text" id="magnetSearchInput" placeholder="Enter keyword (e.g., Deadmau)">
                <button id="magnetSearchBtn">Search & Auto-Check</button>
            </div>
            <div class="row">
                <button id="magnetExtractBtn">Extract Selected</button>
                <button id="magnetCopyBtn" style="display:none;">Copy All</button>
            </div>
            <textarea id="magnetResultTextarea" readonly placeholder="Extracted magnet links will appear here..."></textarea>
        `;

        /* 把面板插在 torrent 列表之前 */
        const table = document.querySelector('.torrent-list');
        table.parentNode.insertBefore(panel, table);

        /* 缓存内部节点 */
        searchInput   = panel.querySelector('#magnetSearchInput');
        searchBtn     = panel.querySelector('#magnetSearchBtn');
        extractBtn    = panel.querySelector('#magnetExtractBtn');
        copyBtn       = panel.querySelector('#magnetCopyBtn');
        resultsArea   = panel.querySelector('#magnetResultTextarea');
        selectAllBox  = panel.querySelector('#magnetSelectAll');

        /* --- 事件绑定 --- */
        searchBtn.addEventListener('click', handleSearch);
        searchInput.addEventListener('keypress', e => { if (e.key === 'Enter') handleSearch(); });
        extractBtn.addEventListener('click', handleExtract);
        copyBtn.addEventListener('click', handleCopy);
        selectAllBox.addEventListener('change', toggleSelectAll);

        panelAdded = true;
    }

    function toggleSelectAll() {
        const checked = selectAllBox.checked;
        torrentRows.forEach(r => {
            const cb = r.querySelector('.magnet-helper-checkbox');
            if (cb) cb.checked = checked;
            r.classList.toggle('magnet-helper-highlight', checked);
        });
    }

    function handleSearch() {
        const term = searchInput.value.toLowerCase().trim();
        torrentRows.forEach(r => {
            const cb = r.querySelector('.magnet-helper-checkbox');
            if (!cb) return;

            /* 获取标题文本 */
            let nameCell = r.querySelector('td[colspan="2"]') || r.querySelectorAll('td')[1];
            const txt = (nameCell?.innerText || '').toLowerCase();

            const hit = term && txt.includes(term);
            cb.checked = hit;
            r.classList.toggle('magnet-helper-highlight', hit);
        });
    }

    function handleExtract() {
        const links = [];
        torrentRows.forEach(r => {
            const cb = r.querySelector('.magnet-helper-checkbox');
            if (cb && cb.checked) {
                const a = r.querySelector('a[href^="magnet:"]');
                a && links.push(a.href);
            }
        });
        resultsArea.value = links.join('\n');
        copyBtn.style.display = links.length ? 'inline-block' : 'none';
        alert(links.length ? `Extracted ${links.length} links.` : 'No links selected.');
    }

    function handleCopy() {
        GM_setClipboard(resultsArea.value);
        alert('Copied to clipboard!');
    }

    /* ----------  Trigger button ---------- */
    triggerBtn.addEventListener('click', () => {
        /* 第一次点:先加复选框 + 面板;之后只切换显示状态 */
        if (!checkboxesAdded) addCheckboxes();
        if (!panelAdded)      buildPanel();

        /* 刷新行 cache(如翻页后) */
        const body = document.querySelector('.torrent-list > tbody');
        if (body) torrentRows = Array.from(body.querySelectorAll('tr'));

        /* toggle 显示 */
        panel.style.display = (panel.style.display === 'none' || !panel.style.display) ? 'block' : 'none';
    });
})();