From 7e21386bd0bca04068c88f8794541a59400e0a99 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Sun, 1 Feb 2026 15:49:46 +0000 Subject: [PATCH] fix(webtorrent): correct webseed availability check and force bootstrap This change addresses two issues with WebTorrent webseed handling: 1. **Grid Availability Check:** Modified `js/gridHealth.js` to correctly classify torrents with only webseeds as "healthy" and report 1 peer (instead of 0) so the UI availability check passes. 2. **Video Player Bootstrap:** Modified `js/webtorrent.js` (`probePeers` and `streamVideo`) to explicitly append provided webseed URLs (`urlList`) to the magnet link as `&ws=` parameters. This forces the WebTorrent client to recognize and use these webseeds as initial peers for bootstrapping the swarm. Tested via `tests/verification/verify_webseed_fix.mjs` (logic verification). Co-authored-by: PR0M3TH3AN <53631862+PR0M3TH3AN@users.noreply.github.com> --- js/gridHealth.js | 4 ++-- js/webtorrent.js | 31 ++++++++++++++++++++++++------- 2 files changed, 26 insertions(+), 9 deletions(-) diff --git a/js/gridHealth.js b/js/gridHealth.js index 0a5c8203..f4b2fa97 100644 --- a/js/gridHealth.js +++ b/js/gridHealth.js @@ -135,9 +135,9 @@ function normalizeResult(result) { const peers = Number.isFinite(result.peers) ? Math.max(0, Number(result.peers)) : 0; - const healthy = Boolean(result.healthy) && peers > 0; - const reason = typeof result.reason === "string" ? result.reason : "error"; const webseedOnly = Boolean(result.webseedOnly) && peers === 0; + const healthy = (Boolean(result.healthy) && peers > 0) || webseedOnly; + const reason = typeof result.reason === "string" ? result.reason : "error"; return { healthy, peers: healthy ? Math.max(1, peers) : peers, diff --git a/js/webtorrent.js b/js/webtorrent.js index b9cad53a..447582f7 100644 --- a/js/webtorrent.js +++ b/js/webtorrent.js @@ -222,11 +222,19 @@ export class TorrentClient { } const trackers = normalizeTrackerList(TorrentClient.PROBE_TRACKERS); - const { magnet: augmentedMagnet, appended, hasProbeTrackers } = + let { magnet: augmentedMagnet, appended, hasProbeTrackers } = appendProbeTrackers(magnet, trackers); const hasMagnetWebSeed = magnet.includes("ws=") || magnet.includes("webSeed="); const hasExplicitWebSeed = Array.isArray(urlList) && urlList.length > 0; + + if (hasExplicitWebSeed) { + const wsParams = urlList + .map((url) => `ws=${encodeURIComponent(url)}`) + .join("&"); + const separator = augmentedMagnet.includes("?") ? "&" : "?"; + augmentedMagnet = `${augmentedMagnet}${separator}${wsParams}`; + } const hasWebSeed = hasMagnetWebSeed || hasExplicitWebSeed; if (!hasProbeTrackers && !hasWebSeed) { @@ -929,7 +937,21 @@ export class TorrentClient { */ async streamVideo(magnetURI, videoElement, opts = {}) { try { - const effectiveMagnetURI = stripXsParameter(magnetURI); + let effectiveMagnetURI = stripXsParameter(magnetURI); + const candidateUrls = Array.isArray(opts?.urlList) + ? opts.urlList + .map((entry) => (typeof entry === "string" ? entry.trim() : "")) + .filter((entry) => /^https?:\/\//i.test(entry)) + : []; + + if (candidateUrls.length > 0) { + const wsParams = candidateUrls + .map((url) => `ws=${encodeURIComponent(url)}`) + .join("&"); + const separator = effectiveMagnetURI.includes("?") ? "&" : "?"; + effectiveMagnetURI = `${effectiveMagnetURI}${separator}${wsParams}`; + } + if ( effectiveMagnetURI !== (typeof magnetURI === "string" ? magnetURI.trim() : "") @@ -962,11 +984,6 @@ export class TorrentClient { } const isFirefoxBrowser = this.isFirefox(); - const candidateUrls = Array.isArray(opts?.urlList) - ? opts.urlList - .map((entry) => (typeof entry === "string" ? entry.trim() : "")) - .filter((entry) => /^https?:\/\//i.test(entry)) - : []; const chromeOptions = { strategy: "sequential" }; if (candidateUrls.length) {