docs: video-liveness plan (speed, playability probe, multi-source)

Audited the card liveness/health system. Policy (cardSourceVisibility) is
already correct — hides only when neither CDN nor WebTorrent is healthy,
un-hides when WebTorrent flips green. Documents the real gaps (slow probes;
opaque HEAD no-cors inaccuracy for cross-origin NIP-71 CDNs; single-source
adapter dropping extra imeta) and a 3-phase plan: multi-source capture,
video-element playability probe across sources, and speed tuning. Adds
docs/video-liveness-plan.md + expands pre-launch TODO #5. Planning only.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
This commit is contained in:
thePR0M3TH3ANandClaude Opus 4.8 committed 2026-06-22 21:26:20 -04:00
1 parent f760abde3d
commit 5e2b22156f
2 files changed
+90 -5

No files matched your search

+73
View File
@@ -0,0 +1,73 @@
# 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 NEEDED: **show-pending (current) vs hide-until-verified.** Showing
pending means a brief flash of soon-to-be-hidden dead cards; hide-until-verified
means good cards pop in after the probe. Recommend: keep show-pending for cards
with a *bitvid-native/whitelisted* source (fast/trusted), but **hide-until-verified
for foreign/ingested** videos (so unplayable strangers never flash in).
## 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.
+17 -5
View File
@@ -106,11 +106,23 @@ tests → `npm run build` + `npm run test:unit` green → commit + push.
double-counting, and resilience to dead relays (now that feed defaults are
reserved, ensure view reads/writes use a sane relay set too).
### 5. Card-hide / video-liveness check
- [ ] Audit the card-hide system that gates a card on a video-liveness probe
(`gridHealth.js`, probe concurrency now 4). Confirm: dead URL/magnet hides the
card correctly, a live video is never falsely hidden, and the probe can't
stampede connections (regression risk from the freeze fix).
### 5. Card-hide / video-liveness check — full plan in docs/video-liveness-plan.md
Audited (2026-06-23). Finding: the hide/show **policy is already correct**
(`cardSourceVisibility.js` hides only when neither CDN nor WebTorrent is healthy;
un-hides when WebTorrent flips green). The real gaps:
- [ ] **Speed**: `URL_PROBE_TIMEOUT_MS=8s` (+retry) and WebTorrent probe 20s mean
unplayable cards stay *visible* (pending isn't hidden) for seconds. Tune
timeouts + concurrency for a faster verdict.
- [ ] **Probe accuracy**: the CDN check races an opaque `HEAD no-cors` (status
unreadable for cross-origin NIP-71 CDNs). Use the **video-element probe**
(`confirmPlayable`, actually loads media) as the card verdict.
- [ ] **Multi-source**: ingest adapter keeps ONE url; NIP-71 events can list
multiple imeta (mirrors / alternate media). Capture all video sources +
magnet; probe/play them in order; healthy if ANY plays, offline only if ALL
fail. (Example: Walker/THE Bitcoin Podcast event — mp4 + mp3 imeta.)
- [ ] **Decision**: show-pending (current) vs hide-until-verified for foreign
ingested videos (so unplayable strangers never flash in). Recommend
hide-until-verified for foreign, show-pending for bitvid-native.
## Open — medium priority