[playback] stop the WebTorrent webseed flood on multi-file torrents

The video's hosted CDN URL was added to WebTorrent's `urlList`, but a
standalone hosted-file URL is only a valid BEP19 webseed for a SINGLE-file
torrent. For a multi-file torrent WebTorrent treats it as a base directory and
appends "<torrent name>/<file path>", producing a doubled URL that 404s on
every request — the flood seen on e.g. archive.org videos.

The CDN URL is no longer placed in urlList. It's forwarded separately as
`hostedUrlWebSeed` and attached post-metadata (handleTorrentStream) via
torrent.addWebSeed ONLY when the torrent is a single file, where the URL maps
directly to the file. Multi-file torrents get no file-pointing webseed, so no
flood. Magnet-declared `ws=` webseeds are unaffected (passed via urlList as
before). With CDN-first, dual-source videos play the CDN directly anyway, so
this only affects forced-torrent / URL-less paths.

Tests: a single-file torrent receives the hosted URL via addWebSeed; a
multi-file torrent does not. The single-file guard is mutation-verified.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
This commit is contained in:
thePR0M3TH3ANandClaude Opus 4.8 committed 2026-06-18 16:05:54 -04:00
1 parent ff5c641951
commit 006403b7b4
4 files changed
+89 -6

No files matched your search

