From 1985b160644ea3ba88151199b208624d04b44d64 Mon Sep 17 00:00:00 2001 From: thePR0M3TH3AN <53631862+PR0M3TH3AN@users.noreply.github.com> Date: Sat, 27 Sep 2025 13:49:45 -0400 Subject: [PATCH] Improve health probe prioritization --- js/gridHealth.js | 139 +++++++++++++++++++++++++++++++---------------- 1 file changed, 91 insertions(+), 48 deletions(-) diff --git a/js/gridHealth.js b/js/gridHealth.js index aba14ed8..b2cf13ca 100644 --- a/js/gridHealth.js +++ b/js/gridHealth.js @@ -110,7 +110,7 @@ function normalizeResult(result) { }; } -function queueProbe(magnet, cacheKey) { +function queueProbe(magnet, cacheKey, priority = 0) { if (!magnet) { return Promise.resolve(null); } @@ -125,21 +125,23 @@ function queueProbe(magnet, cacheKey) { } const job = probeQueue - .run(() => - torrentClient - .probePeers(magnet, { - timeoutMs: PROBE_TIMEOUT_MS, - maxWebConns: 2, - polls: PROBE_POLL_COUNT, - }) - .catch((err) => ({ - healthy: false, - peers: 0, - reason: "error", - error: err, - appendedTrackers: false, - hasProbeTrackers: false, - })) + .run( + () => + torrentClient + .probePeers(magnet, { + timeoutMs: PROBE_TIMEOUT_MS, + maxWebConns: 2, + polls: PROBE_POLL_COUNT, + }) + .catch((err) => ({ + healthy: false, + peers: 0, + reason: "error", + error: err, + appendedTrackers: false, + hasProbeTrackers: false, + })), + priority ) .then((result) => { const normalized = normalizeResult(result); @@ -173,13 +175,35 @@ class ProbeQueue { this.queue = []; } - run(task) { + run(task, priority = 0) { return new Promise((resolve, reject) => { - this.queue.push({ task, resolve, reject }); + const normalizedPriority = Number.isFinite(priority) ? priority : 0; + const job = { + task, + resolve, + reject, + priority: normalizedPriority, + }; + this.enqueue(job); this.drain(); }); } + enqueue(job) { + if (!this.queue.length) { + this.queue.push(job); + return; + } + const index = this.queue.findIndex( + (existing) => existing.priority < job.priority + ); + if (index === -1) { + this.queue.push(job); + } else { + this.queue.splice(index, 0, job); + } + } + drain() { if (this.running >= this.max) { return; @@ -233,36 +257,16 @@ function ensureState(container) { const pendingByCard = new WeakMap(); const observedCards = new WeakSet(); + state = { observer: null, pendingByCard, observedCards }; const observer = new IntersectionObserver( (entries) => { - const viewportCenter = getViewportCenter(); - const prioritized = entries - .filter((entry) => entry.isIntersecting && entry.target instanceof HTMLElement) - .map((entry) => ({ - entry, - ratio: - typeof entry.intersectionRatio === "number" - ? entry.intersectionRatio - : 0, - distance: calculateDistanceSquared(entry, viewportCenter), - })) - .sort((a, b) => { - if (b.ratio !== a.ratio) { - return b.ratio - a.ratio; - } - return a.distance - b.distance; - }); - - prioritized.forEach(({ entry }) => { - const card = entry.target; - handleCardVisible({ card, pendingByCard }); - }); + processObserverEntries(entries, state); }, { root: null, rootMargin: ROOT_MARGIN, threshold: 0.01 } ); - state = { observer, pendingByCard, observedCards }; + state.observer = observer; containerState.set(container, state); return state; } @@ -297,6 +301,47 @@ function calculateDistanceSquared(entry, viewportCenter) { return dx * dx + dy * dy; } +function computeVisibilityPriority({ ratio, distance }) { + const normalizedRatio = Number.isFinite(ratio) + ? Math.min(Math.max(ratio, 0), 1) + : 0; + const distanceScore = Number.isFinite(distance) ? 1 / (1 + distance) : 0; + return normalizedRatio * 1000 + distanceScore; +} + +function prioritizeEntries(entries, viewportCenter) { + if (!Array.isArray(entries) || entries.length === 0) { + return []; + } + return entries + .filter((entry) => entry.isIntersecting && entry.target instanceof HTMLElement) + .map((entry) => { + const ratio = + typeof entry.intersectionRatio === "number" ? entry.intersectionRatio : 0; + const distance = calculateDistanceSquared(entry, viewportCenter); + const priority = computeVisibilityPriority({ ratio, distance }); + return { entry, ratio, distance, priority }; + }) + .sort((a, b) => { + if (b.priority !== a.priority) { + return b.priority - a.priority; + } + return a.distance - b.distance; + }); +} + +function processObserverEntries(entries, state) { + if (!state || !entries || entries.length === 0) { + return; + } + const viewportCenter = getViewportCenter(); + const prioritized = prioritizeEntries(entries, viewportCenter); + prioritized.forEach(({ entry, priority }) => { + const card = entry.target; + handleCardVisible({ card, pendingByCard: state.pendingByCard, priority }); + }); +} + function getIntersectionRect(entry) { if (!entry) { return null; @@ -395,7 +440,7 @@ function setBadge(card, state, details) { } } -function handleCardVisible({ card, pendingByCard }) { +function handleCardVisible({ card, pendingByCard, priority = 0 }) { if (!(card instanceof HTMLElement)) { return; } @@ -428,7 +473,7 @@ function handleCardVisible({ card, pendingByCard }) { setBadge(card, "checking"); - const probePromise = queueProbe(magnet, infoHash); + const probePromise = queueProbe(magnet, infoHash, priority); pendingByCard.set(card, probePromise); @@ -481,6 +526,7 @@ export function attachHealthBadges(container) { setBadge(card, "unknown"); } }); + processObserverEntries(state.observer.takeRecords(), state); } export function refreshHealthBadges(container) { @@ -491,9 +537,6 @@ export function refreshHealthBadges(container) { if (!state) { return; } - state.observer.takeRecords().forEach((entry) => { - if (entry.isIntersecting) { - handleCardVisible({ card: entry.target, pendingByCard: state.pendingByCard }); - } - }); + const records = state.observer.takeRecords(); + processObserverEntries(records, state); }