Adds checkboxes and magnet link extraction functionality to 1337x.to search r###lts
// ==UserScript== // @name 1337x Magnet Link Fetcher // @version 1.8 // @description Adds checkboxes and magnet link extraction functionality to 1337x.to search r###lts // @author neokyuubi // @match *://1337x.to/search/* // @match *://www.1337x.to/search/* // @namespace https://github.com/neokyuubi/1337x-Magnet-Link-Fetcher // @icon https://1337x.to/favicon.ico // @grant GM_xmlhttpRequest // @run-at document-end // ==/UserScript== (function() { 'use strict'; // Only run on search pages if (!window.location.href.includes('/search/') && !window.location.href.includes('/category/') && !window.location.href.includes('/popular/') && !window.location.href.includes('/top-100')) { return; } // Variables for shift-click functionality let lastChecked = null; let shiftPressed = false; // Track shift key state document.addEventListener('keydown', function(e) { if (e.key === 'Shift') { shiftPressed = true; } }); document.addEventListener('keyup', function(e) { if (e.key === 'Shift') { shiftPressed = false; } }); // Add CSS for new elements const style = document.createElement('style'); style.textContent = ` .checkbox-column { width: 30px; text-align: center; } .magnet-column { width: 100px; text-align: center; } .action-buttons { position: fixed; bottom: 20px; right: 20px; display: flex; flex-direction: column; gap: 10px; z-index: 9999; } .action-button { padding: 10px 15px; background: #4CAF50; color: white; border: none; border-radius: 4px; cursor: pointer; box-shadow: 0 2px 5px rgba(0,0,0,0.3); font-weight: bold; } .action-button:disabled { background: #cccccc; cursor: not-allowed; } .magnet-link { cursor: pointer; color: #4CAF50; font-weight: bold; } .magnet-link:hover { text-decoration: underline; } .copy-success { color: green; transition: opacity 1s; } `; document.head.appendChild(style); // Find the table containing search r###lts const table = document.querySelector('.table-list') || document.querySelector('table.table-list'); if (!table) return; // Add header columns for checkbox and magnet const headerRow = table.querySelector('thead tr'); const checkboxHeader = document.createElement('th'); checkboxHeader.className = 'checkbox-column'; checkboxHeader.textContent = ''; headerRow.insertBefore(checkboxHeader, headerRow.firstChild); const magnetHeader = document.createElement('th'); magnetHeader.className = 'magnet-column'; magnetHeader.textContent = 'Magnet'; headerRow.appendChild(magnetHeader); // Add checkboxes and magnet cells to each row const rows = table.querySelectorAll('tbody tr'); rows.forEach(row => { // Add checkbox column const checkboxCell = document.createElement('td'); checkboxCell.className = 'checkbox-column'; const checkbox = document.createElement('input'); checkbox.type = 'checkbox'; // Implement shift-click functionality checkbox.addEventListener('click', function(e) { if (shiftPressed && lastChecked && lastChecked !== this) { // Get all checkboxes const checkboxes = Array.from(document.querySelectorAll('.checkbox-column input[type="checkbox"]')); // Find indices of current and last checked boxes const startIndex = checkboxes.indexOf(this); const endIndex = checkboxes.indexOf(lastChecked); // Determine range to check (works in both directions) const start = Math.min(startIndex, endIndex); const end = Math.max(startIndex, endIndex); // Check all checkboxes in the range for (let i = start; i <= end; i++) { checkboxes[i].checked = this.checked; } } // Update lastChecked reference lastChecked = this; }); checkboxCell.appendChild(checkbox); row.insertBefore(checkboxCell, row.firstChild); // Add magnet column const magnetCell = document.createElement('td'); magnetCell.className = 'magnet-column'; magnetCell.dataset.torrentUrl = row.querySelector('a[href^="/torrent/"]').href; row.appendChild(magnetCell); }); // Create sticky action buttons const actionButtons = document.createElement('div'); actionButtons.className = 'action-buttons'; const fetchButton = document.createElement('button'); fetchButton.className = 'action-button'; fetchButton.textContent = 'Fetch Selected Links'; fetchButton.onclick = fetchSelectedMagnetLinks; const copyAllButton = document.createElement('button'); copyAllButton.className = 'action-button'; copyAllButton.textContent = 'Copy All Links'; copyAllButton.onclick = copyAllMagnetLinks; copyAllButton.disabled = true; actionButtons.appendChild(fetchButton); actionButtons.appendChild(copyAllButton); document.body.appendChild(actionButtons); // Function to fetch magnet links for selected torrents function fetchSelectedMagnetLinks() { const checkedRows = document.querySelectorAll('.checkbox-column input[type="checkbox"]:checked'); if (checkedRows.length === 0) { alert('No torrents selected! Please select at least one torrent.'); return; } let completedRequests = 0; let totalRequests = checkedRows.length; fetchButton.disabled = true; fetchButton.textContent = `Fetching... (0/${totalRequests})`; checkedRows.forEach(checkbox => { const row = checkbox.closest('tr'); const magnetCell = row.querySelector('.magnet-column'); const torrentUrl = magnetCell.dataset.torrentUrl; // Skip if we already fetched this link if (magnetCell.innerHTML !== '') { completedRequests++; updateFetchButtonStatus(completedRequests, totalRequests); return; } // Fetch the torrent page GM_xmlhttpRequest({ method: 'GET', url: torrentUrl, onload: function(response) { const parser = new DOMParser(); const doc = parser.parseFromString(response.responseText, 'text/html'); // Extract magnet link const magnetLink = doc.querySelector('a[href^="magnet:"]'); if (magnetLink) { const magnetUrl = magnetLink.href; // Create copy button for the magnet link const copyLink = document.createElement('span'); copyLink.className = 'magnet-link'; copyLink.textContent = 'Copy'; copyLink.dataset.magnetUrl = magnetUrl; copyLink.onclick = function() { navigator.clipboard.writeText(magnetUrl).then(() => { const originalText = copyLink.textContent; copyLink.textContent = 'Copied!'; copyLink.classList.add('copy-success'); setTimeout(() => { copyLink.textContent = originalText; copyLink.classList.remove('copy-success'); }, 1000); }); }; magnetCell.innerHTML = ''; magnetCell.appendChild(copyLink); } else { magnetCell.textContent = 'Not found'; } completedRequests++; updateFetchButtonStatus(completedRequests, totalRequests); }, onerror: function() { magnetCell.textContent = 'Error'; completedRequests++; updateFetchButtonStatus(completedRequests, totalRequests); } }); }); } // Update fetch button status function updateFetchButtonStatus(completed, total) { fetchButton.textContent = `Fetching... (${completed}/${total})`; if (completed === total) { fetchButton.textContent = 'Fetch Selected Links'; fetchButton.disabled = false; copyAllButton.disabled = false; } } // Function to copy all magnet links function copyAllMagnetLinks() { const magnetLinks = []; const magnetCells = document.querySelectorAll('.magnet-link'); magnetCells.forEach(link => { if (link.dataset.magnetUrl) { magnetLinks.push(link.dataset.magnetUrl); } }); if (magnetLinks.length === 0) { alert('No magnet links found!'); return; } navigator.clipboard.writeText(magnetLinks.join('\n')).then(() => { const originalText = copyAllButton.textContent; copyAllButton.textContent = 'Copied All Links!'; setTimeout(() => { copyAllButton.textContent = originalText; }, 1000); }); } })();