// ==UserScript== // @name 磁力链接预览图片 // @namespace https://bbs.tampermonkey.net.cn/ // @match *://anybt.eth.link/* // @match *://skrbtiu.top/* // @match *://*.btmulu.work/hash/* // @match *://cilian.site/* // @version 1.0 // @description 磁力链接预览图片 对指定网站进行磁力链接预览 // @author Javaow 纸鸢花的花语 // @grant GM_xmlhttpRequest // ==/UserScript== (function () { 'use strict'; // 是否默认显示悬浮窗口 let isBox = false; // 默认窗口宽度 单位:px let boxWidth = "400"; // 最大窗口宽度 let boxMaxWidth = "50%"; // 最小窗口宽度 let boxMinWidth = "10%"; // 最小窗口高度 let boxMinHeight = "50px"; // 最大窗口高度 let boxMaxHeight = "500px"; // 扫描间隔 太快会导致页面卡顿 太慢会导致预览按钮延迟很长时间出现 let scanMs = 1000; // 导入菜单栏样式 function initStyle() { const qStyle = document.createElement("style"); qStyle.setAttribute("type", "text/css"); qStyle.innerHTML = ` ul { list-style: none; } #qBox { max-width: ${boxMaxWidth}; min-width: ${boxMinWidth}; z-index: 9999; position: fixed; right: 30px; top: 20px; border-radius: 7px; border: 2.5px solid black; background-color: white; } #qTop{ background-color: #6192e4; color: #fff; user-select: none; cursor: move; position: relative; } #qBox>div { font-size: 14px; display: flex; align-items: center; justify-content: center; padding: 5px 5px; font-weight: bold; border-radius: 4px 4px 0px 0px; } #jxinput { border-radius: 2px; flex: 1; box-shadow: rgba(60, 64, 67, 0.3) 0px 1px 2px 0px, rgba(60, 64, 67, 0.15) 0px 1px 3px 1px; height: 20px; } #qBox span { font-weight: bold; padding: 5px; } #qBox span button { border: 0; display: inline-block; height: 30px; border-radius: 2px; min-width: 30px; } #qList { display: flex; flex-wrap: wrap; justify-content: center; align-items: center; min-height: ${boxMinHeight}; max-height: ${boxMaxHeight}; overflow: hidden; overflow-y: scroll; overscroll-behavior: none; color: #000; } #qList::-webkit-scrollbar { width: 5px; } #qList::-webkit-scrollbar-track { background: #d7d7d7; border-radius: 6px; } #qList::-webkit-scrollbar-thumb { background: #888; border-radius: 6px; } #qList::-webkit-scrollbar-thumb:hover { background: #6192e4; } .preview-img{ margin-left: .7rem; background-color: #34bf9a; border-radius: 5px; color: #fff; border: none; padding: .5rem; cursor: pointer; } .close-btn{ margin-right: 5px; cursor: pointer; position: absolute; top: 50%; transform: translateY(-50%); right: 0; display: flex; justify-content: center; align-items: center; } `; let headNode = document.querySelector('head'); headNode.appendChild(qStyle); } let boxLeft, boxTop; function insertBox() { // 创建小菜单盒子 let qBox = document.createElement("div"); // 判断显示位置,显示默认位置还是根据上次位置显示 if (boxLeft == undefined || boxTop == undefined) { qBox.style.left = window.innerWidth - boxWidth + "px"; } else { qBox.style.left = boxLeft; qBox.style.top = boxTop; } qBox.id = "qBox"; qBox.innerHTML = `
磁力预览
`; // 添加到页面 document.body.insertBefore(qBox, document.body.firstElementChild); // =============== 拖拽实现 ================ const tag = document.querySelector('#qBox'); const qTop = document.querySelector('#qTop'); let isDragging = false; let startX, startY; let offsetX, offsetY; qTop.addEventListener('mousedown', e => { startX = e.clientX; startY = e.clientY; offsetX = startX - tag.offsetLeft; offsetY = startY - tag.offsetTop; isDragging = true; }); document.addEventListener('mousemove', e => { if (isDragging) { let x = e.clientX - offsetX; let y = e.clientY - offsetY; // 边界检测 防止超出边界 // 减去滚动条宽度 const maxX = window.innerWidth - tag.offsetWidth - (window.innerWidth - document.documentElement.clientWidth); // 可拖拽的最大 X 坐标 const maxY = window.innerHeight - tag.offsetHeight; x = Math.max(0, Math.min(x, maxX)); y = Math.max(0, Math.min(y, maxY)); tag.style.left = `${x}px`; tag.style.top = `${y}px`; } }); document.addEventListener('mouseup', () => { isDragging = false; }); // =============== 阻止滚动条穿透 ================ const scrollBox = document.getElementById("qList"); let initialPageY = 0 scrollBox.addEventListener('touchstart', (e) => { initialPageY = e.changedTouches[0].pageY }) scrollBox.addEventListener('touchmove', (e) => { // 滚动到边缘时阻止滚动 const deltaY = e.changedTouches[0].pageY - initialPageY // 禁止向上滚动溢出 if (e.cancelable && deltaY > 0 && scrollBox.scrollTop <= 0) { e.preventDefault() } // 禁止向下滚动溢出 if ( e.cancelable && deltaY < 0 && scrollBox.scrollTop + scrollBox.clientHeight >= scrollBox.scrollHeight ) { e.preventDefault() } }) // =============== 绑定关闭按钮 ================ document.getElementById("boxCloseBtn").addEventListener('click', (e) => { // 记录关闭前的位置 boxLeft = qBox.style.left boxTop = qBox.style.top qBox.remove(); isBox = false }) isBox = true } // 加载样式 加载窗口 initStyle() // 全文查找磁力链接 获取磁力链接文本所在的节点 function findNodesWithMatch(root, regex) { const results = []; if (!root) return results; if (root.nodeType === 3) { // 文本节点 for (let i = 0; i < regex.length; i++) { if (regex[i].test(root.nodeValue)) { results.push(root); } } } for (let i = 0; i < root.childNodes.length; i++) { results.push(...findNodesWithMatch(root.childNodes[i], regex)); } return results; } function ajax(magnetUrl) { if (!isBox) { // 插入悬浮窗口 insertBox() } const qList = document.getElementById("qList"); // Api有请求限制,一分钟内只能访问5次 GM_xmlhttpRequest({ url: "https://whatslink.info/api/v1/link?url=" + magnetUrl, method: "GET", headers: { "Referer": "https://whatslink.info/" }, onload: function (xhr) { if (xhr.status != 200) { qList.innerHTML = "请求接口错误"; return; } var obj = JSON.parse(xhr.responseText); console.log(xhr.responseText); if (obj.error != "") { qList.innerHTML = obj.name; return; } let div = document.createElement("div"); div.style.display = "flex"; div.style.flexWrap = "wrap"; div.style.padding = "0 .3rem"; div.className = "img-box" if (obj.screenshots == null) { qList.innerHTML = "无预览图"; } else { qList.innerHTML = ""; for (let j = 0; j < obj.screenshots.length; j++) { let a = document.createElement("a"); a.style.width = "100%"; a.style.height = "auto"; a.style.padding = ".3rem 0"; a.href = obj.screenshots[j].screenshot; a.target = "_blank"; let img = document.createElement("img"); img.src = obj.screenshots[j].screenshot img.style.width = "100%"; img.style.height = "auto"; a.append(img) div.append(a) } } qList.append(div) } }); } // 储存已经添加过的节点 const processedNodes = new Set(); function exec(node) { // 跳过已经添加上的 if (!processedNodes.has(node)) { // 检查是否已处理 const button = document.createElement('button'); button.textContent = '预览图片'; button.classList.add('preview-img'); // 添加样式类 let hash = node.nodeValue let magnetRegex = /magnet:\?xt=urn:btih:/i; if (!magnetRegex.test(hash)) { hash = `magnet:?xt=urn:btih:${node.nodeValue}`; } button.dataset.magnetUrl = hash button.onclick = function (event) { // 阻止事件穿透到父级 event.stopPropagation(); event.preventDefault(); ajax(event.target.dataset.magnetUrl) } // 将按钮插入到文本节点的父元素后 node.parentNode.insertBefore(button, node.nextSibling); processedNodes.add(node); // 将节点标记为已处理 } } // 定时器 setInterval(function () { // 匹配磁力链接 let regexArr = [ /magnet:\?xt=urn:btih:[a-z0-9]{40}/i, /^[a-z0-9]{40}$/i ] let nodes = findNodesWithMatch(document.body, regexArr); nodes.forEach(node => { exec(node) }); }, scanMs); })();