+7 -1
View File
@@ -362,7 +362,7 @@ export function createPlaybackCoordinator(deps) {
async playViaWebTorrent(
magnet,
{ fallbackMagnet = "", urlList = [] } = {}
{ fallbackMagnet = "", urlList = [], hostedUrlWebSeed = "" } = {}
) {
const sanitizedUrlList = Array.isArray(urlList)
? urlList
@@ -371,6 +371,11 @@ export function createPlaybackCoordinator(deps) {
)
.filter((entry) => /^https?:\/\//i.test(entry))
: [];
const sanitizedHostedWebSeed =
typeof hostedUrlWebSeed === "string" &&
/^https?:\/\//i.test(hostedUrlWebSeed.trim())
? hostedUrlWebSeed.trim()
: "";
const attemptStream = async (candidate) => {
const trimmedCandidate =
@@ -418,6 +423,7 @@ export function createPlaybackCoordinator(deps) {
this.modalVideo,
{
urlList: sanitizedUrlList,
hostedUrlWebSeed: sanitizedHostedWebSeed,
hooks: {
// Surface a mid-stream stall instead of silently freezing on a
// buffering frame: tell the user whether the swarm is just slow
+7 -4
View File
@@ -868,10 +868,12 @@ class PlaybackSession extends SimpleEventEmitter {
webSeedCandidates.push(trimmed);
};
if (httpsUrl) {
addWebSeedCandidate(httpsUrl);
}
// NOTE: the hosted CDN URL is intentionally NOT added here. As a
// WebTorrent `urlList` entry it is only valid for single-file torrents;
// for multi-file torrents WebTorrent appends the torrent's internal path
// and floods the network with doubled, 404-ing requests. It is forwarded
// separately as `hostedUrlWebSeed` and attached post-metadata only when
// the torrent is a single file (see TorrentClient.handleTorrentStream).
const magnetWebSeeds = extractWebSeedsFromMagnet(this.magnetForPlayback);
if (magnetWebSeeds.length > 0) {
magnetWebSeeds.forEach((seed) => addWebSeedCandidate(seed));
@@ -948,6 +950,7 @@ class PlaybackSession extends SimpleEventEmitter {
torrentInstance = await playViaWebTorrent(this.magnetForPlayback, {
fallbackMagnet: this.fallbackMagnet,
urlList: webSeedCandidates,
hostedUrlWebSeed: httpsUrl,
});
} catch (err) {
const errorMessage = getTorrentErrorMessage(err);
+34 -1
View File
@@ -796,6 +796,12 @@ export class TorrentClient {
hooks && Number.isFinite(hooks.stallMs) && hooks.stallMs > 0
? hooks.stallMs
: TORRENT_PLAYBACK_STALL_MS;
const hostedUrlWebSeed =
hooks &&
typeof hooks.hostedUrlWebSeed === "string" &&
/^https?:\/\//i.test(hooks.hostedUrlWebSeed.trim())
? hooks.hostedUrlWebSeed.trim()
: "";
// Chrome-specific: Prune demo web seeds/trackers that chronically trip Chromium CORS
// and deliberately mutate `torrent._opts` as a sanctioned WebTorrent workaround.
@@ -853,6 +859,26 @@ export class TorrentClient {
}
}
// Attach the hosted CDN URL as a webseed ONLY for single-file torrents.
// A standalone hosted-file URL is a valid BEP19 webseed only when the
// torrent is a single file (the URL maps directly to it). For a multi-file
// torrent, WebTorrent treats the URL as a base directory and appends
// "<torrent name>/<file path>", producing a doubled URL that 404s on every
// request — the webseed flood. Adding it post-metadata (when files.length
// is known) avoids that entirely. Magnet-declared `ws=` webseeds are passed
// separately via urlList and untouched.
if (
hostedUrlWebSeed &&
files.length === 1 &&
typeof torrent.addWebSeed === "function"
) {
try {
torrent.addWebSeed(hostedUrlWebSeed);
} catch (err) {
this.log(`Failed to attach hosted webseed (${context} path):`, err);
}
}
// Satisfy autoplay requirements and keep cross-origin chunks usable (e.g., for snapshots).
videoElement.crossOrigin = "anonymous";
@@ -1106,7 +1132,14 @@ export class TorrentClient {
const isFirefoxBrowser = this.isFirefox();
const streamHooks =
opts && typeof opts.hooks === "object" && opts.hooks ? opts.hooks : {};
opts && typeof opts.hooks === "object" && opts.hooks
? { ...opts.hooks }
: {};
// Hosted CDN URL to use as a single-file webseed (handleTorrentStream
// attaches it post-metadata, only when the torrent is a single file).
if (typeof opts?.hostedUrlWebSeed === "string" && opts.hostedUrlWebSeed) {
streamHooks.hostedUrlWebSeed = opts.hostedUrlWebSeed;
}
const candidateUrls = Array.isArray(opts?.urlList)
? opts.urlList
.map((entry) => (typeof entry === "string" ? entry.trim() : ""))
+41
View File
@@ -293,4 +293,45 @@ describe("TorrentClient streaming hardening", () => {
"previous streams' error listeners are torn down (no accumulation)",
);
});
it("attaches the hosted CDN URL as a webseed for a SINGLE-file torrent", () => {
const client = freshClient();
const torrent = new MultiFileTorrent([
{ name: "v.mp4", length: 1000, streamTo: (el) => { el.src = "blob:x"; } },
]);
const added = [];
torrent.addWebSeed = (url) => added.push(url);
const videoElement = new MockVideoElement();
client.handleTorrentStream(torrent, videoElement, () => {}, () => {}, "chrome", {
hostedUrlWebSeed: "https://cdn.example.com/v.mp4",
});
assert.deepEqual(
added,
["https://cdn.example.com/v.mp4"],
"a single-file torrent gets the hosted URL as a webseed",
);
});
it("does NOT attach the hosted CDN URL for a MULTI-file torrent (no doubled-URL flood)", () => {
const client = freshClient();
const torrent = new MultiFileTorrent([
{ name: "feature.mp4", length: 100000, streamTo: (el) => { el.src = "blob:x"; } },
{ name: "extra.mp4", length: 100 },
]);
const added = [];
torrent.addWebSeed = (url) => added.push(url);
const videoElement = new MockVideoElement();
client.handleTorrentStream(torrent, videoElement, () => {}, () => {}, "chrome", {
hostedUrlWebSeed: "https://cdn.example.com/feature.mp4",
});
assert.equal(
added.length,
0,
"a multi-file torrent must NOT get a file-pointing webseed (it would double + 404)",
);
});
});