add seed timer & re-org
This commit is contained in:
parent
a3cd2f0fdd
commit
563ad690c2
9 changed files with 131 additions and 0 deletions
131
scripts/javascript/privatehd-seed-timer.user.js
Normal file
131
scripts/javascript/privatehd-seed-timer.user.js
Normal file
|
|
@ -0,0 +1,131 @@
|
|||
// ==UserScript==
|
||||
// @name PrivateHD Seed Timer
|
||||
// @namespace https://privatehd.to/
|
||||
// @version 1.0.0
|
||||
// @description Shows required seed time and whether you can stop seeding each torrent, based on file size.
|
||||
// @author you
|
||||
// @match https://privatehd.to/profile/*/active*
|
||||
// @grant none
|
||||
// ==/UserScript==
|
||||
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Formula: required seed time in hours, where x = size in GB
|
||||
// x <= 1 → 72h
|
||||
// 1 < x < 50 → 72 + 2x h
|
||||
// x >= 50 → 100 * ln(x) - 219.2023 h
|
||||
// ---------------------------------------------------------------------------
|
||||
function requiredHours(sizeGB) {
|
||||
if (sizeGB <= 1) return 72;
|
||||
if (sizeGB < 50) return 72 + 2 * sizeGB;
|
||||
return 100 * Math.log(sizeGB) - 219.2023;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Parse a size string like "32.62 GB", "512.00 MB", "1.20 TB" → GB (float)
|
||||
// ---------------------------------------------------------------------------
|
||||
function parseSizeToGB(str) {
|
||||
const m = str.trim().match(/([\d.]+)\s*(B|KB|MB|GB|TB)/i);
|
||||
if (!m) return null;
|
||||
const val = parseFloat(m[1]);
|
||||
const unit = m[2].toUpperCase();
|
||||
const factors = { B: 1e-9, KB: 1e-6, MB: 1e-3, GB: 1, TB: 1e3 };
|
||||
return val * (factors[unit] ?? 1);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Parse elapsed time from the "For" column (last <td> in each row).
|
||||
// Examples: "23h", "2d", "5d 3h", "1h 30m", "45m"
|
||||
// Returns elapsed hours as a float.
|
||||
// ---------------------------------------------------------------------------
|
||||
function parseElapsedHours(str) {
|
||||
let hours = 0;
|
||||
const days = str.match(/(\d+)\s*d/);
|
||||
const hrs = str.match(/(\d+)\s*h/);
|
||||
const mins = str.match(/(\d+)\s*m/);
|
||||
if (days) hours += parseInt(days[1], 10) * 24;
|
||||
if (hrs) hours += parseInt(hrs[1], 10);
|
||||
if (mins) hours += parseInt(mins[1], 10) / 60;
|
||||
return hours;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Format hours nicely: "72h 00m" etc.
|
||||
// ---------------------------------------------------------------------------
|
||||
function fmtHours(h) {
|
||||
const days = Math.floor(h / 24);
|
||||
const hrs = Math.floor(h % 24);
|
||||
const mins = Math.round((h % 1) * 60);
|
||||
if (days > 0) return `${days}d ${hrs}h`;
|
||||
return `${hrs}h ${String(mins).padStart(2, '0')}m`;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Add a "Seed Status" column header
|
||||
// ---------------------------------------------------------------------------
|
||||
const thead = document.querySelector('table thead tr');
|
||||
if (!thead) return;
|
||||
|
||||
const th = document.createElement('th');
|
||||
th.textContent = 'Seed Status';
|
||||
th.style.whiteSpace = 'nowrap';
|
||||
thead.appendChild(th);
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Process each data row
|
||||
// ---------------------------------------------------------------------------
|
||||
const rows = document.querySelectorAll('table tbody tr');
|
||||
|
||||
rows.forEach(row => {
|
||||
const cells = row.querySelectorAll('td');
|
||||
// Column layout (0-indexed):
|
||||
// 0: icon | 1: name+stats | 2: progress | 3: status | 4: client
|
||||
// 5: upload | 6: download | 7: left | 8: ratio | 9: started | 10: updated | 11: For
|
||||
|
||||
const nameCell = cells[1];
|
||||
const forCell = cells[11]; // "For" — how long you've been seeding
|
||||
|
||||
if (!nameCell || !forCell) {
|
||||
const td = document.createElement('td');
|
||||
td.textContent = '—';
|
||||
row.appendChild(td);
|
||||
return;
|
||||
}
|
||||
|
||||
// File size lives inside the name cell: fa-database span
|
||||
const sizeSpan = nameCell.querySelector('.fa-database');
|
||||
const sizeText = sizeSpan ? sizeSpan.textContent.trim() : null;
|
||||
const sizeGB = sizeText ? parseSizeToGB(sizeText) : null;
|
||||
|
||||
const elapsedText = forCell.textContent.trim();
|
||||
const elapsed = parseElapsedHours(elapsedText);
|
||||
const required = sizeGB !== null ? requiredHours(sizeGB) : null;
|
||||
|
||||
const td = document.createElement('td');
|
||||
td.style.whiteSpace = 'nowrap';
|
||||
td.style.verticalAlign = 'middle';
|
||||
|
||||
if (required === null) {
|
||||
td.textContent = 'No size data';
|
||||
td.style.color = '#999';
|
||||
} else {
|
||||
const remaining = required - elapsed;
|
||||
const canStop = remaining <= 0;
|
||||
|
||||
if (canStop) {
|
||||
td.innerHTML =
|
||||
`<span style="color:#5cb85c;font-weight:bold;">✔ Can stop</span><br>` +
|
||||
`<small style="color:#aaa;">Req: ${fmtHours(required)} | Done: ${fmtHours(elapsed)}</small>`;
|
||||
} else {
|
||||
td.innerHTML =
|
||||
`<span style="color:#d9534f;font-weight:bold;">✘ Keep seeding</span><br>` +
|
||||
`<small style="color:#aaa;">${fmtHours(remaining)} left of ${fmtHours(required)}</small>`;
|
||||
}
|
||||
}
|
||||
|
||||
row.appendChild(td);
|
||||
});
|
||||
|
||||
})();
|
||||
Loading…
Add table
Add a link
Reference in a new issue