mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-10-27 16:02:26 +00:00
Updates documentation to match current codebase implementation: - Deprecates `nostrClient.fetchVideos` in README.md, pointing to `subscribeVideos`. - Clarifies `playVideoWithFallback` implementation location in `docs/playback-fallback.md` and `AGENTS.md` (moved to `js/app/playbackCoordinator.js`). - Corrects `WATCH_HISTORY` d-tag description in `docs/nostr-event-schemas.md` to match actual code behavior (`<YYYY-MM>`). Co-authored-by: PR0M3TH3AN <[email protected]>
95 lines
5.5 KiB
Markdown
95 lines
5.5 KiB
Markdown
# URL-first playback fallback orchestration
|
||
|
||
This document explains how `bitvidApp.playVideoWithFallback` (implemented via `PlaybackCoordinator` in `js/app/playbackCoordinator.js` and bound to the application instance in `js/app.js`) coordinates hosted URL playback first and falls back to WebTorrent when needed. It also covers the supporting helpers in `js/services/playbackService.js`, `js/playbackUtils.js`, and `js/magnetUtils.js`, including how tracker lists and `ws=`/`xs=` hints are applied.
|
||
|
||
## High-level flow
|
||
|
||
1. `bitvidApp.playVideoWithFallback({ url, magnet })` (via `PlaybackCoordinator`) trims inputs, ensures the video modal is ready, and constructs a playback session via `playbackService.createSession(...)`.
|
||
2. The session (from `js/services/playbackService.js`) derives a canonical magnet payload using `deriveTorrentPlaybackConfig` (from `js/playbackUtils.js`).
|
||
3. Playback starts with the hosted URL when `URL_FIRST_ENABLED` is true and a URL is available; otherwise, it goes directly to torrent playback.
|
||
4. If URL playback stalls or errors, the session triggers `playViaWebTorrent` with the normalized magnet and optional web seed hints.
|
||
|
||
## Call flow into playbackService
|
||
|
||
`bitvidApp.playVideoWithFallback` performs UI preparation and delegates the decision-making to a `PlaybackSession`:
|
||
|
||
- **Session setup**
|
||
- `playbackService.createSession({ url, magnet, videoElement, probeUrl, playViaWebTorrent, ... })`
|
||
- The session captures `url`, `magnet`, and a request signature to de-duplicate duplicate calls.
|
||
- **Hosted URL probe**
|
||
- `PlaybackSession.execute()` calls `probeUrl(url)` to check availability and decides whether to attempt direct playback.
|
||
- **Hosted URL playback**
|
||
- On a successful probe, the session assigns `video.src = url` and calls `video.play()`.
|
||
- It registers watchdogs (`registerUrlPlaybackWatchdogs`) to detect stalls, errors, or aborts.
|
||
- **Fallback to WebTorrent**
|
||
- If watchdogs trigger or direct playback fails, `PlaybackSession` calls `playViaWebTorrent(magnet, { fallbackMagnet, urlList })`.
|
||
- `urlList` includes the hosted URL (if present) so it becomes a WebTorrent web seed.
|
||
|
||
## Magnet helpers and normalization
|
||
|
||
`playbackService` doesn’t manipulate magnets directly. It relies on `deriveTorrentPlaybackConfig` in `js/playbackUtils.js`, which does the following:
|
||
|
||
- Calls `safeDecodeMagnet()` from `js/magnetUtils.js` so URL-encoded magnets become raw `magnet:?xt=urn:btih:...` strings.
|
||
- Accepts bare info hashes and promotes them to full magnet URIs when needed.
|
||
- Runs `normalizeAndAugmentMagnet()` (from `js/magnetUtils.js`) to:
|
||
- Append `ws=` web seed hints when a hosted URL is present.
|
||
- Append `xs=` torrent hints when supplied.
|
||
- Ensure WSS-only tracker URLs are present.
|
||
|
||
The result is a canonical magnet used by WebTorrent, plus an optional `fallbackMagnet` that preserves the original raw input if normalization changed it.
|
||
|
||
## Tracker lists, ws/xs hints, and constants
|
||
|
||
Tracker and hint behavior is centralized in `js/constants.js` and `js/magnetUtils.js`:
|
||
|
||
- `WSS_TRACKERS` in `js/constants.js` is the canonical browser-safe tracker list. `normalizeAndAugmentMagnet()` merges this list into the magnet if trackers are missing.
|
||
- `normalizeAndAugmentMagnet()` also applies `ws=` web seed hints (from the hosted URL) and `xs=` torrent hints (from user input), enforcing HTTPS when appropriate.
|
||
- `URL_FIRST_ENABLED` (also in `js/constants.js`) controls whether the hosted URL probe is attempted before torrent fallback.
|
||
|
||
## Failure detection and hand-off points
|
||
|
||
The fallback hand-off happens in these places inside `PlaybackSession.execute()`:
|
||
|
||
1. **URL probe fails**: `probeUrl(url)` reports a bad outcome → session skips direct playback and goes straight to WebTorrent.
|
||
2. **Playback errors**: `video.play()` throws or fires `error` → session triggers the fallback.
|
||
3. **Playback stalls**: the watchdog timer (`registerUrlPlaybackWatchdogs`) detects `stalled`, `abort`, or no progress → session triggers the fallback.
|
||
4. **Autoplay blocked**: the browser blocks autoplay → session waits for a user gesture, re-enables watchdogs, and only falls back if playback still fails.
|
||
|
||
## Sequence diagram
|
||
|
||
```mermaid
|
||
sequenceDiagram
|
||
participant App as App (Coordinator)
|
||
participant Service as PlaybackService (js/services/playbackService.js)
|
||
participant Session as PlaybackSession
|
||
participant Video as HTMLVideoElement
|
||
participant Magnet as magnetUtils/playbackUtils
|
||
|
||
App->>Service: createSession({ url, magnet, videoElement, ... })
|
||
Service->>Session: new PlaybackSession(...)
|
||
Session->>Magnet: deriveTorrentPlaybackConfig()
|
||
Magnet-->>Session: normalized magnet + fallbackMagnet
|
||
|
||
Session->>App: showModalWithPoster()
|
||
Session->>App: probeUrl(url)
|
||
alt URL probe ok + URL_FIRST_ENABLED
|
||
Session->>Video: src=url; play()
|
||
Session->>Session: registerUrlPlaybackWatchdogs()
|
||
alt hosted playback succeeds
|
||
Session-->>App: sourcechange("url")
|
||
else watchdog/error triggers
|
||
Session->>App: playViaWebTorrent(magnet, { urlList })
|
||
Session-->>App: sourcechange("torrent")
|
||
end
|
||
else URL probe fails or URL-first disabled
|
||
Session->>App: playViaWebTorrent(magnet, { urlList })
|
||
Session-->>App: sourcechange("torrent")
|
||
end
|
||
```
|
||
|
||
## Implementation notes
|
||
|
||
- The playback session is responsible for cleanup and emits events (`status`, `sourcechange`, `error`, `finished`) that `bitvidApp` listens to for UI updates.
|
||
- `bitvidApp.playVideoWithFallback` keeps the current video card and modal state in sync with the normalized magnet values so copy actions and stats reflect the actual playback payload.
|
||
|