您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Optimized & Cleaned. Refreshes every 3 seconds. Ranks peers, gives top 10 badges, and highlights your row with a bold font & a vertical bar matching your official rank color.
// ==UserScript== // @name TorrentBD Peer Rank // @version 1.1 // @description Optimized & Cleaned. Refreshes every 3 seconds. Ranks peers, gives top 10 badges, and highlights your row with a bold font & a vertical bar matching your official rank color. // @author RUXE // @namespace 69. // @match https://*.torrentbd.com/* // @match https://*.torrentbd.net/* // @match https://*.torrentbd.org/* // @match https://*.torrentbd.me/* // @icon https://www.google.com/s2/favicons?sz=64&domain=torrentbd.net // @grant none // @license MIT // @run-at document-end // ==/UserScript== (function() { 'use strict'; const CONFIG = { selectors: { peerTable: 'table.peers-table', username: '.card-content .card-title span.tbdrank', tableHeaderRow: 'thead tr', tableBody: 'tbody', }, classNames: { selfRow: 'tbd-self-row', rankHeader: 'peer-rank-header', rankCell: 'peer-rank-cell', }, uploadColumnIndex: 4, usernameColumnIndex: 7, refreshIntervalMs: 3000, // <-- Refresh interval set to 3 seconds style: ` .rank-badge { display: inline-block; padding: 2px 8px; border-radius: 4px; font-weight: bold; color: #fff; text-shadow: 1px 1px 2px rgba(0,0,0,0.4); font-size: 0.9em; } .rank-1 { background-color: #d4af37; } .rank-2 { background-color: #c0c0c0; } .rank-3 { background-color: #cd7f32; } .rank-4 { background-color: #4caf50; } .rank-5 { background-color: #2196f3; } .rank-6 { background-color: #9c27b0; } .rank-7 { background-color: #ff9800; } .rank-8 { background-color: #795548; } .rank-9 { background-color: #009688; } .rank-10 { background-color: #607d8b; } .tbd-self-row { border-left: 4px solid var(--user-rank-color, #00ffff); font-weight: bold; } ` }; let refreshIntervalId = null; let cachedUsername = null; let cachedUserClass = null; function injectStyles() { const style = document.createElement('style'); style.textContent = CONFIG.style; document.head.appendChild(style); } function parseToBytes(uploadStr) { if (!uploadStr) return 0; const parts = uploadStr.trim().split(' '); if (parts.length < 2) return 0; const value = parseFloat(parts[0].replace(/,/g, '')); const unit = parts[1].toUpperCase(); switch (unit) { case 'TIB': return value * 1024**4; case 'GIB': return value * 1024**3; case 'MIB': return value * 1024**2; case 'KIB': return value * 1024; default: return value; } } function getLoggedInUsername() { if (cachedUsername) return cachedUsername; try { const userElement = document.querySelector(CONFIG.selectors.username); const name = userElement?.textContent.trim(); if (name) { cachedUsername = name; cachedUserClass = userElement.className; return name; } } catch (e) { // Fails silently if the element isn't found } return null; } function applyHighlightAndStyle(rows) { if (!cachedUsername || !cachedUserClass) return; const finalUsernameIndex = CONFIG.usernameColumnIndex + 1; for (const row of rows) { if (row.cells.length <= finalUsernameIndex) continue; const usernameCell = row.cells[finalUsernameIndex]; const cellText = usernameCell.textContent.trim(); // Reset styles from previous runs row.classList.remove(CONFIG.classNames.selfRow); row.style.removeProperty('--user-rank-color'); const styledSpan = usernameCell.querySelector('span[data-script-styled]'); if (styledSpan) { usernameCell.innerHTML = styledSpan.textContent; } // Apply styles if username matches if (cellText === cachedUsername) { usernameCell.innerHTML = `<span class="${cachedUserClass}" data-script-styled="true">${cellText}</span>`; const newSpan = usernameCell.querySelector('span[data-script-styled]'); if (newSpan) { const computedColor = window.getComputedStyle(newSpan).color; row.style.setProperty('--user-rank-color', computedColor); row.classList.add(CONFIG.classNames.selfRow); } } } } function processPeerTable() { const peerTable = document.querySelector(CONFIG.selectors.peerTable); if (!peerTable) return; const headerRow = peerTable.querySelector(CONFIG.selectors.tableHeaderRow); const tbody = peerTable.querySelector(CONFIG.selectors.tableBody); if (!headerRow || !tbody) return; getLoggedInUsername(); if (!headerRow.querySelector(`.${CONFIG.classNames.rankHeader}`)) { const rankHeader = document.createElement('th'); rankHeader.className = CONFIG.classNames.rankHeader; rankHeader.textContent = 'Rank'; headerRow.insertBefore(rankHeader, headerRow.firstChild); } const rows = Array.from(tbody.querySelectorAll('tr')); rows.forEach(row => row.querySelector(`.${CONFIG.classNames.rankCell}`)?.remove()); const peers = rows.map(row => ({ row: row, ul: parseToBytes(row.cells[CONFIG.uploadColumnIndex]?.textContent) })); peers.sort((a, b) => b.ul - a.ul); const fragment = document.createDocumentFragment(); peers.forEach((peer, index) => { const rank = index + 1; const rankCell = document.createElement('td'); rankCell.className = CONFIG.classNames.rankCell; rankCell.style.textAlign = 'center'; if (rank <= 10) { rankCell.innerHTML = `<span class="rank-badge rank-${rank}">${rank}</span>`; } else { rankCell.textContent = rank.toString(); } peer.row.insertBefore(rankCell, peer.row.firstChild); fragment.appendChild(peer.row); }); tbody.innerHTML = ''; tbody.appendChild(fragment); applyHighlightAndStyle(tbody.querySelectorAll('tr')); } function init() { injectStyles(); const observer = new MutationObserver(() => { const peerTable = document.querySelector(CONFIG.selectors.peerTable); if (peerTable && !refreshIntervalId) { processPeerTable(); refreshIntervalId = setInterval(processPeerTable, CONFIG.refreshIntervalMs); } else if (!peerTable && refreshIntervalId) { clearInterval(refreshIntervalId); refreshIntervalId = null; } }); observer.observe(document.body, { childList: true, subtree: true }); } init(); })();