diff --git a/js/services/playbackService.js b/js/services/playbackService.js index c927ed82..e6b74bc6 100644 --- a/js/services/playbackService.js +++ b/js/services/playbackService.js @@ -1288,8 +1288,19 @@ class PlaybackSession extends SimpleEventEmitter { } } - // Fallback to URL - if (httpsUrl && forcedSource !== "torrent") { + // Fallback to URL. Even when P2P was explicitly forced, a hosted URL is a + // better outcome than a hard "No playable source found" if the torrent + // DEFINITIVELY failed (e.g. a transient companion-lookup failure or a dead + // swarm). The torrent-first attempt above already ran — and, when there was + // no URL plan B, waited indefinitely — so we only reach here on real + // failure, not on a slow-but-working swarm. + // Rollback: restore `&& forcedSource !== "torrent"` to make forced P2P hard-fail. + if (httpsUrl) { + if (forcedSource === "torrent") { + this.service.log( + "[playVideoWithFallback] Forced torrent failed; falling back to the hosted URL." + ); + } const urlResult = await attemptHostedPlayback(); if (urlResult && urlResult.source === "url") { return urlResult; diff --git a/js/webtorrent.js b/js/webtorrent.js index 87c04fe8..5264177c 100644 --- a/js/webtorrent.js +++ b/js/webtorrent.js @@ -941,7 +941,17 @@ export class TorrentClient { // Cap peer connections for the streaming torrent too. 30 is plenty to // saturate playback bandwidth while avoiding the WebRTC-handshake storm // that can peg the main thread (WebTorrent's default is 55). - const chromeOptions = { strategy: "sequential", maxConns: 30 }; + // Announce to the WSS trackers explicitly. The magnet path already carries + // trackers (playbackUtils augments it), but a .torrent-buffer add + // (torrentFileBytes, from a Blossom video's companion event) has an EMPTY + // embedded announce list — without this it would reach no tracker, discover + // no WebRTC peers, and stream from the webseed only (looks like "1 peer"). + // WebTorrent merges this with any trackers already on the source, deduped. + const chromeOptions = { + strategy: "sequential", + maxConns: 30, + announce: [...WSS_TRACKERS], + }; /** * CRITICAL: Passing `urlList` ensures the client can stream from the webseed. * Without this, videos with 0 P2P peers will fail to play even if a valid