Merge pull request #2646 from PR0M3TH3AN/agents/daily/docs-code-investigator-2026-02-20-14386379861585929806

[docs-code-investigator] Document PlaybackService
This commit is contained in:
thePR0M3TH3AN authored and GitHub committed 2026-02-20 16:37:23 -05:00
commit 05701bf3d9
5 files changed
+152

No files matched your search

+14
View File
@@ -0,0 +1,14 @@
# Documentation Plan for js/services/playbackService.js
**File:** `js/services/playbackService.js`
**Lines:** ~650
**Reason:** Critical component for "Hybrid Playback Strategy", complex state machine (`PlaybackSession`), and fallback logic. Contains long methods like `execute()` that need flow documentation.
**Plan:**
1. **Analyze**: Understand `PlaybackService` and `PlaybackSession` interactions.
2. **Document**:
* Add JSDoc to `PlaybackService` methods (`probeHostedUrl`, `registerUrlPlaybackWatchdogs`, etc.).
* Add JSDoc to `PlaybackSession` methods (`start`, `execute`).
* Add flow comments to `execute()` explaining the fallback sequence.
3. **Overview**: Create `docs/playbackService-overview.md` with sequence diagrams (text) and usage examples.
4. **Verify**: Run `npm run lint` and `npm run test:unit`.
@@ -0,0 +1,20 @@
# Agent: docs-code-investigator
# Status: Completed
# Date: 2026-02-20
## Summary
Documented `js/services/playbackService.js` to clarify the "Hybrid Playback Strategy" and fallback logic.
## Changes
- **Modified:** `js/services/playbackService.js` (Added JSDoc and flow comments)
- **Created:** `docs/playbackService-overview.md` (Overview, flow, and usage examples)
- **Created:** `context/CONTEXT_2026-02-20_18-46-31.md`
- **Created:** `test_logs/TEST_LOG_2026-02-20_18-46-31.md`
## Verification
- `npm run lint`: Passed
- `npm run test:unit`: Passed (8 tests in `playbackService.test.mjs`, 5 in `playbackService_forcedSource.test.mjs`, 5 in `playbackService_order.test.mjs`)
## Decisions
- Chose `playbackService.js` because it is a critical component for mission #2 (Hybrid Playback Strategy) and lacked comprehensive JSDoc and flow documentation.
- Created an overview document due to the complexity of the state machine.
+75
View File
@@ -0,0 +1,75 @@
# PlaybackService Overview
**File:** `js/services/playbackService.js`
The `PlaybackService` is the central orchestrator for video playback in bitvid. It implements the "Hybrid Playback Strategy", ensuring users get the fastest possible start (via direct URL) while maintaining the censorship-resistance of P2P (via WebTorrent) as a seamless fallback.
## Key Concepts
### 1. Hybrid Playback Strategy
The goal is to play video from a standard HTTPS URL (CDN/Server) first because it offers instant seek and low latency. If that fails (404, blocked, or stalls), the system automatically switches to the WebTorrent engine to stream the content from the P2P network using the magnet link.
### 2. PlaybackSession
Each attempt to play a video creates a `PlaybackSession`. This object encapsulates the state machine for that specific video request.
- **Request Signature:** A unique ID derived from the URL and Magnet. Used to prevent race conditions (e.g., user clicks the same video twice, or switches videos rapidly).
- **Watchdogs:** Event listeners attached to the `<video>` element that monitor for stalls, errors, or network timeouts. If a watchdog barks, the session triggers a fallback.
## Execution Flow
The `execute()` method in `PlaybackSession` drives the logic:
### URL-First (Default)
1. **Probe:** Send a HEAD request to the HTTPS URL to check reachability and CORS status.
2. **Play:** If reachable, set `video.src = url` and attempt playback.
3. **Monitor:** Attach watchdogs.
* **Success:** If `playing` event fires and progress continues, keep playing.
* **Failure:** If `error`, `stalled`, or `timeout` occurs, stop playback and trigger **Torrent Fallback**.
4. **Fallback:** Initialize WebTorrent with the magnet link.
### Torrent-First (Configurable)
If `urlFirstEnabled` is false or the user explicitly selects "P2P":
1. **Torrent:** Start WebTorrent immediately.
2. **Fallback:** If no peers are found within a timeout, fall back to the HTTPS URL (if available).
## Public API
### `playbackService.createSession(options)`
Creates a new session.
- `url`: HTTPS URL string.
- `magnet`: Magnet URI string.
- `videoElement`: The HTMLVideoElement to control.
- `forcedSource`: Optional 'url' or 'torrent' override.
### `session.start()`
Begins the playback flow. Returns a `Promise` that resolves when playback successfully starts (from either source) or rejects if both fail.
### `session.cleanupWatchdog()`
Removes all event listeners and timers. Automatically called when a session ends or is replaced.
## Example Usage
```javascript
import { playbackService } from './services/playbackService.js';
// 1. Create a session
const session = playbackService.createSession({
url: 'https://example.com/video.mp4',
magnet: 'magnet:?xt=urn:btih:...',
videoElement: document.querySelector('video'),
// Optional callbacks
onSuccess: () => console.log('Playback started!'),
onFallback: (reason) => console.log('Switched to torrent due to:', reason)
});
// 2. Start playback
try {
const result = await session.start();
console.log(`Playing via: ${result.source}`); // 'url' or 'torrent'
} catch (err) {
console.error('Playback failed completely:', err);
}
```
## When to Refactor
- If the `execute()` method grows beyond 500 lines, consider splitting the URL and Torrent logic into separate strategy classes (`UrlPlaybackStrategy`, `TorrentPlaybackStrategy`).
- If the "Watchdog" logic becomes more complex (e.g., adaptive bitrates), extract it into a dedicated `PlaybackMonitor` class.
+27
View File
@@ -352,6 +352,15 @@ export class PlaybackService {
this.probeCache.set(cacheKey, { result, expiresAt: now + ttl });
}
/**
* Probes a hosted URL to check for reachability (usually via a HEAD request).
*
* @param {Object} options
* @param {string} [options.url] - The HTTPS URL to probe.
* @param {string} [options.magnet] - Optional magnet context for caching keys.
* @param {Function} [options.probeUrl] - The actual probe function (returns { outcome: 'ok'|'bad'|'timeout'|'error', status?: number }).
* @returns {Promise<{outcome: string, status?: number, error?: Error}>}
*/
async probeHostedUrl({ url, magnet, probeUrl } = {}) {
const sanitizedUrl = typeof url === "string" ? url.trim() : "";
if (!sanitizedUrl) {
@@ -537,6 +546,19 @@ export class PlaybackService {
}
}
/**
* Creates a new playback session for a specific video request.
*
* @param {Object} options
* @param {string} [options.url] - The hosted video URL.
* @param {string} [options.magnet] - The WebTorrent magnet link.
* @param {string} [options.requestSignature] - Unique identifier for deduplication.
* @param {HTMLVideoElement} [options.videoElement] - Target video element.
* @param {Function} [options.probeUrl] - Probe function override.
* @param {Function} [options.playViaWebTorrent] - Torrent handler.
* @param {string} [options.forcedSource] - 'url' or 'torrent' to skip priority checks.
* @returns {PlaybackSession} The new session instance.
*/
createSession(options = {}) {
const session = new PlaybackSession(this, {
...options,
@@ -727,6 +749,8 @@ class PlaybackSession extends SimpleEventEmitter {
/**
* Begins the playback flow. Returns a promise that resolves when playback
* has either successfully started (URL or Torrent) or fatally failed.
*
* @returns {Promise<{source: 'url'|'torrent'|null, error?: Error}>}
*/
async start() {
if (this.startPromise) {
@@ -738,6 +762,7 @@ class PlaybackSession extends SimpleEventEmitter {
/**
* The core execution loop for the session.
*
* Flow:
* 1. Check if forced source (e.g. user manually switched to "Torrent")
* 2. If URL available & URL-first enabled:
@@ -746,6 +771,8 @@ class PlaybackSession extends SimpleEventEmitter {
* c. Attach watchdogs.
* d. If watchdog triggers (stall/error), trigger `startTorrentFallback`.
* 3. If URL fails or not available, call `startTorrentFallback`.
*
* @returns {Promise<{source: 'url'|'torrent'|null, error?: Error}>}
*/
async execute() {
const {
+16
View File
@@ -0,0 +1,16 @@
# Test Log 2026-02-20
**Agent:** docs-code-investigator
**Target:** js/services/playbackService.js
## Lint
`npm run lint` passed (ignoring asset check warnings).
## Unit Tests
`tests/services/playbackService.test.mjs`: Passed (8 tests)
`tests/services/playbackService_forcedSource.test.mjs`: Passed (5 tests)
`tests/services/playbackService_order.test.mjs`: Passed (5 tests)
## Manual Verification
- Verified JSDoc syntax via visual inspection.
- Verified flow comments in `execute()`.