From 83dd3a97384b88c22d9ac2f1bd3647953f2b55d2 Mon Sep 17 00:00:00 2001 From: thePR0M3TH3AN <53631862+PR0M3TH3AN@users.noreply.github.com> Date: Sun, 1 Feb 2026 12:23:02 -0500 Subject: [PATCH] Add magnet webseeds to torrent fallback --- js/services/playbackService.js | 66 +++++++++++++++++++++++++++++++++- 1 file changed, 65 insertions(+), 1 deletion(-) diff --git a/js/services/playbackService.js b/js/services/playbackService.js index 6b665a31..3a35e226 100644 --- a/js/services/playbackService.js +++ b/js/services/playbackService.js @@ -1,4 +1,5 @@ import { userLogger } from "../utils/logger.js"; +import { safeDecodeURIComponent } from "../utils/safeDecode.js"; import { PLAYBACK_START_TIMEOUT } from "../constants.js"; // js/services/playbackService.js @@ -66,6 +67,43 @@ const AUTH_STATUS_CODES = new Set([401, 403]); const SSL_ERROR_PATTERN = /(ssl|cert|certificate)/i; const CORS_ERROR_PATTERN = /cors/i; const PROBE_CACHE_TTL_MS = 45000; +const WEBSEED_PARAM_KEYS = new Set(["ws", "webseed"]); + +const extractWebSeedsFromMagnet = (magnetUri) => { + if (typeof magnetUri !== "string") { + return []; + } + const trimmed = magnetUri.trim(); + if (!trimmed) { + return []; + } + const [withoutFragment] = trimmed.split("#", 1); + const [, query = ""] = withoutFragment.split("?", 2); + if (!query) { + return []; + } + + const webSeeds = []; + for (const segment of query.split("&")) { + if (!segment) { + continue; + } + const [rawKey, rawValue = ""] = segment.split("=", 2); + if (!rawKey) { + continue; + } + const key = rawKey.trim().toLowerCase(); + if (!WEBSEED_PARAM_KEYS.has(key)) { + continue; + } + const decodedValue = safeDecodeURIComponent(rawValue.replace(/\+/g, "%20")); + const candidate = decodedValue.trim(); + if (candidate) { + webSeeds.push(candidate); + } + } + return webSeeds; +}; const getHostedUrlFailureDetails = (probeResult = {}) => { const outcome = probeResult?.outcome || "error"; @@ -775,7 +813,33 @@ class PlaybackSession extends SimpleEventEmitter { let cleanupHostedUrlStatusListeners = () => {}; const httpsUrl = this.sanitizedUrl; - const webSeedCandidates = httpsUrl ? [httpsUrl] : []; + const webSeedCandidates = []; + const webSeedSet = new Set(); + + const addWebSeedCandidate = (candidate) => { + if (typeof candidate !== "string") { + return; + } + const trimmed = candidate.trim(); + if (!trimmed) { + return; + } + const key = trimmed.toLowerCase(); + if (webSeedSet.has(key)) { + return; + } + webSeedSet.add(key); + webSeedCandidates.push(trimmed); + }; + + if (httpsUrl) { + addWebSeedCandidate(httpsUrl); + } + + const magnetWebSeeds = extractWebSeedsFromMagnet(this.magnetForPlayback); + if (magnetWebSeeds.length > 0) { + magnetWebSeeds.forEach((seed) => addWebSeedCandidate(seed)); + } if (webSeedCandidates.length > 0) { this.service.log(