您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Enhanced magnet link handling with copy, preview, and image URL features//Add buttons to copy magnet links and preview on magnet.pics, works on 1cili.com and other sites
// ==UserScript== // @name Magnet Copy & Preview // @namespace http://tampermonkey.net/ // @version 2025.09.12 // @description Enhanced magnet link handling with copy, preview, and image URL features//Add buttons to copy magnet links and preview on magnet.pics, works on 1cili.com and other sites // @author 庄引 // @icon data:image/gif;base64,iVBORw0KGgoAAAANSUhEUgAAAC4AAAAuCAMAAABgZ9sFAAAAwFBMVEX///8AAAD/XV2nxf//X1//YWGsy//k5ORlZWXx8fGoqKjIyMg4ODhtbW2pyP9ZWVlBQUGAgIC+vr5fX1+urq69RUUTExPq6ura2tqKioqRq910dHRPT0/4+PguLi63t7c3QVI9SFuVlZUmJiZGUmgbGxsXGyNYaIWgvfN7kbuKo9IOExxzh69jJSWenp5bIiJhc5Oz0/8tNUSVNDMlDg57REREGhrPTEw3FBTvV1ekNjbRhYX/oqN4LCyKMzPiU1PPfiQiAAACfElEQVRIiY2VC3eiMBCFcxUFxReCPKqoq7a17Wq3q6vW3W3//7/qTIKAEqpzDgcOfJnc3EwGIXRhWz66E+0nTVhAq0fXbbSDUUOIeIjRLXTAaWM5iX0D3kRfjGAKYcK5SYsp+mD+JjUzhEJMABHDugG3pQbbI1XNq0o8IeYkniIE4is0Od4ka+B6kyGuGmPBnTM/AsU4lO/i0t210BUNzi+C/sRUC6H5SnimRcLLCGY+0PPKaOVbw094hxRFZqkStNWTTZ5ztDEvk030YgGXn8LUEqVOT0/r9R/MhzkDhye+77Sj8JyuVom3zBwddoGA7sE9G5tNpWjJZ7RJ7lu80gavuEFjk/PSTuhq/S6Xm/ZW2eJK52OSJj2daeioK+xEbIPq2AXXG3rSiYdOIXeUeWJjzfXvB6LLBo9x1ykoycU5PsFj/Ts6FeNJMS0sme5oadNvZkt1aESAhVqnlgbXmzQyDl02coInVl5/1Jw0oj2uNzPbJgdLxjvwdXQ/qee0CFywls4zZlr6rP4pei8S/6mOc5Em/h7ZCek9JLhdoE9txsnV8UiJeYJ3SQ9Wqv6dfCseQtq4VJ/ydM145ZdntFjjWRo5hXlBVyrEt6PzNh/iV2LN+JImflX4KcCXu/r2G+NAvbFPdMXYFH4hkdzW+gJbagX9MPToEA0MRVf+9C5o2oaXDtO72v4g9xqr94Q+/r2EBR+mxzeiCal9DjabwbsSQoGhBqcSnkqaE1IkrFH5p+/XVBWHNOMpav9R1u/IuI80azLLKwqrzPKPsd3X0gFG5Zg2TH1EJOj4aRg1GrT/QGlTP0UwZA8Pu92W7+vvYRl25I7ht6x1WIp8AXxjKOd1DpCTAAAAAElFTkSuQmCC // @match *://u3c3.com/* // @match *://hjd2048.com/* // @match *://*.cctv10.cc/* // @match *://*.cctv12.cc/* // @match *://*.1cili.com/!* // @match *://sukebei.nyaa.si/* // @match *://btdig.com/* // @match *://whatslink.info* // @match *://btsow.pics/* // @match *://*.sis001.com/* // @grant GM_setClipboard // @grant GM_openInTab // @grant GM_xmlhttpRequest // @grant GM_addElement // @grant GM_addStyle // @run-at document-idle // @license MIT // ==/UserScript== (function () { 'use strict'; // Debug flag to reduce noisy logs in production const DEBUG = false; // Schedule DOM writes safely to avoid ResizeObserver loops const schedule = (callback) => { return requestAnimationFrame(() => { try { callback(); } catch (error) { console.error(error); } }); }; /** * 创建并添加HTML元素到指定父节点 * @param {string} tagName - HTML标签名称,默认为'button' * @param {string} innerHTML - 元素的内部HTML内容 * @param {Object} options - 要应用到元素上的属性 * @param {HTMLElement} parentNode - 父节点元素 * @param {boolean} flag - 是否添加到父节点末尾,true使用append,false使用prepend */ const addElement = (tagName = `button`, innerHTML, options, parentNode, flag = true) => { if (!parentNode) { console.warn('addElement: parentNode is null or undefined'); return; } const el = document.createElement(tagName); el.innerHTML = innerHTML; Object.assign(el, options); // 使用 requestAnimationFrame 来避免 ResizeObserver 循环问题 schedule(() => { try { parentNode[flag ? 'prepend' : 'append'](el); if (DEBUG) console.log(parentNode); } catch (error) { console.error('addElement error:', error); } }); }; /** * 监听并复制图片URL * @param {string} parentDiv - 父元素选择器 * @param {string} targetNode - 图片元素选择器 * @param {string} attribute - 要监听的属性名 * @param {string} displayDiv - 显示元素选择器 * @returns {boolean} 是否成功设置监听 */ const monitorAndCopyImageUrls = (parentDiv, targetNode, attribute, displayDiv) => { const searchDiv = document.querySelector(parentDiv); if (!searchDiv) { console.error(`找不到父元素: ${parentDiv}`); return false; } // 防抖函数,避免频繁更新 let debounceTimer; const debounce = (fn, delay) => { return function (...args) { clearTimeout(debounceTimer); debounceTimer = setTimeout(() => fn.apply(this, args), delay); }; }; // 处理图片URL的函数 const processImages = () => { try { const images = document.querySelectorAll(targetNode); if (images.length === 0) { return; } // 收集所有图片URL const urls = Array.from(images).map((img) => img[attribute]); const displayElement = document.querySelector(displayDiv); if (!displayElement) { return; } // 使用 DocumentFragment 来批量操作 DOM,减少重排 const fragment = document.createDocumentFragment(); // 创建或更新显示区域 let section = displayElement.querySelector('section'); if (!section) { section = document.createElement("section"); fragment.appendChild(section); } // 更新URL列表 section.innerHTML = ''; urls.forEach((url) => { const p = document.createElement("p"); p.textContent = url; section.appendChild(p); }); // 如果section是新创建的,添加到fragment if (!displayElement.querySelector('section')) { displayElement.appendChild(fragment); } // 添加或更新复制按钮 let copyButton = displayElement.querySelector('.copy-urls-button'); if (!copyButton) { // 使用 setTimeout 来延迟按钮创建,避免在 MutationObserver 回调中直接修改 DOM setTimeout(() => { addElement( 'button', '复制所有URL', { className: 'copy-urls-button', onclick: (e) => { try { const currentImages = document.querySelectorAll(targetNode); const currentUrls = Array.from(currentImages).map((img) => img[attribute]); GM_setClipboard(currentUrls.join("\n"), "text"); e.target.textContent = '已复制所有URL'; setTimeout(() => { e.target.textContent = '复制所有URL'; }, 3000); } catch (error) { console.error("复制到剪贴板时出错:", error); e.target.textContent = '复制失败'; setTimeout(() => { e.target.textContent = '复制所有URL'; }, 3000); } } }, displayElement ); }, 0); } } catch (error) { console.error("处理图片时出错:", error); } }; // 使用防抖处理图片更新 const debouncedProcessImages = debounce(processImages, 1000); // 创建MutationObserver监听DOM变化 const observer = new MutationObserver((mutations) => { const hasRelevantChanges = mutations.some((mutation) => { return ( (mutation.type === "attributes" && mutation.attributeName === attribute) || mutation.type === "childList" ); }); if (hasRelevantChanges) { // 使用 requestAnimationFrame 来避免 ResizeObserver 循环问题 schedule(() => { debouncedProcessImages(); }); } }); // 配置观察选项 const config = { childList: true, subtree: true, attributes: true, attributeFilter: [attribute], }; // 开始观察 observer.observe(searchDiv, config); // 初始处理 processImages(); return true; }; // Function to extract hash from magnet URL function extractHashFromMagnet(magnetUrl) { const btihMatch = magnetUrl.match(/urn:btih:([a-zA-Z0-9]+)/i); if (btihMatch && btihMatch[1]) { return btihMatch[1].toLowerCase(); } return ''; } class Drawer { static instance = null; constructor(options = {}) { this.direction = options.direction || 'left'; this.width = options.width || '300px'; this.title = options.title || 'Drawer'; this.init(); } init() { // 如果存在之前的实例,先移除它 if (Drawer.instance) { document.body.removeChild(Drawer.instance.drawer); } // Create drawer container this.drawer = document.createElement('div'); this.drawer.style.cssText = ` position: fixed; top: 0; ${this.direction}: 0; width: ${this.width}; height: 100vh; background: white; box-shadow: 0 0 10px rgba(0,0,0,0.1); transform: translateX(${this.direction === 'left' ? '-100%' : '100%'}); transition: transform 0.3s ease; z-index: 1000; `; // Create header const header = document.createElement('div'); header.style.cssText = ` display: flex; justify-content: space-between; align-items: center; padding: 16px; border-bottom: 1px solid #eee; `; // Create title const title = document.createElement('h5'); title.textContent = this.title; title.style.margin = '0'; // Create close button const closeBtn = document.createElement('button'); closeBtn.textContent = '×'; closeBtn.style.cssText = ` background: none; border: none; font-size: 24px; cursor: pointer; padding: 0; color: #666; `; closeBtn.onclick = () => this.close(); // Create content container this.content = document.createElement('div'); this.content.style.cssText = ` padding: 16px; height: calc(100vh - 60px); overflow-y: auto; `; // Assemble drawer header.appendChild(title); header.appendChild(closeBtn); this.drawer.appendChild(header); this.drawer.appendChild(this.content); document.body.appendChild(this.drawer); // 保存当前实例 Drawer.instance = this; } open() { this.drawer.style.transform = 'translateX(0)'; } close() { this.drawer.style.transform = `translateX(${this.direction === 'left' ? '-100%' : '100%'})`; // Clear content after animation completes setTimeout(() => { this.content.innerHTML = ''; }, 300); // Match the transition duration } setContent(content) { this.content.innerHTML = content; } } // Usage example function openDrawer(url, title, width = '40vw', direction = 'right') { // 如果已经存在drawer实例,先清空内容 const existingDrawer = document.querySelector('.drawer-container'); if (existingDrawer) { const content = existingDrawer.querySelector('.drawer-content'); if (content) { content.innerHTML = ''; } } const drawer = new Drawer({ direction: direction, width: `${width}`, title: `${title} ` }); drawer.setContent(`<iframe width='100%;' height='100%' src=${url}></iframe>`); drawer.open(); } // Special handling for 1cili.com function handle1CiliSite() { const magnetBoxes = document.querySelectorAll('.magnet-box'); if (magnetBoxes.length > 0) { // For each magnet box magnetBoxes.forEach(box => { const inputField = box.querySelector('#input-magnet'); if (!inputField) return; const magnetUrl = inputField.value; const hash = extractHashFromMagnet(magnetUrl); if (!hash) return; // Get title from page const pageTitle = document.querySelector('.magnet-title')?.textContent || 'Magnet Preview'; // Find the input-group-btn div where the existing buttons are const btnGroup = box.querySelector('.input-group-btn'); if (btnGroup) { // 使用 requestAnimationFrame 来避免 ResizeObserver 循环问题 requestAnimationFrame(() => { try { // Create a new preview button const previewBtn = document.createElement('a'); previewBtn.className = 'btn preview-button-1cili'; previewBtn.innerHTML = '<svg class="svg-icon"><use xlink:href="/assets/icons.svg#icon-search"></use></svg>'; previewBtn.title = '预览 Preview'; previewBtn.href = 'javascript:void(0);'; // Add click event for preview previewBtn.addEventListener('click', function (e) { e.preventDefault(); e.stopPropagation(); // Open preview in side panel openDrawer(`https://magnet.pics/m/${hash}`, pageTitle); }); // Add the button to the button group btnGroup.appendChild(previewBtn); } catch (error) { console.error('handle1CiliSite error:', error); } }); } }); } } /** * 处理常规网站的磁力链接功能 * @param {string} datalist - 要处理的元素列表选择器 * @param {string} hash - 包含磁力链接hash的元素选择器 * @param {string} title - 标题元素选择器 * @param {string} size - 文件大小元素选择器 * @param {string} date - 日期元素选择器 * @param {string} magnet - 磁力链接元素选择器 * @param {boolean} flag - 是否将按钮添加到元素末尾,true使用append,false使用prepend */ const handleRegularSites = (datalist, hash, title, size, date, magnet, flag) => { if (DEBUG) console.log(datalist, hash, title, size, date, magnet, flag); if (DEBUG) console.log(document.querySelectorAll(datalist)); //在磁力链接详情页面(.fa-magnet)也添加预览按钮 if (/[0-9a-fA-F]{40}/.test(window.location.href)) { // 使用 requestAnimationFrame 来避免 ResizeObserver 循环问题 schedule(() => { try { // 构建完整的磁力链接,包含标题、大小和日期信息 const link = `magnet:?xt=urn:btih:${window.location.href.match(/[0-9a-fA-F]{40}/)[0].toLowerCase()}&dn=${document.querySelector('tbody > tr:nth-child(5) > td:nth-child(2)').innerText}🔞Size=${document.querySelector('tbody > tr:nth-child(6) > td:nth-child(2)').innerText}🔞Date=${document.querySelector(' tbody > tr:nth-child(7) > td:nth-child(2)').innerText}`; document.querySelector('tbody > tr:nth-child(4) > td:nth-child(2) > div > a').textContent = link; // 添加复制按钮(幂等) const detailContainer = document.querySelector('.fa-magnet'); if (!detailContainer) return; if (!detailContainer.querySelector('.magnet-btn.copy-btn')) { addElement('a', '📋', { className: 'magnet-btn copy-btn', title: 'Copy Magnet Link', onclick: (e) => { e.preventDefault(); e.stopPropagation(); GM_setClipboard(decodeURIComponent(link)); }, }, detailContainer ) } // 添加预览按钮(幂等) if (!detailContainer.querySelector('.magnet-btn.preview-btn')) { addElement('a', '👁️', { className: 'magnet-btn preview-btn', title: 'Preview on magnet.pics', onclick: (e) => { e.preventDefault(); e.stopPropagation(); openDrawer(`https://magnet.pics/m/${window.location.href.match(/[0-9a-fA-F]{40}/)[0].toLowerCase()}`, document.querySelector('.fa-folder-open').innerText); }, }, detailContainer ) } } catch (error) { console.error('磁力链接详情页面处理错误:', error); } }); }; // 遍历所有匹配的元素 document.querySelectorAll(datalist).forEach((element, index) => { if (DEBUG) console.log(element); const hashElement = element.querySelector(hash); if (!hashElement) return; // 从链接中提取40位的磁力链接hash const hashMatch = hashElement.href.match(/[0-9a-fA-F]{40}/); if (!hashMatch) return; // 构建完整的磁力链接,包含标题、大小和日期信息 const link = `magnet:?xt=urn:btih:${hashMatch[0].toLowerCase()}&dn=${element.querySelector(title).innerText}🔞Size=${element.querySelector(size).innerText}🔞Date=${element.querySelector(date).innerText}`; // 使用 schedule 来避免 ResizeObserver 循环问题 schedule(() => { try { const magnetContainer = element.querySelector(magnet); if (!magnetContainer) return; magnetContainer.textContent = link; // 添加复制按钮(幂等) if (!magnetContainer.querySelector('.magnet-btn.copy-btn')) { addElement('a', '📋', { className: 'magnet-btn copy-btn', title: 'Copy Magnet Link', onclick: (e) => { e.preventDefault(); e.stopPropagation(); GM_setClipboard(decodeURIComponent(link)); }, }, magnetContainer ) } // 添加预览按钮(幂等) if (!magnetContainer.querySelector('.magnet-btn.preview-btn')) { addElement('a', '👁️', { className: 'magnet-btn preview-btn', title: 'Preview on magnet.pics', onclick: (e) => { e.preventDefault(); e.stopPropagation(); openDrawer(`https://magnet.pics/m/${hashMatch[0].toLowerCase()}`, element.querySelector(title).innerText); }, }, magnetContainer ) } } catch (error) { console.error('handleRegularSites error:', error); } }); }) } const isIncludes = (str) => window.location.hostname.includes(str); switch (true) { case isIncludes('1cili'): handle1CiliSite(); break; case isIncludes('whatslink.info'): // 自定义样式和图片URL复制功能 GM_addStyle(`.img {flex-shrink: 0;height: auto;width: auto;margin-right: 12px;} .banner-title,.banner,.disc,div.wrapper:nth-child(5), #app > div > div:nth-child(7),#app > div > div.footer {display:none} #app {max-width: 100vw !important; padding: 5px !important;} .content {padding: 5px !important;} .wrapper {margin: 0 !important;} .el-input-group {width: 100vw;} body {place-items: baseline !important;}`); monitorAndCopyImageUrls( "div.search", ".image-list .img img", "src", "div.search" ); break; case isIncludes('u3c3'): case isIncludes("sukebei.nyaa.si"): GM_addStyle(`/*u9a9*/ .container .ad,.hdr-link,tr td:nth-child(1),tr td:nth-child(3),tr td:nth-child(6),tr td:nth-child(7),.text-center{display:none !important;} .table {width: 100%;} .torrent-list>tbody>tr>td { white-space: normal;} .torrent-list > tbody > tr > td {max-width:90vw;white-space: normal !important;} .data-list .row {padding: 0;} .navbar-form .input-group {position: fixed; left:0;width: 100vw;}` ); handleRegularSites("tbody tr", "td:nth-child(3) a:last-child", "td:nth-child(2) a", "td:nth-child(4)", "td:nth-child(5)", "td:nth-child(2) a:last-child"); break; case isIncludes('btdig.com'): // GM_addStyle(`center div div {width: 100vw;}`); handleRegularSites( ".one_result > div", ".torrent_name a", ".torrent_name a", ".torrent_size", ".torrent_age", ".torrent_magnet"); break; case isIncludes('btsow.pics'): // GM_addStyle(`center div div {width: 100vw;}`); GM_addStyle(`/*btsow*/ .search {position: sticky !important;top: 80px !important;} .form-inline .input-group {width: 100%;} .hidden-xs:not(.tags-box,.text-right,.search,.search-container),.data-list:not(.detail) .size,.data-list:not(.detail) .date{ display: none !important;}`); // 为 btsow.pics 添加等待机制,确保内容加载完成 const waitForBtsowContent = () => { const elements = document.querySelectorAll(".q-infinite-scroll .row"); if (elements.length > 0) { console.log("btsow.pics 内容已加载,找到元素数量:", elements.length); // 使用 requestAnimationFrame 来避免 ResizeObserver 循环问题 requestAnimationFrame(() => { handleRegularSites( ".q-infinite-scroll .row", "a", "a", ".size", ".date", "a" ); }); } else { console.log("btsow.pics 内容未加载,等待中..."); setTimeout(waitForBtsowContent, 2000); // 每2秒检查一次 } }; // 开始等待内容加载 waitForBtsowContent(); break; case isIncludes('hjd2048.com'): case isIncludes('cctv10.cc'): case isIncludes('cctv12.cc'): break; // case isIncludes('javdb'): // GM_addStyle(`.container:not(.is-max-desktop):not(.is-max-widescreen) {max-width: 100%;} // .movie-list .item .video-title {white-space: normal;}`); // replaceImg(); // break; case isIncludes('javbus'): GM_addStyle(`.masonry #waterfall {display: grid; grid-template-columns: repeat(auto-fill, minmax(465px, 1fr)); gap: 0px; padding: 0px;} .movie-box{width:465px !important;height:400px !important;margin:0 !important;} #waterfall .masonry-brick{position:relative !important; top: 0px !important; left: 0 !important;margin:5px} .item-tag {display: inline-block;}.movie-box .photo-frame {height: auto !important;margin:0 !important;}.movie-box img {height: auto !important;}`); break; default: GM_addStyle(`* {-webkit-touch-callout: text;-webkit-user-select: text !important;-moz-user-select: text;user-select: text;}`);; } })();