Greasy Fork is available in English.
Ranks peers by uploaded amount and highlights the top 5 peers while keeping your rank on top.
// ==UserScript==
// @name TweaxPeerRank
// @namespace eLibrarian-userscripts
// @version 1.0.1
// @author eLibrarian
// @description Ranks peers by uploaded amount and highlights the top 5 peers while keeping your rank on top.
// @license GPL-3.0-or-later
// @match https://*.torrentbd.net/*
// @match https://*.torrentbd.com/*
// @match https://*.torrentbd.org/*
// @match https://*.torrentbd.me/*
// @grant none
// @run-at document-end
// ==/UserScript==
(function () {
'use strict';
const TABLE_SEL = 'table.peers-table';
const CSS_ID = 'tbpr-peer-rank-css';
const CLASS = {
selfRow: 'tbpr-self-row',
selfUser: 'tbpr-self-user',
rankHeader: 'tbpr-rank-header',
rankCell: 'tbpr-rank-cell',
rankBadge: 'tbpr-rank-badge',
};
const DEFAULT_UPLOAD_COL_INDEX = 4;
const REFRESH_MS = 10000;
const DATA_ORIGINAL_ORDER = 'tbprOriginalOrder';
const TOP_RANK_STYLE_LIMIT = 5;
const DATA_APPLIED_PEER_RANK_CLASSES = 'tbprAppliedRankClasses';
let refreshIntervalId = null;
let cachedUsername = null;
let cachedSelfRankClasses = '';
let renderQueued = false;
let peerPresenceObserver = null;
let lastObservedPeerRowCount = -1;
const userRankClassFetches = new Map();
function cssOnce(id, text) {
if (document.getElementById(id)) return;
const style = document.createElement('style');
style.id = id;
style.textContent = text;
document.head.appendChild(style);
}
function toBytes(text) {
const raw = String(text || '').replace(/,/g, '').trim();
if (!raw) return 0;
const match = raw.match(/([0-9]*\.?[0-9]+)\s*([a-zA-Z]+)/);
if (!match) return Number(raw) || 0;
const value = Number(match[1]);
const unit = String(match[2] || '').toUpperCase();
const map = {
B: 1,
KB: 1000,
MB: 1000 ** 2,
GB: 1000 ** 3,
TB: 1000 ** 4,
PB: 1000 ** 5,
KIB: 1024,
MIB: 1024 ** 2,
GIB: 1024 ** 3,
TIB: 1024 ** 4,
PIB: 1024 ** 5,
};
return Number.isFinite(value) ? value * (map[unit] || 1) : 0;
}
function safeRun(fn) {
try { fn(); } catch (error) { console.error('[PeerRank]', error); }
}
function ensureStyles() {
cssOnce(CSS_ID, `
.${CLASS.rankBadge}{
display:inline-block;
padding:2px 8px;
border-radius:4px;
font-weight:700;
color:#fff;
text-shadow:0 1px 2px rgba(0,0,0,.4);
font-size:.9em;
line-height:1.2;
}
.${CLASS.rankBadge}.rank-1{ background-color:#d4af37; }
.${CLASS.rankBadge}.rank-2{ background-color:#c0c0c0; color:#1a1a1a; text-shadow:none; }
.${CLASS.rankBadge}.rank-3{ background-color:#cd7f32; }
.${CLASS.rankBadge}.rank-4{ background-color:#4b8b61; }
.${CLASS.rankBadge}.rank-5{ background-color:#2f9eac; }
td.${CLASS.rankCell}{
text-align:center !important;
width:56px;
min-width:56px;
padding-left:6px;
padding-right:6px;
box-sizing:border-box;
}
.${CLASS.selfRow}{
background:linear-gradient(90deg, rgba(0,255,255,.15) 0%, rgba(0,255,255,0) 20%);
border-left:3px solid #00ffff;
}
.${CLASS.selfUser}{
font-weight:700;
text-shadow:0 0 6px rgba(0,255,255,.18);
}
.${CLASS.selfUser} a,
.${CLASS.selfUser} .tbdrank,
.${CLASS.selfUser} span{
font-weight:700;
}
`);
}
function normalizeText(text) {
return String(text || '').replace(/\s+/g, ' ').trim();
}
function readRankName(node) {
const raw = (node?.childNodes?.[0]?.textContent || node?.textContent || '').trim();
return raw ? raw.replace(/\s+/g, ' ').trim() : '';
}
function readRankClasses(node) {
return Array.from(node?.classList || [])
.filter((cls) => cls && cls !== 'tbdrank')
.join(' ')
.trim();
}
function findLoggedInRankNode() {
const candidates = [
'.card.margin-t-0 .card-content .card-title > .tbdrank',
'.card.margin-t-0 .card-content .card-title .tbdrank',
'.card-content .card-title > .tbdrank',
'.card-content .card-title .tbdrank',
'nav .tbdrank',
'header .tbdrank',
'.top-nav .tbdrank',
'.sidenav .tbdrank'
];
for (const selector of candidates) {
const node = document.querySelector(selector);
if (node) return node;
}
return null;
}
function detectLoggedInUsernameFromDom() {
return readRankName(findLoggedInRankNode());
}
function detectLoggedInUserRankClassesFromDom() {
return readRankClasses(findLoggedInRankNode());
}
function getLoggedInUsername() {
const detected = detectLoggedInUsernameFromDom();
if (detected) cachedUsername = normalizeText(detected);
const rankClasses = detectLoggedInUserRankClassesFromDom();
if (rankClasses) cachedSelfRankClasses = rankClasses;
return cachedUsername;
}
function getLoggedInUserRankClasses() {
const live = detectLoggedInUserRankClassesFromDom();
if (live) {
cachedSelfRankClasses = live;
return cachedSelfRankClasses;
}
return cachedSelfRankClasses;
}
function clearSelfHighlight(peerTable) {
peerTable.querySelectorAll('tbody tr.' + CLASS.selfRow).forEach((row) => row.classList.remove(CLASS.selfRow));
peerTable.querySelectorAll('tbody td.' + CLASS.selfUser).forEach((cell) => cell.classList.remove(CLASS.selfUser));
}
function getPeerUserAnchor(userCell) {
return userCell?.querySelector?.('a[href*="account-details.php?id="]') || null;
}
function getPeerUserIdFromCell(userCell) {
const anchor = getPeerUserAnchor(userCell);
const href = anchor?.getAttribute('href') || '';
const match = href.match(/[?&]id=(\d+)/i);
return match ? match[1] : '';
}
function getPeerUsernameFromCell(userCell) {
if (!userCell) return '';
const anchor = getPeerUserAnchor(userCell);
const rankNode = anchor?.querySelector?.('.tbdrank');
if (rankNode) return normalizeText(readRankName(rankNode));
if (anchor) return normalizeText(anchor.textContent);
return normalizeText(userCell.textContent);
}
function ensurePeerUserHoverTrigger(userCell) {
const anchor = getPeerUserAnchor(userCell);
if (!anchor) return;
const userId = getPeerUserIdFromCell(userCell);
if (!userId) return;
let trigger = anchor.closest('.dl-sc-trg');
if (!trigger || !userCell.contains(trigger)) {
trigger = document.createElement('span');
trigger.className = 'dl-sc-trg fx';
anchor.parentNode.insertBefore(trigger, anchor);
trigger.appendChild(anchor);
} else if (!trigger.classList.contains('fx')) {
trigger.classList.add('fx');
}
trigger.setAttribute('data-type', 'user');
trigger.setAttribute('data-tid', userId);
if (!trigger.hasAttribute('data-props')) trigger.setAttribute('data-props', '');
if (!trigger.querySelector(':scope > .dl-sc')) {
const popupHost = document.createElement('div');
popupHost.className = 'dl-sc';
trigger.appendChild(popupHost);
}
}
function clearPeerUserRankClass(userCell) {
const anchor = getPeerUserAnchor(userCell);
if (!anchor) return;
const applied = String(anchor.dataset?.[DATA_APPLIED_PEER_RANK_CLASSES] || '')
.split(/\s+/)
.filter(Boolean);
if (applied.length) anchor.classList.remove('tbdrank', ...applied);
if (anchor.dataset) delete anchor.dataset[DATA_APPLIED_PEER_RANK_CLASSES];
}
function applyPeerUserRankClass(userCell, rankClasses) {
clearPeerUserRankClass(userCell);
const anchor = getPeerUserAnchor(userCell);
if (!anchor) return;
const classes = String(rankClasses || '').split(/\s+/).filter(Boolean);
if (!classes.length) return;
anchor.classList.add('tbdrank', ...classes);
if (anchor.dataset) anchor.dataset[DATA_APPLIED_PEER_RANK_CLASSES] = classes.join(' ');
}
function extractProfileUserRankClasses(html) {
const doc = new DOMParser().parseFromString(String(html || ''), 'text/html');
const node =
doc.querySelector('.profile-tib-container h5 > .tbdrank') ||
doc.querySelector('.profile-tib-container .tbdrank') ||
doc.querySelector('#middle-block .tbdrank');
if (!node) return '';
return Array.from(node.classList || [])
.filter((cls) => cls && cls !== 'tbdrank')
.join(' ')
.trim();
}
async function fetchUserRankClasses(userId) {
const id = String(userId || '').trim();
if (!id) return '';
if (userRankClassFetches.has(id)) return userRankClassFetches.get(id);
const promise = (async () => {
try {
const response = await fetch(`account-details.php?id=${encodeURIComponent(id)}`, { credentials: 'include' });
if (!response.ok) return '';
const html = await response.text();
return extractProfileUserRankClasses(html);
} catch {
return '';
} finally {
userRankClassFetches.delete(id);
}
})();
userRankClassFetches.set(id, promise);
return promise;
}
function applyTopPeerRankClasses(peers, userCellIndex, selfUsername, selfRankClasses) {
peers.forEach((peer, index) => {
const userCell = peer.row.cells?.[userCellIndex] || peer.row.cells?.[peer.row.cells.length - 1] || null;
if (!userCell) return;
const isSelf = !!selfUsername && getPeerUsernameFromCell(userCell) === selfUsername;
if (isSelf) {
if (selfRankClasses) applyPeerUserRankClass(userCell, selfRankClasses);
else clearPeerUserRankClass(userCell);
return;
}
if (index >= TOP_RANK_STYLE_LIMIT) {
clearPeerUserRankClass(userCell);
return;
}
const userId = getPeerUserIdFromCell(userCell);
if (!userId) {
clearPeerUserRankClass(userCell);
return;
}
void fetchUserRankClasses(userId).then((rankClasses) => {
if (!peer.row.isConnected) return;
const liveCell = peer.row.cells?.[userCellIndex] || peer.row.cells?.[peer.row.cells.length - 1] || null;
if (!liveCell) return;
if (getPeerUserIdFromCell(liveCell) !== userId) return;
if (!rankClasses) return;
applyPeerUserRankClass(liveCell, rankClasses);
});
});
}
function findUploadColumnIndex(headerRow) {
const ths = Array.from(headerRow?.querySelectorAll('th') || []);
const index = ths.findIndex((th) => /^(UL|Upload(?:ed)?)$/i.test((th.textContent || '').trim()));
return index >= 0 ? index : DEFAULT_UPLOAD_COL_INDEX;
}
function findUsernameColumnIndex(headerRow) {
const ths = Array.from(headerRow?.querySelectorAll('th') || []);
const index = ths.findIndex((th) => /^username$/i.test((th.textContent || '').trim()));
return index >= 0 ? index : Math.max(0, ths.length - 1);
}
function findIpv6ColumnIndex(headerRow) {
const ths = Array.from(headerRow?.querySelectorAll('th') || []);
return ths.findIndex((th) => /^ipv6$/i.test((th.textContent || '').trim()));
}
function stripExistingRankColumn(headerRow, rows) {
headerRow.querySelector('.' + CLASS.rankHeader)?.remove();
rows.forEach((row) => row.querySelector('.' + CLASS.rankCell)?.remove());
}
function makeRankCell(rank) {
const cell = document.createElement('td');
cell.className = CLASS.rankCell;
if (rank <= 5) {
const badge = document.createElement('span');
badge.className = `${CLASS.rankBadge} rank-${rank}`;
badge.textContent = String(rank);
cell.appendChild(badge);
} else {
cell.textContent = String(rank);
}
return cell;
}
function ensureOriginalRowOrder(rows) {
rows.forEach((row, index) => {
if (!row?.dataset) return;
if (row.dataset[DATA_ORIGINAL_ORDER] == null) row.dataset[DATA_ORIGINAL_ORDER] = String(index);
});
}
function getCurrentPeerRowCount() {
const table = document.querySelector(TABLE_SEL);
if (!table) return -1;
return table.querySelectorAll('tbody tr').length;
}
function pinSelfRowsToTop(tbody, selfUsername) {
if (!tbody || !selfUsername) return;
const rows = Array.from(tbody.querySelectorAll(':scope > tr'));
if (!rows.length) return;
const selfRows = [];
const otherRows = [];
rows.forEach((row) => {
const cells = row.cells;
const userCell = cells?.[cells.length - 1] || null;
const isSelf = row.classList.contains(CLASS.selfRow) || getPeerUsernameFromCell(userCell) === selfUsername;
if (isSelf) {
row.classList.add(CLASS.selfRow);
userCell?.classList?.add(CLASS.selfUser);
selfRows.push(row);
} else {
otherRows.push(row);
}
});
if (!selfRows.length) return;
const fragment = document.createDocumentFragment();
selfRows.forEach((row) => fragment.appendChild(row));
otherRows.forEach((row) => fragment.appendChild(row));
tbody.appendChild(fragment);
}
function processPeerTable() {
const peerTable = document.querySelector(TABLE_SEL);
if (!peerTable) return;
const headerRow = peerTable.querySelector('thead tr');
const tbody = peerTable.querySelector('tbody');
if (!headerRow || !tbody) return;
const rows = Array.from(tbody.querySelectorAll('tr')).filter((row) => row.cells && row.cells.length > 1);
if (!rows.length) return;
ensureOriginalRowOrder(rows);
stripExistingRankColumn(headerRow, rows);
clearSelfHighlight(peerTable);
const uploadColIndex = findUploadColumnIndex(headerRow);
const usernameColIndex = findUsernameColumnIndex(headerRow);
const ipv6ColIndex = findIpv6ColumnIndex(headerRow);
const userCellIndex = usernameColIndex + 1;
const ipv6CellIndex = ipv6ColIndex >= 0 ? ipv6ColIndex + 1 : -1;
const selfUsername = normalizeText(getLoggedInUsername());
const selfRankClasses = String(getLoggedInUserRankClasses() || '').trim();
const rankHeader = document.createElement('th');
rankHeader.className = CLASS.rankHeader;
rankHeader.textContent = 'Rank';
headerRow.insertBefore(rankHeader, headerRow.firstChild);
const peers = rows.map((row) => ({
row,
uploadedBytes: Number(toBytes(row.cells[uploadColIndex]?.textContent || '0 B')) || 0,
originalOrder: Number(row.dataset?.[DATA_ORIGINAL_ORDER] || 0)
}));
peers.sort((a, b) => (b.uploadedBytes - a.uploadedBytes) || (a.originalOrder - b.originalOrder));
const selfRows = [];
const otherRows = [];
peers.forEach((peer, index) => {
const rank = index + 1;
const rankCell = makeRankCell(rank);
peer.row.insertBefore(rankCell, peer.row.firstChild);
if (ipv6CellIndex >= 0) {
const ipv6Cell = peer.row.cells?.[ipv6CellIndex] || null;
if (ipv6Cell) ipv6Cell.style.textAlign = 'center';
}
const cells = peer.row.cells;
const userCell = cells?.[userCellIndex] || cells?.[cells.length - 1] || null;
ensurePeerUserHoverTrigger(userCell);
const isSelf = !!selfUsername && getPeerUsernameFromCell(userCell) === selfUsername;
if (isSelf) {
peer.row.classList.add(CLASS.selfRow);
userCell?.classList?.add(CLASS.selfUser);
if (selfRankClasses) applyPeerUserRankClass(userCell, selfRankClasses);
selfRows.push(peer.row);
} else {
otherRows.push(peer.row);
}
});
const fragment = document.createDocumentFragment();
selfRows.forEach((row) => fragment.appendChild(row));
otherRows.forEach((row) => fragment.appendChild(row));
tbody.appendChild(fragment);
applyTopPeerRankClasses(peers, userCellIndex, selfUsername, selfRankClasses);
pinSelfRowsToTop(tbody, selfUsername);
}
function scheduleProcess() {
if (renderQueued) return;
renderQueued = true;
requestAnimationFrame(() => {
renderQueued = false;
safeRun(processPeerTable);
});
}
function ensureRefreshLoop() {
if (refreshIntervalId) return;
refreshIntervalId = setInterval(() => {
if (!document.querySelector(TABLE_SEL)) return;
getLoggedInUsername();
scheduleProcess();
}, REFRESH_MS);
}
function ensurePresenceObserver() {
if (peerPresenceObserver || !document.body) return;
lastObservedPeerRowCount = getCurrentPeerRowCount();
peerPresenceObserver = new MutationObserver(() => {
const nextCount = getCurrentPeerRowCount();
if (nextCount !== lastObservedPeerRowCount) {
lastObservedPeerRowCount = nextCount;
if (nextCount >= 0) {
getLoggedInUsername();
scheduleProcess();
}
}
});
peerPresenceObserver.observe(document.body, { childList: true, subtree: true });
}
function run() {
ensureStyles();
getLoggedInUsername();
scheduleProcess();
ensureRefreshLoop();
ensurePresenceObserver();
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', run, { once: true });
} else {
run();
}
})();