fix(liveness): don't bury WebTorrent-only content under hide-until-verified

A WebTorrent-only card's sole liveness signal is the swarm probe (20s timeout,
5min cache), and P2P liveness is slow + intermittent — a live-but-slow video is
indistinguishable from a dead one until that probe finishes. Hiding such a card
until the swarm verifies could bury perfectly good P2P content for minutes.

hide-until-verified now only suppresses a card while the FAST, reliable CDN probe
is still running. Once the CDN resolves dead (or is absent, i.e. P2P-only), it
falls back to show-pending for the WebTorrent part: shown while the swarm is
checked, hidden only once it too is confirmed dead. Burying live content is worse
than a brief flash of a dead one, and only the slow P2P probe is at issue.
cardNeedsEagerLivenessProbe likewise skips P2P-only cards (they start visible).

Tests: hide-foreign does-not-bury-WebTorrent-only coverage in
tests/video-card-source-visibility.test.mjs.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
This commit is contained in:
thePR0M3TH3ANandClaude Opus 4.8 committed 2026-06-25 09:48:55 -04:00
1 parent 129d877d38
commit 2e510b2330
2 files changed
+79 -13

No files matched your search

+31 -12
View File
@@ -13,13 +13,16 @@ export function cardNeedsEagerLivenessProbe(cardLike) {
return false;
}
const policy = getCardLivenessPolicy();
if (policy === "hide-all") {
return true;
const gated =
policy === "hide-all" ||
(policy === "hide-foreign" && card.dataset.foreign === "true");
if (!gated) {
return false;
}
if (policy === "hide-foreign") {
return card.dataset.foreign === "true";
}
return false;
// Only cards backed by a fast CDN probe are hidden-until-verified (so they need
// eager probing while hidden). P2P-only cards fall back to show-pending — they
// start visible and are probed normally on scroll, so no eager kick is needed.
return Boolean(card.dataset.urlHealthUrl);
}
function resolveCardElement(cardLike) {
@@ -99,12 +102,28 @@ export function updateVideoCardSourceVisibility(cardLike) {
const hideUntilVerified =
policy === "hide-all" || (policy === "hide-foreign" && isForeign);
const shouldHide = hideUntilVerified
? // Hidden until at least one source proves playable (covers the pending
// window too, so a dead foreign stranger never flashes in).
!cdnHealthy && !streamHealthy
: // Default: only hide once every source has come back dead.
!cdnHealthy && !streamHealthy && !cdnPending && !streamPending;
// Default policy: hide only once every source has come back dead.
const confirmedDead =
!cdnHealthy && !streamHealthy && !cdnPending && !streamPending;
let shouldHide;
if (!hideUntilVerified) {
shouldHide = confirmedDead;
} else if (cdnHealthy || streamHealthy) {
shouldHide = false; // proven playable → show
} else if (cdnPending) {
// Still waiting on the FAST, reliable CDN probe (~4s) — keep it hidden so a
// dead foreign card never flashes in.
shouldHide = true;
} else {
// CDN is resolved dead (or absent) and the only remaining signal is the slow,
// unreliable WebTorrent swarm probe (20s + 5min cache). Do NOT bury the card
// behind it — a live-but-slow P2P video is indistinguishable from a dead one
// until that probe finishes, and burying live content is worse than a brief
// flash. Fall back to show-pending for the stream: show while it's checked,
// hide only once it too is confirmed dead.
shouldHide = !streamHealthy && !streamPending;
}
if (shouldHide) {
if (!card.hidden) {
+48 -1
View File
@@ -160,13 +160,25 @@ test("VideoCard hides cards without playable sources until a healthy CDN update
assert.equal(root.dataset.sourceVisibility, "visible", "visibility dataset should reflect the restored state");
});
function makeCard(document, { foreign = "false", owner = "false", url = "checking", stream = "checking" } = {}) {
function makeCard(
document,
{
foreign = "false",
owner = "false",
url = "checking",
stream = "checking",
cdnUrl = "https://cdn.example/v.mp4", // pass "" for a WebTorrent-only card
} = {}
) {
const card = document.createElement("article");
card.classList.add("card");
card.dataset.ownerIsViewer = owner;
card.dataset.foreign = foreign;
card.dataset.urlHealthState = url;
card.dataset.streamHealthState = stream;
if (cdnUrl) {
card.dataset.urlHealthUrl = encodeURIComponent(cdnUrl);
}
document.body.appendChild(card);
return card;
}
@@ -225,6 +237,41 @@ test("CARD_LIVENESS_POLICY=hide-all hides every pending non-owner card but never
assert.equal(nativeCard.hidden, false, "card appears once a source verifies");
});
// WebTorrent-only foreign content must NOT get buried under hide-foreign: its
// only signal is the slow/unreliable swarm probe (20s + 5min cache), so a
// live-but-slow P2P video would otherwise stay hidden. It falls back to
// show-pending instead (shown while checked, hidden only once confirmed dead).
test("hide-foreign does not bury WebTorrent-only foreign cards (no CDN to verify fast)", (t) => {
const { document } = setupDom(t);
setCardLivenessPolicy("hide-foreign");
t.after(() => setCardLivenessPolicy("show-pending"));
// No CDN url → urlHealthState resolves to "offline"; stream probe still running.
const p2pCard = makeCard(document, {
foreign: "true",
cdnUrl: "",
url: "offline",
stream: "checking",
});
updateVideoCardSourceVisibility(p2pCard);
assert.equal(p2pCard.hidden, false, "P2P-only foreign card is shown while its swarm is checked, not buried");
assert.equal(
cardNeedsEagerLivenessProbe(p2pCard),
false,
"P2P-only card is show-pending, so it is not eager-probed",
);
// Only once the swarm probe also confirms it dead does it hide.
p2pCard.dataset.streamHealthState = "unhealthy";
updateVideoCardSourceVisibility(p2pCard);
assert.equal(p2pCard.hidden, true, "confirmed-dead P2P card finally hides");
// A CDN-backed foreign card, by contrast, IS hidden while its fast probe runs.
const cdnCard = makeCard(document, { foreign: "true", url: "checking", stream: "checking" });
updateVideoCardSourceVisibility(cdnCard);
assert.equal(cdnCard.hidden, true, "CDN-backed foreign card stays hidden during its fast probe");
});
// Hide-until-verified cards start display:none, so the viewport observer can't
// fire for them — they must be eager-probed. This predicate gates that, and it
// must mirror the visibility policy exactly so the right cards get probed.