您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Floating info boxes for magnet links with tracker details (guaranteed correct placement)
// ==UserScript== // @name Magnet Info Enhancer v3.6 (Stable Placement Fix) // @namespace Violentmonkey Scripts // @version 3.6 // @description Floating info boxes for magnet links with tracker details (guaranteed correct placement) // @match *://*/* // @grant GM_xmlhttpRequest // @connect checker.openwebtorrent.com // @license MIT // ==/UserScript== (function() { 'use strict'; // === API Fetch === function getTorrentInfo(magnet, callback) { const apiUrl = "https://checker.openwebtorrent.com/check?magnet=" + encodeURIComponent(magnet); GM_xmlhttpRequest({ method: "GET", url: apiUrl, headers: { "accept": "application/json" }, onload: response => { try { const data = JSON.parse(response.responseText); if (data) { callback({ seeds: data.seeds ?? 0, peers: data.peers ?? 0, extra: data.extra ?? [] }); } else callback(null); } catch (e) { console.error("API parse error:", e); callback(null); } }, onerror: () => callback(null) }); } // === Position Helper with retry until valid === function positionBox(box, link) { function tryPosition() { const rect = link.getBoundingClientRect(); if (rect.width > 0 || rect.height > 0) { box.style.top = (window.scrollY + rect.bottom + 5) + "px"; box.style.left = (window.scrollX + rect.left) + "px"; } else { // Retry next frame if element not ready yet requestAnimationFrame(tryPosition); } } requestAnimationFrame(tryPosition); } // === UI Helpers === function createBoxElement() { const box = document.createElement("div"); Object.assign(box.style, { position: "absolute", background: "#1a1a1a", color: "#eee", border: "1px solid #444", borderRadius: "6px", padding: "8px", fontSize: "12px", fontFamily: "monospace", boxShadow: "0 4px 10px rgba(0,0,0,0.5)", zIndex: 9999, maxWidth: "400px" }); return box; } function createHeader(status, details, box) { const header = document.createElement("div"); Object.assign(header.style, { display: "flex", justifyContent: "space-between", alignItems: "center" }); // Status span status.textContent = "⏳ Checking..."; status.style.color = "#ff0"; header.appendChild(status); // Buttons const buttons = document.createElement("div"); const detailsBtn = document.createElement("button"); detailsBtn.textContent = "More details"; Object.assign(detailsBtn.style, { background: "transparent", border: "none", color: "#0af", cursor: "pointer", fontSize: "12px", marginRight: "8px" }); detailsBtn.onclick = () => { const visible = details.style.display === "block"; details.style.display = visible ? "none" : "block"; detailsBtn.textContent = visible ? "More details" : "Hide details"; }; const closeBtn = document.createElement("button"); closeBtn.textContent = "❌"; Object.assign(closeBtn.style, { background: "transparent", border: "none", color: "#f55", cursor: "pointer", fontSize: "13px" }); closeBtn.onclick = () => box.remove(); buttons.appendChild(detailsBtn); buttons.appendChild(closeBtn); header.appendChild(buttons); return header; } function buildTrackerList(extra) { if (!extra.length) { return document.createTextNode("No tracker details available."); } const container = document.createElement("div"); extra.forEach(t => { const row = document.createElement("div"); Object.assign(row.style, { display: "flex", justifyContent: "space-between", marginBottom: "3px" }); const tracker = document.createElement("span"); tracker.textContent = t.tracker; const stats = document.createElement("span"); if (t.seeds !== undefined) { stats.textContent = `⬆ ${t.seeds} ⬇ ${t.peers} DL: ${t.downloads}`; stats.style.color = (t.seeds === 0 && t.peers === 0) ? "#ff0" : "#0f0"; } else if (t.error) { stats.textContent = "ERROR"; stats.style.color = "#f55"; } row.appendChild(tracker); row.appendChild(stats); container.appendChild(row); }); return container; } // === Main UI Builder === function createFloatingBox(link) { const box = createBoxElement(); const status = document.createElement("span"); const details = document.createElement("div"); Object.assign(details.style, { display: "none", marginTop: "6px", fontSize: "11px", whiteSpace: "normal" }); const header = createHeader(status, details, box); box.appendChild(header); box.appendChild(details); document.body.appendChild(box); // Position after DOM + ensure non-zero rect positionBox(box, link); // Reposition on scroll/resize window.addEventListener("scroll", () => positionBox(box, link)); window.addEventListener("resize", () => positionBox(box, link)); return { status, details }; } // === Main Runner === document.addEventListener("DOMContentLoaded", () => { const links = document.querySelectorAll('a[href^="magnet:"]'); links.forEach(link => { const { status, details } = createFloatingBox(link); getTorrentInfo(link.href, info => { if (info) { status.textContent = `⬆ Seeds: ${info.seeds} ⬇ Peers: ${info.peers}`; status.style.color = (info.seeds === 0 && info.peers === 0) ? "#f55" : "#0f0"; details.innerHTML = ""; details.appendChild(buildTrackerList(info.extra)); } else { status.textContent = "❌ No info available"; status.style.color = "#f55"; details.textContent = "No tracker data."; } }); }); }); })();