// ==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();
})();