mirror of
https://gitlab.com/vyla-entertainment/documentation.git
synced 2026-11-14 22:47:26 -05:00
113 lines
5.1 KiB
Markdown
113 lines
5.1 KiB
Markdown
# Error Handling & Resilience Guide
|
||
|
||
<p class="page-description">Strategies, error classification, and recovery patterns for handling network stalls, upstream rate limits, and streaming failures.</p>
|
||
|
||
## Overview
|
||
|
||
Streaming from distributed upstream web providers is inherently dynamic. Upstream CDN nodes rotate domains, regional ISPs implement traffic shaping, and token signatures expire.
|
||
|
||
Building a resilient client requires planning for failure at every step. This guide explains how Vyla handles errors internally and how client players should respond.
|
||
|
||
---
|
||
|
||
## Error Classification Matrix
|
||
|
||
| Error Type | Common Cause | Stream API / SDK Behavior | Recommended Player Action |
|
||
| :--- | :--- | :--- | :--- |
|
||
| **HTTP 400 Bad Request** | Missing or malformed TMDB ID, invalid season/episode | Returns immediate JSON error payload without querying providers. | Validate input parameters client-side before sending requests. |
|
||
| **HTTP 429 Too Many Requests** | Upstream host rate-limiting client IP | SDK provider applies backoff delay and retries up to `retries` limit. | If proxy returns 429, respect the `retry_after` header and back off. |
|
||
| **HTTP 502 Bad Gateway** | Upstream provider host is unreachable or timed out | Provider is discarded; other scrapers continue executing. | Fall back to the next available stream candidate in your queue. |
|
||
| **SSE Disconnect** | Network hiccup or client tab backgrounded | Connection drops; `EventSource.onerror` fires. | If video is already playing, do not interrupt. Otherwise, reconnect after 2 seconds. |
|
||
| **HLS Fatal Media Error** | Segment buffer stall, corrupt chunk, or missing decryption key | Player triggers `Hls.Events.ERROR` with `fatal: true`. | Call `hls.destroy()` and load the next candidate from your fallback queue. |
|
||
|
||
---
|
||
|
||
## Client Fallback Queue Strategy
|
||
|
||
Always treat streaming candidate resolution as a prioritized queue rather than a single static link:
|
||
|
||
### Queue Priority Assignment
|
||
|
||
| Candidate Stream | Player State | Fallback Precedence |
|
||
| :--- | :--- | :--- |
|
||
| **Candidate 1** (`vidlink`) | **Active Playback** | Mounted into HLS.js or ExoPlayer on arrival. |
|
||
| **Candidate 2** (`vixsrc`) | **Standby Backup** | Enqueued as Priority #1 fallback stream. |
|
||
| **Candidate 3** (`cinecat`) | **Standby Backup** | Enqueued as Priority #2 fallback stream. |
|
||
|
||
### Recovery Sequence on Stream Stall
|
||
1. **Error Interception**: Player detects a fatal network or decode event (`data.fatal === true`).
|
||
2. **Timestamp Preservation**: Record current playback offset (e.g. `currentTime = 142.5s`).
|
||
3. **Buffer Teardown**: Call `hls.destroy()` or `player.release()` to release media pipelines and decoders.
|
||
4. **Queue Advancement**: Dequeue the next available verified candidate from the standby queue.
|
||
5. **Seamless Seek**: Mount the fallback source and seek immediately to the preserved timestamp upon `loadedmetadata`.
|
||
|
||
### Implementing Fallback Recovery in JavaScript
|
||
|
||
```javascript
|
||
function attachHlsPlayer(videoElement, candidate, fallbackQueue) {
|
||
const hls = new Hls({
|
||
maxBufferLength: 30,
|
||
maxMaxBufferLength: 60,
|
||
enableWorker: true
|
||
});
|
||
|
||
hls.loadSource(candidate.url);
|
||
hls.attachMedia(videoElement);
|
||
|
||
hls.on(Hls.Events.ERROR, (event, data) => {
|
||
if (!data.fatal) return;
|
||
|
||
console.warn(`Encountered fatal error on ${candidate.source}:`, data.type);
|
||
|
||
switch (data.type) {
|
||
case Hls.ErrorTypes.NETWORK_ERROR:
|
||
console.log("Attempting network recovery...");
|
||
hls.startLoad();
|
||
break;
|
||
case Hls.ErrorTypes.MEDIA_ERROR:
|
||
console.log("Attempting media recovery...");
|
||
hls.recoverMediaError();
|
||
break;
|
||
default:
|
||
hls.destroy();
|
||
if (fallbackQueue.length > 0) {
|
||
const nextCandidate = fallbackQueue.shift();
|
||
const lastTime = videoElement.currentTime;
|
||
console.warn(`Switching to fallback candidate: ${nextCandidate.label}`);
|
||
|
||
attachHlsPlayer(videoElement, nextCandidate, fallbackQueue);
|
||
videoElement.addEventListener('loadedmetadata', () => {
|
||
videoElement.currentTime = lastTime;
|
||
videoElement.play();
|
||
}, { once: true });
|
||
} else {
|
||
showErrorOverlay("All available streaming providers have failed.");
|
||
}
|
||
break;
|
||
}
|
||
});
|
||
|
||
return hls;
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## Client-Side Timeout Budgets
|
||
|
||
Never leave users hanging on an indefinite spinner. Implement a two-tiered timeout budget:
|
||
|
||
1. **Initial Candidate Timeout (8–10 seconds)**: If no `type: "source"` event has arrived within 8 seconds, update the UI to indicate extended searching.
|
||
2. **Hard Timeout (20 seconds)**: If the stream has not completed or emitted a viable candidate within 20 seconds:
|
||
- Call `eventSource.close()`.
|
||
- Render a helpful empty-state UI with a "Retry" button or an option to change the server URL.
|
||
|
||
---
|
||
|
||
## Decoupled Subtitle Fallback
|
||
|
||
Subtitles originate from independent subtitle repositories (e.g. OpenSubtitles, SubDL). If a subtitle track fails to load:
|
||
- The video playback must continue uninterrupted.
|
||
- Log a warning in the console.
|
||
- Offer the viewer an in-player menu to toggle alternative subtitle providers or upload a local `.srt`/`.vtt` file.
|