// ==UserScript== // @name 磁力链接增强器 // @namespace http://tampermonkey.net/ // @version 1.0.0 // @description 识别、复制、解析磁力链接,支持一键复制hash和转换为下载链接 // @author YourName // @match *://*/* // @grant GM_setClipboard // @grant GM_xmlhttpRequest // @connect * // @run-at document-idle // @license MIT // ==/UserScript== (function() { 'use strict'; // ==================== 配置 ==================== const CONFIG = { // 识别关键字(可自定义添加更多) keywords: ['magnet:', 'magnet:?', 'xt=urn:btih:'], // 样式配置 style: { buttonBg: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)', buttonHoverBg: 'linear-gradient(135deg, #764ba2 0%, #667eea 100%)', buttonColor: '#fff', buttonRadius: '4px', buttonFontSize: '12px', buttonPadding: '4px 10px', badgeBg: '#ff6b6b', badgeColor: '#fff' } }; // ==================== 工具函数 ==================== /** * 从磁力链接提取 info hash */ function extractInfoHash(magnet) { const match = magnet.match(/xt=urn:btih:([a-fA-F0-9]{32,40})/i); return match ? match[1].toUpperCase() : null; } /** * 从磁力链接提取显示名称 */ function extractName(magnet) { const match = magnet.match(/dn=([^&]+)/i); return match ? decodeURIComponent(match[1]) : '未知文件'; } /** * 复制到剪贴板 */ function copyToClipboard(text, button) { GM_setClipboard(text); const originalText = button.textContent; button.textContent = '已复制 ✓'; button.style.background = '#51cf66'; setTimeout(() => { button.textContent = originalText; button.style.background = ''; }, 1500); } /** * 显示通知 */ function showNotification(message, type = 'success') { const notification = document.createElement('div'); notification.textContent = message; notification.style.cssText = ` position: fixed; top: 20px; right: 20px; padding: 12px 24px; background: ${type === 'success' ? '#51cf66' : type === 'error' ? '#ff6b6b' : '#339af0'}; color: white; border-radius: 8px; font-size: 14px; z-index: 999999; box-shadow: 0 4px 12px rgba(0,0,0,0.15); animation: slideIn 0.3s ease; `; const style = document.createElement('style'); style.textContent = ` @keyframes slideIn { from { transform: translateX(100%); opacity: 0; } to { transform: translateX(0); opacity: 1; } } `; document.head.appendChild(style); document.body.appendChild(notification); setTimeout(() => { notification.style.animation = 'slideIn 0.3s ease reverse'; setTimeout(() => notification.remove(), 300); }, 2000); } /** * 格式化文件大小 */ function formatSize(bytes) { if (!bytes) return '未知'; const units = ['B', 'KB', 'MB', 'GB', 'TB']; let i = 0; while (bytes >= 1024 && i < units.length - 1) { bytes /= 1024; i++; } return `${bytes.toFixed(2)} ${units[i]}`; } // ==================== 样式注入 ==================== function injectStyles() { const css = ` /* 磁力链接容器样式 */ .magnet-enhance-wrapper { display: inline-flex; align-items: center; gap: 8px; flex-wrap: wrap; margin: 4px 0; } /* 原始磁力链接样式 */ .magnet-enhance-link { color: #3b82f6 !important; text-decoration: none !important; border-bottom: 1px dashed #3b82f6 !important; word-break: break-all; transition: all 0.2s; } .magnet-enhance-link:hover { color: #2563eb !important; border-bottom-color: #2563eb !important; background: rgba(59, 130, 246, 0.1); } /* 按钮通用样式 */ .magnet-btn { display: inline-flex; align-items: center; gap: 4px; padding: ${CONFIG.style.buttonPadding}; font-size: ${CONFIG.style.buttonFontSize}; color: ${CONFIG.style.buttonColor}; background: ${CONFIG.style.buttonBg}; border: none; border-radius: ${CONFIG.style.buttonRadius}; cursor: pointer; transition: all 0.2s; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; white-space: nowrap; } .magnet-btn:hover { background: ${CONFIG.style.buttonHoverBg}; transform: translateY(-1px); box-shadow: 0 2px 8px rgba(102, 126, 234, 0.4); } .magnet-btn:active { transform: translateY(0); } /* 按钮图标 */ .magnet-btn svg { width: 14px; height: 14px; fill: currentColor; } /* 按钮组 */ .magnet-btn-group { display: inline-flex; gap: 4px; } /* Hash 显示标签 */ .magnet-hash-badge { display: inline-block; padding: 2px 8px; background: rgba(255, 107, 107, 0.15); color: #ff6b6b; border-radius: 4px; font-family: monospace; font-size: 11px; max-width: 120px; overflow: hidden; text-overflow: ellipsis; } /* 展开/收起区块 */ .magnet-expand-panel { width: 100%; margin-top: 8px; padding: 12px; background: #f8f9fa; border-radius: 8px; border: 1px solid #e9ecef; } .magnet-expand-header { display: flex; align-items: center; justify-content: space-between; cursor: pointer; margin-bottom: 8px; } .magnet-expand-title { font-weight: 600; color: #495057; } .magnet-expand-icon { transition: transform 0.2s; } .magnet-expand-icon.open { transform: rotate(180deg); } .magnet-expand-content { display: none; } .magnet-expand-content.show { display: block; } /* 信息行 */ .magnet-info-row { display: flex; align-items: center; gap: 12px; padding: 6px 0; border-bottom: 1px solid #e9ecef; font-size: 13px; } .magnet-info-row:last-child { border-bottom: none; } .magnet-info-label { color: #868e96; min-width: 70px; } .magnet-info-value { color: #212529; word-break: break-all; } /* 搜索链接 */ .magnet-search-link { color: #3b82f6; text-decoration: none; margin-right: 12px; } .magnet-search-link:hover { text-decoration: underline; } `; const style = document.createElement('style'); style.id = 'magnet-enhance-styles'; style.textContent = css; document.head.appendChild(style); } // ==================== SVG 图标 ==================== const icons = { copy: ``, hash: ``, download: ``, search: ``, chevron: ``, link: `` }; // ==================== 创建按钮 ==================== function createCopyButton(magnet, text = '复制链接') { const btn = document.createElement('button'); btn.className = 'magnet-btn magnet-copy-btn'; btn.innerHTML = `${icons.copy}${text}`; btn.addEventListener('click', (e) => { e.preventDefault(); e.stopPropagation(); copyToClipboard(magnet, btn); }); return btn; } function createHashButton(hash) { const btn = document.createElement('button'); btn.className = 'magnet-btn magnet-hash-btn'; btn.innerHTML = `${icons.hash}Hash`; btn.title = '复制 Info Hash'; btn.addEventListener('click', (e) => { e.preventDefault(); e.stopPropagation(); copyToClipboard(hash, btn); showNotification(`已复制 Hash: ${hash}`); }); return btn; } function createSearchButton(magnet) { const name = extractName(magnet); const hash = extractInfoHash(magnet); const btn = document.createElement('button'); btn.className = 'magnet-btn magnet-search-btn'; btn.innerHTML = `${icons.search}搜索`; btn.title = '在线搜索资源'; btn.addEventListener('click', (e) => { e.preventDefault(); e.stopPropagation(); const searchUrl = hash ? `https://itorrents.org/torrent/${hash}.torrent` : `https://www.google.com/search?q=${encodeURIComponent(name)}`; window.open(searchUrl, '_blank'); }); return btn; } // ==================== 处理纯文本磁力链接 ==================== function processTextNode(textNode) { const text = textNode.textContent; const magnetRegex = /magnet:\?[^<>\"\s]+/g; const matches = text.match(magnetRegex); if (!matches || matches.length === 0) return; const fragment = document.createDocumentFragment(); let lastIndex = 0; matches.forEach((magnet, index) => { const startIndex = text.indexOf(magnet, lastIndex); // 添加匹配前的文本 if (startIndex > lastIndex) { fragment.appendChild(document.createTextNode(text.substring(lastIndex, startIndex))); } // 创建磁力链接包装器 const wrapper = document.createElement('span'); wrapper.className = 'magnet-enhance-wrapper'; // 创建链接元素 const link = document.createElement('a'); link.className = 'magnet-enhance-link'; link.href = magnet; link.textContent = magnet.length > 60 ? magnet.substring(0, 57) + '...' : magnet; link.title = magnet; link.target = '_blank'; wrapper.appendChild(link); // 提取 hash 并显示 const hash = extractInfoHash(magnet); if (hash) { const badge = document.createElement('span'); badge.className = 'magnet-hash-badge'; badge.textContent = hash.substring(0, 8) + '...'; badge.title = '完整 Hash: ' + hash; wrapper.appendChild(badge); } // 添加按钮组 const btnGroup = document.createElement('span'); btnGroup.className = 'magnet-btn-group'; btnGroup.appendChild(createCopyButton(magnet, '复制')); if (hash) { btnGroup.appendChild(createHashButton(hash)); } btnGroup.appendChild(createSearchButton(magnet)); wrapper.appendChild(btnGroup); // 添加分隔符(如果不是最后一个) if (index < matches.length - 1) { const separator = document.createTextNode(' '); fragment.appendChild(wrapper); fragment.appendChild(separator); } else { fragment.appendChild(wrapper); } lastIndex = startIndex + magnet.length; }); // 添加剩余文本 if (lastIndex < text.length) { fragment.appendChild(document.createTextNode(text.substring(lastIndex))); } textNode.parentNode.replaceChild(fragment, textNode); } // ==================== 处理已有 href 的磁力链接 ==================== function processMagnetLinks() { const links = document.querySelectorAll('a[href^="magnet:"]'); links.forEach(link => { // 避免重复处理 if (link.dataset.magnetEnhanced) return; link.dataset.magnetEnhanced = 'true'; const magnet = link.getAttribute('href'); const hash = extractInfoHash(magnet); const name = extractName(magnet); // 更新显示文本 if (!link.textContent || link.textContent === magnet) { link.textContent = name !== '未知文件' ? name : (magnet.length > 50 ? magnet.substring(0, 47) + '...' : magnet); } link.classList.add('magnet-enhance-link'); // 创建包装器 const wrapper = document.createElement('span'); wrapper.className = 'magnet-enhance-wrapper'; link.parentNode.insertBefore(wrapper, link); wrapper.appendChild(link); // 添加 hash 标签 if (hash) { const badge = document.createElement('span'); badge.className = 'magnet-hash-badge'; badge.textContent = hash.substring(0, 8) + '...'; badge.title = '完整 Hash: ' + hash; wrapper.appendChild(badge); } // 添加按钮组 const btnGroup = document.createElement('span'); btnGroup.className = 'magnet-btn-group'; btnGroup.appendChild(createCopyButton(magnet, '复制')); if (hash) { btnGroup.appendChild(createHashButton(hash)); } btnGroup.appendChild(createSearchButton(magnet)); wrapper.appendChild(btnGroup); }); } // ==================== 主处理函数 ==================== function processPage() { injectStyles(); processMagnetLinks(); // 处理纯文本中的磁力链接 const walker = document.createTreeWalker( document.body, NodeFilter.SHOW_TEXT, { acceptNode: function(node) { // 跳过已有磁力链接的节点 if (node.parentNode.closest('.magnet-enhance-wrapper')) { return NodeFilter.FILTER_REJECT; } // 跳过 script 和 style if (['SCRIPT', 'STYLE', 'TEXTAREA', 'INPUT'].includes(node.parentNode.tagName)) { return NodeFilter.FILTER_REJECT; } // 检查是否包含磁力链接 if (CONFIG.keywords.some(kw => node.textContent.includes(kw))) { return NodeFilter.FILTER_ACCEPT; } return NodeFilter.FILTER_REJECT; } } ); const textNodes = []; while (walker.nextNode()) { textNodes.push(walker.currentNode); } textNodes.forEach(processTextNode); } // ==================== 初始化 ==================== function init() { // 等待 DOM 加载完成 if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', processPage); } else { // 延迟执行,确保页面渲染完成 setTimeout(processPage, 500); } // 监听动态加载的内容(MutationObserver) const observer = new MutationObserver((mutations) => { let shouldProcess = false; mutations.forEach(mutation => { if (mutation.addedNodes.length > 0) { shouldProcess = true; } }); if (shouldProcess) { setTimeout(processPage, 300); } }); observer.observe(document.body, { childList: true, subtree: true }); } // 启动 init(); })();