Files
bitvid/dist/docs/playback-fallback.md
T
google-labs-jules[bot]andPR0M3TH3AN 0bfa3b08e8 fix(ci): fix netlify deployment by building to dist
- Move from root publishing to clean `dist/` directory
- Create `scripts/build-dist.mjs` to orchestrate build and copy assets
- Update `package.json` build script
- Update `netlify.toml` to publish `dist/`
- Ignore `dist/` in `.gitignore`

Co-authored-by: PR0M3TH3AN <[email protected]>
2026-01-29 09:07:15 +00:00

5.4 KiB
Raw Blame History

URL-first playback fallback orchestration

This document explains how bitvidApp.playVideoWithFallback 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 }) 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

sequenceDiagram
  participant App as bitvidApp (js/app.js)
  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.