mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-10-28 16:32:28 +00:00
Makes the card liveness-visibility behavior switchable so it can be A/B feel-tested, and probes ahead of the scroll for a snappier result. - CARD_LIVENESS_POLICY (config/instance-config.js): "show-pending" (default, unchanged) | "hide-foreign" (foreign/ingested cards hidden until a source verifies; native cards stay show-pending) | "hide-all". Owner's own cards are always visible. Flip live for testing without a rebuild: window.__BITVID_CARD_LIVENESS_POLICY__ = "hide-foreign" then refresh/scroll. Implemented via a data-foreign provenance flag on the card + getCardLivenessPolicy() in cardSourceVisibility.js. - LIVENESS_PROBE_PREFETCH_MARGIN (default 600px): both liveness probes were already viewport-gated (IntersectionObserver, on-screen-first, WebTorrent priority-queued + concurrency-capped); this expands their rootMargin so cards just below the fold verify before the user reaches them. - Tests: three policy scenarios in tests/video-card-source-visibility.test.mjs; also un-broke 3 pre-existing VideoCard tests by adding the missing IntersectionObserver JSDOM stub. Co-Authored-By: Claude Opus 4.8 <[email protected]>
77 lines
4.3 KiB
Markdown
77 lines
4.3 KiB
Markdown
# Video Liveness / Card Health — Dev Plan
|
||
|
||
Status: **planned** (investigated; not started). Branch: `unstable`.
|
||
|
||
## Goal
|
||
Cards should only render videos that can actually play, decide fast, support
|
||
multiple source links (fall back if one is down), and never show two of the same
|
||
video (handled separately by cross-ecosystem dedup). Prefer CDN; if CDN is dead
|
||
but a WebTorrent source is live, keep the card.
|
||
|
||
## What already works (don't rebuild)
|
||
- **Hide/show policy is correct** (`js/utils/cardSourceVisibility.js`): a card
|
||
hides only when **neither** CDN (`urlHealthState`) **nor** WebTorrent
|
||
(`streamHealthState`) is healthy (and neither is pending); it un-hides the
|
||
moment WebTorrent flips `healthy`. This already matches "no CDN → block; if a
|
||
webtorrent flips green → bring it back."
|
||
- Owner's own videos bypass hiding.
|
||
|
||
## The real problems (diagnosis)
|
||
1. **Too slow.** `URL_PROBE_TIMEOUT_MS = 8s` (+ a retry), WebTorrent
|
||
`PROBE_TIMEOUT_MS = 20s`. While a probe is *pending*, the card is **shown**
|
||
(pending ≠ hidden). So unplayable videos sit visible for many seconds before
|
||
being hidden. → tune timeouts + concurrency for a faster verdict.
|
||
2. **Probe accuracy.** The CDN check races a `HEAD {mode:"no-cors"}` which is
|
||
**opaque** (status unreadable) for cross-origin hosts (most NIP-71 CDNs like
|
||
`relay.towardsliberty.com`). Opaque without `confirmPlayable` → `unknown` (not
|
||
healthy). Whether a card shows then hinges on timing/streamState. Net: foreign
|
||
CDNs are judged unreliably. → use the **video-element probe** (`confirmPlayable`,
|
||
actually loads the media) as the source of truth for the card verdict.
|
||
3. **Single source only.** The ingest adapter keeps ONE `url` (the primary imeta
|
||
variant) and drops the rest. NIP-71 events can list **multiple imeta** (mirrors
|
||
/ alternate media). Example: Walker/THE Bitcoin Podcast event has a `video/mp4`
|
||
imeta AND an `audio/mpeg` imeta; other events may list multiple video mirrors.
|
||
→ capture all playable sources and fall back across them.
|
||
|
||
## Plan
|
||
|
||
### Phase 1 — Multi-source capture (adapter + video model)
|
||
- `buildVideoFromNip71Event` (and the 30078 path where applicable) should expose a
|
||
`sources` list: every imeta `url` whose `m` is a video type (and the magnet),
|
||
in order, each with `{ url, mimeType, sha256, dim, duration }`. Keep the primary
|
||
`url`/`magnet` for back-compat; add `sources` for fallback.
|
||
- Skip non-video media (e.g. `audio/mpeg`) for the *video* player, but we may keep
|
||
audio as a separate affordance later (out of scope).
|
||
|
||
### Phase 2 — Liveness uses real playability across sources
|
||
- The card URL probe should try sources **in order** via the video-element probe
|
||
(`confirmPlayable`); `healthy` if ANY source loads; `offline` only if ALL fail.
|
||
- Cache per-source results (already have a probe cache) so scrolling doesn't
|
||
re-probe.
|
||
- Player playback should consume the same ordered `sources` so the one that
|
||
probed healthy is what plays (and can fail over at play time too).
|
||
|
||
### Phase 3 — Speed
|
||
- Lower `URL_PROBE_TIMEOUT_MS` (8s → ~3–4s) and the video-element probe timeout;
|
||
keep one retry only for `timeout` (not `error`). Consider bumping URL-probe
|
||
concurrency (it's cheap vs WebTorrent swarm joins).
|
||
- WebTorrent `PROBE_TIMEOUT_MS` (20s): keep generous (swarm joins are slow) but
|
||
ensure it runs in parallel and never blocks the CDN verdict.
|
||
- DECISION (2026-06-24): made **config-driven for live A/B testing** instead of
|
||
hard-coding one policy. `CARD_LIVENESS_POLICY` =
|
||
`show-pending` (default) | `hide-foreign` | `hide-all`, flippable live via
|
||
`window.__BITVID_CARD_LIVENESS_POLICY__`. Recommendation stands: `hide-foreign`
|
||
(native = show-pending, foreign/ingested = hidden-until-verified). Also added a
|
||
configurable probe prefetch margin (`LIVENESS_PROBE_PREFETCH_MARGIN`, default
|
||
600px) so cards verify before they scroll into view. Pick the final default
|
||
after feel-testing.
|
||
|
||
## Reuse map
|
||
- `js/ui/urlHealthController.js` (probeUrl / probeUrlWithVideoElement) — make
|
||
it iterate sources + prefer the video-element verdict.
|
||
- `js/gridHealth.js` (WebTorrent swarm probe) — timing only.
|
||
- `js/utils/cardSourceVisibility.js` — policy already correct; no change expected.
|
||
- `js/nostr/nip71IngestAdapter.js` — add `sources`.
|
||
- `js/constants.js` — `URL_PROBE_TIMEOUT_MS`, probe concurrency.
|
||
- Player/playback service — consume `sources` for fail-over.
|