mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-03-11 21:40:48 +00:00
Update contributor docs for URL-first playback
This commit is contained in:
279
AGENTS.md
279
AGENTS.md
@@ -1,263 +1,66 @@
|
||||
# Bitvid — AI Agent Guide (AGENTS.md)
|
||||
# Bitvid — AI Agent Guide
|
||||
|
||||
This document tells AI agents how Bitvid works **now** and how to extend it going forward. It focuses on the newly adopted **URL‑first playback with WebTorrent fallback** strategy, minimal code touchpoints, and reliable interop with the Nostr ecosystem.
|
||||
|
||||
As you work in the repo, please add detailed comments to help others understand what and why your code is doing.
|
||||
This guide tells AI agents how to keep Bitvid aligned with the current product direction. Follow it whenever you touch code, content, or documentation inside this repository.
|
||||
|
||||
---
|
||||
|
||||
## 1) Mission & Product Direction
|
||||
## 1. Mission: URL‑First Playback with WebTorrent Fallback
|
||||
|
||||
**Goal:** Stream videos reliably and cheaply.
|
||||
|
||||
* **Primary transport:** a **hosted video URL** (MP4/WebM/HLS/DASH) that plays directly in the browser.
|
||||
* **Secondary transport (fallback & cost control):** a **WebTorrent magnet** enhanced with **`ws=` (web seed)** and **`xs=` (.torrent URL)**.
|
||||
* **Behavior:** Try `url` first. If it’s missing or unreachable, **fallback to WebTorrent** using the same post’s magnet. If the magnet includes `ws`/`xs`, WebTorrent can bootstrap via HTTP and/or peers.
|
||||
* **Nostr interop:** Keep existing kind `30078` events for Bitvid’s app data; optionally **mirror a NIP‑94 kind `1063`** event so other clients can discover the same hosted video URL.
|
||||
* **Resilience:** Light link‑health probing to hide/flag dead URLs and avoid broken playback.
|
||||
* **Goal:** Always deliver smooth playback while keeping hosting costs low.
|
||||
* **Primary transport:** A hosted video URL (MP4/WebM/HLS/DASH) that the `<video>` element can stream directly.
|
||||
* **Fallback transport:** A WebTorrent magnet that includes browser‑safe trackers plus optional HTTP hints.
|
||||
* **Runtime behavior:** Call `playVideoWithFallback({ url, magnet })` (see `js/playbackUtils.js`). It probes the URL first; only when that fails should WebTorrent start.
|
||||
* **Content contract:** Bitvid posts (Nostr kind `30078`) must include a `title` and at least one of `url` or `magnet`. Prefer to publish both along with optional `thumbnail`, `description`, and `mode` fields. When mirroring to NIP‑94 (`kind 1063`), copy the hosted URL and (optionally) the magnet so other clients can discover the same asset.
|
||||
|
||||
---
|
||||
|
||||
## 2) High‑Level Architecture
|
||||
## 2. Magnet Handling — Do’s & Don’ts
|
||||
|
||||
* **Frontend app** (vanilla JS + HTML/CSS)
|
||||
|
||||
* **Upload modal** accepts: Title (required), **URL or Magnet** (at least one required), Thumbnail, Description. Optional fields for **`ws`** and **`xs`** help P2P bootstrap.
|
||||
* **Feed / Cards** render video posts, each carrying both `data-play-url` and `data-play-magnet` attributes.
|
||||
* **Player** uses `playVideoWithFallback({ url, magnet })`: probe `url`; if OK, play via `<video>`; else call `torrentClient.streamVideo(magnet, videoEl)`.
|
||||
* **Nostr client**
|
||||
|
||||
* Publishes Bitvid’s **kind `30078`** events with content `{ version, title, url?, magnet?, thumbnail?, description?, mode }`.
|
||||
* Subscribes to Bitvid video events and (optionally) **NIP‑94 `kind 1063`** to interop with external clients.
|
||||
* **Optional Upload API** (future): If adding server‑side upload (NIP‑96/98), the client would POST to an uploader, receive an HTTPS URL, then publish events.
|
||||
* **Use the helpers:** Always pass inbound values through `safeDecodeMagnet()` before playback, and use `normalizeAndAugmentMagnet()` (see `js/magnetUtils.js`) to append `ws=` or `xs=` hints.
|
||||
* **Do keep magnets raw:** Store and reuse the literal `magnet:?xt=urn:btih:...` string. Decode once, then feed that exact value to WebTorrent.
|
||||
* **Don’t call `new URL()` on magnets:** URL constructors/`URLSearchParams` percent‑encode the `xt` payload and can break legacy hashes. Manipulate magnets via the helper utilities or string concatenation.
|
||||
* **Do encourage HTTP hints:** `ws=` (web seed) should point to an HTTPS file root; `xs=` points to a `.torrent`. They help the fallback path warm up quickly.
|
||||
* **Don’t ship insecure trackers:** Stick to the WSS tracker list exported from `js/constants.js`. Avoid UDP or plaintext HTTP trackers in browser code.
|
||||
|
||||
---
|
||||
|
||||
## 3) Data Contracts
|
||||
## 3. Upload Modal Troubleshooting
|
||||
|
||||
### 3.1 Bitvid app event (kind `30078`)
|
||||
|
||||
**Content JSON** (superset; some fields optional):
|
||||
|
||||
```json
|
||||
{
|
||||
"version": 2,
|
||||
"title": "<string>",
|
||||
"url": "https://cdn.example.com/video.mp4", // optional but recommended
|
||||
"magnet": "magnet:?xt=urn:btih:...&tr=...", // optional but recommended
|
||||
"thumbnail": "https://.../thumb.jpg", // optional
|
||||
"description": "<string>", // optional
|
||||
"mode": "live" | "dev" // optional
|
||||
}
|
||||
```
|
||||
|
||||
**Validation:**
|
||||
|
||||
* `title` **required**; **at least one** of `url` **or** `magnet` **required**.
|
||||
* If `magnet` present and uploader supplied `ws` and/or `xs`, append them to the magnet before publish.
|
||||
|
||||
### 3.2 NIP‑94 mirror (kind `1063`, optional but encouraged)
|
||||
|
||||
**Tags (typical):**
|
||||
|
||||
* `["url", "https://cdn.example.com/video.mp4"]`
|
||||
* `["m", "video/mp4"]` (or `video/webm`, `application/x-mpegURL`, etc.)
|
||||
* `["thumb", "https://.../thumb.jpg"]` (optional)
|
||||
* `["alt", "<title or description>"]` (optional)
|
||||
* `["size", "<bytes>"]` (optional)
|
||||
* `["x", "<sha256-of-file>"]` (optional integrity)
|
||||
* `["magnet", "magnet:?xt=...&ws=...&xs=..."]` (optional for P2P aware clients)
|
||||
|
||||
> Agents SHOULD keep Bitvid’s 30078 as the source of truth, and publish a **lossless mirror** into 1063 when possible.
|
||||
1. **Required fields:** Validation must ensure a title exists and that either a hosted URL or a magnet (or both) is supplied. Optional `ws` / `xs` inputs should be appended only when a magnet is present.
|
||||
2. **Magnet parsing errors:** When a user pastes encoded magnets, run `safeDecodeMagnet()` before normalizing. Show inline errors if decoding fails.
|
||||
3. **URL-first promise:** After submission, confirm the resulting feed item carries `data-play-url` and `data-play-magnet` attributes so playback utilities can attempt the URL first.
|
||||
4. **Mixed-content warnings:** If the page is served over HTTPS, reject `ws=` or `xs=` hints that begin with `http://` and show guidance to upgrade to HTTPS.
|
||||
5. **Telemetry hooks:** Keep existing analytics/logging untouched unless instructed. If you add new modal states, annotate them clearly in code comments.
|
||||
|
||||
---
|
||||
|
||||
## 4) UI & UX Flows
|
||||
## 4. Manual QA Checklist
|
||||
|
||||
### 4.1 Upload Flow (author)
|
||||
Run this script before shipping notable UI changes, especially around upload/playback flows. Automate when possible, but manual verification is required for releases.
|
||||
|
||||
1. Author opens **Upload modal**.
|
||||
2. Inputs **Title**, plus **either** Hosted URL **or** Magnet (or both). Optionally adds **Web seed base (ws)** and **.torrent URL (xs)**.
|
||||
3. Client **augments magnet** with `ws`/`xs` and publishes a kind `30078` event (and optionally a NIP‑94 `1063`).
|
||||
1. **Smoke test the modal**
|
||||
* Launch the site, open the Upload modal, and confirm required-field validation (title + one of URL/magnet).
|
||||
* Submit with URL only, magnet only, and both; ensure success states are clear.
|
||||
2. **URL-first playback**
|
||||
* Publish a post with both URL and magnet. Load the card and verify the player fetches the hosted URL first (check network tab or logs).
|
||||
* Temporarily block the URL (e.g., devtools throttling or offline). Confirm playback seamlessly falls back to the magnet.
|
||||
3. **Magnet hygiene**
|
||||
* Paste an encoded magnet; ensure `safeDecodeMagnet()` returns the raw value and the final string still includes the original `xt` hash.
|
||||
* Confirm `normalizeAndAugmentMagnet()` appends `ws=`/`xs=` hints without corrupting parameters.
|
||||
4. **P2P hints**
|
||||
* Verify magnets authored through the modal include HTTPS `ws=` and optional `xs=` values when provided.
|
||||
* Ensure tracker lists come from `js/constants.js` and remain WSS-only.
|
||||
5. **Cross-browser sanity**
|
||||
* Spot-check playback in Chromium and Firefox (desktop) to ensure no console errors related to CORS, Range requests, or tracker connections.
|
||||
|
||||
### 4.2 Consumption Flow (viewer)
|
||||
|
||||
1. Feed renders cards with `data-play-url` + `data-play-magnet`.
|
||||
2. On click: `playVideoWithFallback()`
|
||||
|
||||
* `probeUrl(url)` → If reachable, set `<video>.src = url` and play.
|
||||
* Else: `torrentClient.streamVideo(magnet, videoEl)`.
|
||||
3. UI hides or badges items whose `probeUrl()` fails consistently (cached temporarily).
|
||||
Document the run in PR descriptions so QA can cross-reference results.
|
||||
|
||||
---
|
||||
|
||||
## 5) Key Client Functions (reference)
|
||||
## 5. Additional Notes for Agents
|
||||
|
||||
### 5.1 `normalizeAndAugmentMagnet(raw, { webSeed?, torrentUrl?, xs?, extraTrackers? })`
|
||||
|
||||
* Accepts a magnet URI or bare hex info hash and returns a **sanitized magnet**.
|
||||
* Fixes accidentally encoded `xt=urn%3Abtih:` segments and tolerates bare hashes by
|
||||
promoting them to `magnet:?xt=urn:btih:<hash>`.
|
||||
* Appends only **browser-safe WSS trackers** plus optional extras, avoiding mixed
|
||||
content.
|
||||
* Adds HTTPS web seeds (`ws=`) and torrent hints (`xs=`) when provided, skipping
|
||||
insecure `http://` web seeds unless the app itself is served via HTTP.
|
||||
* Preserves existing parameters in their original form—no `new URL()` usage and no
|
||||
percent re-encoding of the `xt` payload.
|
||||
|
||||
### 5.2 `probeUrl(url)`
|
||||
|
||||
* `HEAD` request to check availability; expects `2xx` and (ideally) `Accept-Ranges: bytes`.
|
||||
* Fallback small `GET` with `Range: bytes=0-1023` can be used if certain hosts block `HEAD`.
|
||||
* Returns boolean `ok`.
|
||||
|
||||
### 5.3 `playVideoWithFallback({ url, magnet })`
|
||||
|
||||
* Try `playHttp(url)` if `probeUrl(url)` passes.
|
||||
* Else try `playViaWebTorrent(magnet)`.
|
||||
* Show UI error if neither source available.
|
||||
|
||||
### 5.4 `playViaWebTorrent(magnet)`
|
||||
|
||||
* Uses existing `torrentClient.streamVideo(magnet, videoEl)` under the hood.
|
||||
* Benefits if magnet contains `ws`/`xs` for bootstrap.
|
||||
|
||||
---
|
||||
|
||||
## 6) Markup Contracts
|
||||
|
||||
* **Cards:** add both attributes where applicable:
|
||||
|
||||
* `data-play-url="<encoded URL or empty>"`
|
||||
* `data-play-magnet="<raw magnet or empty>"`
|
||||
* **Click delegate:** look for a closest element bearing either attribute; decode
|
||||
both `data-play-url` and `data-play-magnet` (if encoded upstream) **before** passing
|
||||
them to playback logic. Never double-encode magnets when writing them into the DOM.
|
||||
|
||||
---
|
||||
|
||||
## 7) Main vs Unstable — legacy WebTorrent compatibility lessons
|
||||
|
||||
Legacy Bitvid posts (v1 / magnet-only) still play on **Main**. They broke on
|
||||
**Unstable** because of double-encoded magnets, URL constructors, and strict
|
||||
filters. Apply these rules to stay compatible:
|
||||
|
||||
* **Raw magnets end-to-end.** Store the literal `magnet:?xt=urn:btih:...` in the DOM
|
||||
and feed that same string into WebTorrent. If you must encode for transport, be
|
||||
sure to decode once when handling clicks.
|
||||
* **No `new URL()` for magnets.** Appending params with `URLSearchParams` or `new URL`
|
||||
percent-encodes the `xt` value and can corrupt legacy hashes. When you need to add
|
||||
trackers or cache-busting params, concatenate plain text (or use
|
||||
`normalizeAndAugmentMagnet`, which already preserves the payload).
|
||||
* **Converters should allow magnet-only posts.** A note is playable when it exposes
|
||||
either a usable `magnet` **or** a direct `url`. Don’t require `version >= 2` to keep
|
||||
legacy uploads alive.
|
||||
* **Browser-safe announce list.** Ship WSS trackers for browser playback. Skip UDP
|
||||
trackers in the client path and only add `ws=` hints when they’re HTTPS on HTTPS
|
||||
pages (mixed content gets blocked anyway).
|
||||
|
||||
---
|
||||
|
||||
## 8) Server Requirements for Hosted URLs
|
||||
|
||||
* **CORS:** allow the app origin (or `*`).
|
||||
* **Range requests:** ensure `Accept-Ranges: bytes`.
|
||||
* **Correct MIME:** `video/mp4`, `video/webm`, `application/x-mpegURL`, etc.
|
||||
* **Optional:** expose `Content-Length` for better UX/progress.
|
||||
|
||||
---
|
||||
|
||||
## 9) P2P Notes for Agents
|
||||
|
||||
* **`ws=` (web seed):** HTTP(S) base path where the file is fetchable; enables WebTorrent to download pieces even with zero peers.
|
||||
* **`xs=`:** HTTP(S) URL of the `.torrent` metadata so the client can start promptly without DHT/peer discovery.
|
||||
* **Trackers:** Keep WSS trackers for peer discovery; they reduce CDN load when peers exist.
|
||||
|
||||
---
|
||||
|
||||
## 10) Interop & Extensions
|
||||
|
||||
* **Live streams:** future option via HLS URLs (MIME `application/x-mpegURL`). For Nostr live presence, consider **NIP‑53** events alongside VOD 1063 entries.
|
||||
* **Adaptive Bitrate + P2P:** if ABR is needed with P2P, consider a segment‑sharing overlay (e.g., p2p segment loaders) rather than WebTorrent.
|
||||
* **Uploads (optional):** NIP‑96/NIP‑98 flow to obtain a hosted URL, then publish Bitvid/NIP‑94 events.
|
||||
|
||||
---
|
||||
|
||||
## 11) Testing Checklist (agents)
|
||||
|
||||
* **Form validation:** title present; ensure `(url || magnet)` is true; `ws` and `xs` appended only for magnet.
|
||||
* **Probe:** healthy URL returns `ok=true`; dead URL hides/badges card; cache prevents re‑probing every render.
|
||||
* **Playback:** URL path plays successfully; disconnect network to force WebTorrent path; verify video plays via magnet.
|
||||
* **CORS & Range:** verify no console CORS errors; confirm seeking works.
|
||||
* **Events:** verify kind `30078` contains `url` and/or `magnet`; optional 1063 mirror created correctly.
|
||||
* **Cards:** both `data-play-url` and `data-play-magnet` present; click handler calls fallback player.
|
||||
|
||||
---
|
||||
|
||||
## 12) Guardrails & Do/Don’t
|
||||
|
||||
**Do**
|
||||
|
||||
* Fail soft: when URL fails, fall back to magnet without blocking UI.
|
||||
* Prefer standards: URL + NIP‑94 mirror when possible.
|
||||
* Degrade gracefully when `ws`/`xs` missing; torrent should still work with peers.
|
||||
|
||||
**Don’t**
|
||||
|
||||
* Don’t require magnet if URL is provided (and vice versa).
|
||||
* Don’t assume CORS/Range are configured—probe and message helpfully.
|
||||
* Don’t block the main thread with large HEAD/GETs; keep probes lightweight with timeouts.
|
||||
|
||||
---
|
||||
|
||||
## 13) Quick Reference (snippets)
|
||||
|
||||
**Bitvid content object (30078)**
|
||||
|
||||
```js
|
||||
const content = {
|
||||
version: 2,
|
||||
title,
|
||||
url, // optional but preferred
|
||||
magnet, // optional but preferred
|
||||
thumbnail, // optional
|
||||
description, // optional
|
||||
mode // "live" | "dev"
|
||||
};
|
||||
```
|
||||
|
||||
**Magnet normalization & augmentation**
|
||||
|
||||
```js
|
||||
import { normalizeAndAugmentMagnet } from "./js/magnetUtils.js";
|
||||
|
||||
const { magnet } = normalizeAndAugmentMagnet(inputMagnetOrInfoHash, {
|
||||
webSeed: hostedUrl ? [hostedUrl] : [],
|
||||
torrentUrl: xsHint,
|
||||
extraTrackers: ["wss://tracker.openwebtorrent.com"],
|
||||
});
|
||||
|
||||
// Feed `magnet` directly to playVideoWithFallback / WebTorrent.
|
||||
```
|
||||
|
||||
**Playback with fallback**
|
||||
|
||||
```js
|
||||
async function playVideoWithFallback({ url, magnet }) {
|
||||
if (url && await probeUrl(url)) return playHttp(url);
|
||||
if (magnet) return playViaWebTorrent(magnet);
|
||||
throw new Error("No playable source");
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 14) Roadmap Hooks (where to extend)
|
||||
|
||||
* **Analytics:** Keep using your existing 30078 analytics/logging. Add fields for `play_source: "url"|"torrent"` to measure cost savings.
|
||||
* **Integrity:** Optionally compute SHA‑256 of the hosted file (off‑thread) and publish as NIP‑94 `["x", hash]` tag.
|
||||
* **Moderation:** Add a background task to periodically probe and mark dead links; publish a lightweight status event so other Bitvid clients learn from each other.
|
||||
|
||||
---
|
||||
* **Nostr interoperability:** Keep kind `30078` events as the source of truth and optionally mirror to kind `1063` so external clients see the hosted URL.
|
||||
* **Probing:** Lightweight `HEAD`/`GET` requests should back `probeUrl()` so dead URLs can be hidden or flagged without blocking the UI.
|
||||
* **Extensibility:** Future work (live streams, NIP-96 uploads, analytics) should preserve the URL-first strategy and magnet safety rules above.
|
||||
|
||||
**End of AGENTS.md**
|
||||
|
||||
Reference in New Issue
Block a user