fixed syntax issues

This commit is contained in:
2026-11-14 19:05:02 -08:00
parent 2cec85a8f3
commit 36b2b33ecc
29 changed files with 842 additions and 977 deletions

View File

@@ -1,28 +1,28 @@
# Vyla Ecosystem Architecture
> High-level architecture, module boundaries, communication protocols, and design principles of the Vyla Ecosystem.
High-level architecture, module boundaries, communication protocols, and design principles of the Vyla Ecosystem.
## System Topology
| Ecosystem Layer | Core Technologies | Primary Responsibilities |
| :--- | :--- | :--- |
| **Client Interfaces** | Vyla Home (Tauri v2), Vyla Go (Mobile), Vyla TV (ExoPlayer), Web Players | Render UI, manage local user state, initiate playback via SSE, handle failover queues. |
| **Stream API Gateway** | Node.js Cluster, SSE, HTTP Proxy | Multi-core IPC caching (1,500 entries, 5-min TTL), HLS/AES key rewriting, 120-byte fake image mask stripping. |
| **Vyla SDK Resolver** | `@vyla-entertainment/sdk` (ESM) | 60+ provider scrapers, randomized jitter scheduling, two-phase stream validation, manga scraping with Chrome 131 TLS fingerprinting. |
| **Upstream Hosts** | Third-party CDNs & Video Hosts | Remote video segment delivery, HLS manifest endpoints, subtitle hosts. |
- **Client Interfaces**: Vyla Home (Tauri v2), Vyla Go (Mobile), Vyla TV (ExoPlayer), Web Players. Render UI, manage local state, initiate playback via SSE, and handle failover queues.
- **Stream API Gateway**: Node.js Cluster, SSE, HTTP Proxy. Multi-core IPC caching (1,500 entries, 5-min TTL), HLS/AES key rewriting, and 120-byte fake image mask stripping.
- **Vyla SDK Resolver**: `@vyla-entertainment/sdk` (ESM). 60+ provider scrapers, randomized jitter scheduling, two-phase stream validation, and manga scraping with Chrome 131 TLS fingerprinting.
- **Upstream Hosts**: Remote video segment delivery, HLS manifest endpoints, and subtitle hosts.
---
## Projects Overview
| Directory / Package | Language / Framework | Purpose |
| :--- | :--- | :--- |
| `Documentation` | HTML5, Vanilla JS, CSS3 | Official developer documentation platform hosted at [vyla.cc](https://vyla.cc). |
| `Stream API` | Node.js (Cluster, HTTP, SSE) | Self-hosted streaming microservice gateway with caching and reverse proxying. |
| `SDK` | Node.js (ESM), TLS Client | Core multi-provider scraping library (`@vyla-entertainment/sdk`). |
| `Home` | Tauri v2, Rust, Node.js Sidecar | Native desktop application for Windows, macOS, and Linux. |
| `TV` | Kotlin, Jetpack Compose, ExoPlayer | Native 10-foot Leanback streaming application for Android TV and Fire TV. |
| `Go` | Tauri v2 Mobile, WebGL Shaders | Native mobile client for Android devices. |
- **Documentation**: Developer documentation platform hosted at [vyla.cc](https://vyla.cc).
- **Stream API**: Self-hosted streaming microservice gateway with caching and reverse proxying.
- **SDK**: Core multi-provider scraping library (`@vyla-entertainment/sdk`).
- **Home**: Native desktop application for Windows, macOS, and Linux (Tauri v2 + Rust + Node.js sidecar).
- **TV**: Native 10-foot Leanback streaming application for Android TV and Fire TV (Kotlin + Jetpack Compose + ExoPlayer).
- **Go**: Native mobile client for Android devices (Tauri v2 Mobile).
## Core Architectural Invariants
---
## Core Invariants
1. **Decoupled Resolution**: Client interfaces never scrape providers directly; they interact through `@vyla-entertainment/sdk` or the Stream API gateway.
2. **Progressive Playback**: Streaming links are emitted incrementally over Server-Sent Events (SSE). Players start playback on the first candidate (3-8s) and queue subsequent sources as fallbacks.

View File

@@ -4,32 +4,30 @@
## Overview
At the heart of the Vyla ecosystem is the **Provider Layer**. Rather than relying on a single upstream video host, Vyla SDK aggregates over 60 independent, community-maintained scraping resolvers.
Each provider operates with distinct upstream mechanics, domain rotations, rate-limiting policies, and security defenses. This document explains how Vyla orchestrates these disparate sources into a unified, high-reliability streaming pipeline.
Rather than relying on a single upstream host, the Vyla SDK aggregates over 60 independent provider scrapers. Each scraper handles its own domain logic, extraction mechanics, and header requirements.
---
## Provider Configuration Interface
## Provider Configuration
In `@vyla-entertainment/sdk`, each provider is declared in `src/config.js` via the `SourceConfig` interface:
Scrapers are configured in `src/config.js`:
```typescript
export interface SourceConfig {
key: string; // Unique source identifier (e.g. "vidlink", "vixsrc")
label: string; // Human-readable display label
sourceFile: string; // Scraper module filename in src/sources/
proxyParam: string; // Query parameter used for HLS proxy routing
timeout: number; // Total milliseconds allowed before source abort
sourcesTimeout?: number; // Secondary timeout for multi-server extraction
jitter: number; // Maximum randomized startup delay in ms
retries: number; // Number of automatic retry attempts on error
multiUrl: boolean; // Returns multiple stream candidate URLs if true
skipProxy?: boolean; // If true, returns direct upstream URLs without proxy
skipVerify?: boolean; // If true, bypasses verification checks
verifyHeaders?: boolean; // Injects custom headers into verification probes
disabled?: boolean; // If true, provider is completely excluded
cdnHeaders?: { // Custom headers injected for specific CDN patterns
key: string;
label: string;
sourceFile: string;
proxyParam: string;
timeout: number;
sourcesTimeout?: number;
jitter: number;
retries: number;
multiUrl: boolean;
skipProxy?: boolean;
skipVerify?: boolean;
verifyHeaders?: boolean;
disabled?: boolean;
cdnHeaders?: {
pattern: RegExp;
headers: { Referer: string; Origin: string; };
}[];
@@ -38,80 +36,56 @@ export interface SourceConfig {
---
## The Query Pipeline
## Query Pipeline
When a client requests a title (`getStream()` in SDK or `/movie` / `/tv` in Stream API), the engine executes the following pipeline:
| Pipeline Stage | Operation | Purpose & Verification |
| :--- | :--- | :--- |
| **1. Source Filtering** | `SOURCES.filter(!disabled)` | Excludes disabled scrapers and selects active providers based on configuration. |
| **2. Jitter Staggering** | Sleep between `0` and `cfg.jitter` ms | Disperses outbound connection spikes across time to prevent DDoS protection triggers. |
| **3. Concurrent Scraping** | Parallel async scraper execution | Scrapers parse HTML/JSON endpoints using customized `User-Agent`, `Referer`, and `Origin` headers. |
| **4. Two-Phase Verification** | `verifyStream()` then `verifyPlayable()` | Phase 1 validates HTTP status ($< 400$ via HEAD). Phase 2 parses HLS manifests and verifies segment availability. Dead streams are discarded. |
| **5. Proxy URL Rewriting** | Encodes target URL into `/api?url=...` | Adds proxy flags, sets required playback headers, and prepares segment de-obfuscation. |
| **6. Progressive Emission** | Pushes SSE `source` event | Delivers playable stream candidates to the client player immediately without waiting for slower providers. |
1. **Source Filtering**: Excludes disabled scrapers and loads active configurations.
2. **Jitter Delay**: Sleeps for a randomized duration between `0` and `cfg.jitter` ms to avoid burst requests.
3. **Concurrent Scraping**: Runs scrapers in parallel with provider-specific `User-Agent`, `Referer`, and `Origin` headers.
4. **Two-Phase Verification**: Validates candidate URLs via `verifyStream()` (HTTP status < 400) and `verifyPlayable()` (HLS playlist and segment checks).
5. **Proxy URL Rewriting**: Encodes verified streams into `/api?url=...` with required proxy flags.
6. **Progressive Emission**: Pushes candidate streams over Server-Sent Events (SSE) immediately.
---
## Jitter Staggering & Rate-Limit Defense
## Jitter Staggering
If a client triggers 60 scrapers instantaneously, your client IP address could send hundreds of simultaneous HTTP requests in the same millisecond, triggering upstream Cloudflare or DDoS rate limits.
To prevent triggering upstream rate limits when querying dozens of scrapers simultaneously, each scraper applies random jitter:
To mitigate this, Vyla introduces **per-source jitter**:
```javascript
const jitterDelay = Math.floor(Math.random() * (cfg.jitter || 400));
await new Promise(resolve => setTimeout(resolve, jitterDelay));
```
By staggering outbound requests across a 100ms–800ms window, upstream hosts perceive natural traffic patterns, dramatically reducing HTTP 429 (Too Many Requests) errors.
This spreads outbound requests across a 100ms–800ms window, reducing HTTP 429 errors.
---
## Two-Phase Playback Verification
## Two-Phase Verification
Many streaming extractors successfully return a URL, only for that URL to return an HTTP 403 Forbidden, an empty manifest, or a dead video chunk. To protect players from stalling, Vyla validates every candidate through two independent verification stages before emitting it:
1. **Phase 1 (`verifyStream`)**: Sends a lightweight HTTP `HEAD` request with source-specific headers. Discards any response >= 400.
2. **Phase 2 (`verifyPlayable`)**: Fetches the initial playlist bytes, verifies the `#EXTM3U` header, confirms playable segment paths, and tests the first chunk or key URI.
### Phase 1: `verifyStream()`
- Dispatches a lightweight HTTP `HEAD` request to the upstream target URL.
- Enforces custom `User-Agent`, `Referer`, and `Origin` headers.
- Requires HTTP status code `< 400` within 4,000 milliseconds.
### Phase 2: `verifyPlayable()`
- Fetches the initial playlist bytes through the local proxy.
- Validates the presence of the `#EXTM3U` header tag.
- Checks for stream definitions:
- If a Master Playlist: ensures valid `#EXT-X-STREAM-INF` entries exist.
- If a Media Playlist: ensures valid `#EXTINF` duration tags and playable segment URIs exist.
- Attempts a test read on the first media segment or key URI.
- Only candidates that pass both phases are deemed **verified**.
Only streams that pass both checks are returned to the client.
---
## The Universal HLS Proxy & Byte-Mask Stripper
## Proxy & Header Stripping
Modern streaming hosts often protect their video segments by adding CORS restrictions, token signatures, or binary header masking. Stream API handles this transparently:
- **M3U8 Rewriting**: Rewrites AES-128 encryption key URIs and `.ts` segment paths to route through the proxy with CORS headers (`Access-Control-Allow-Origin: *`).
- **120-Byte Mask Stripping**: Slices off fake 120-byte image headers prepended by certain CDNs:
### 1. M3U8 Playlist Rewriting
When an HLS stream is proxied, Stream API fetches the upstream playlist and rewrites every internal URI:
- **AES-128 Key URIs**: `#EXT-X-KEY:METHOD=AES-128,URI="/api?url=<key>&<param>=1"`
- **Video Chunks**: Segment URIs are rewritten to route back through the `/api` proxy endpoint.
- **CORS Headers**: Returned with `Access-Control-Allow-Origin: *` and `Access-Control-Allow-Headers: *`.
### 2. Segment Byte-Mask Stripping
Certain major CDNs mask MPEG-TS chunks by prepending a 120-byte fake image header (`image/png`, `image/jpeg`, TikTok CDN, or `letsgocdn` signatures) so firewalls treat them as images.
When Stream API proxies these segments, it inspects the leading bytes and automatically strips the 120-byte image wrapper:
```javascript
const needsStrip = searchParams.has('tt') || STRIP_REGEX.test(cleanUrl);
if (needsStrip && (chunk[0] === 0x89 || chunk[0] === 0xFF || chunk[0] === 0x00)) {
return chunk.subarray(120); // Strips fake header, leaving clean MPEG-TS
return chunk.subarray(120);
}
```
---
## Inter-Process Caching
## In-Memory Caching
Verified stream results are cached in-memory with a **5-minute Time-To-Live (TTL)**:
- **Cache Key**: `source_key + tmdb_id + season + episode`
- In multi-core cluster deployments, cache entries are coordinated across workers via inter-process communication (IPC) messages (`cache:get`, `cache:hit`, `cache:set`).
- Automatic prune routines run every 30 seconds to evict expired items and enforce maximum cache limits (default: 1,500 entries).
Verified stream candidates are cached in-memory for 5 minutes (300,000 ms):
- Cache keys format: `source_key + tmdb_id + season + episode`.
- In cluster mode, workers coordinate entries via IPC messages (`cache:get`, `cache:hit`, `cache:set`).
- Expired entries are pruned every 30 seconds, maintaining a 1,500-entry ceiling.

View File

@@ -4,44 +4,32 @@
## 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.
Upstream video providers can rotate domains, experience rate limits, or suffer transient outages. This guide explains how Vyla handles errors internally and how client players should respond.
---
## Error Classification Matrix
## Error Classification
| 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. |
- **HTTP 400 Bad Request**: Missing or invalid TMDB ID or episode parameters. Validate input parameters on the client before requesting streams.
- **HTTP 429 Too Many Requests**: Upstream host rate limit triggered. The SDK applies backoff delays; clients should respect the `retry_after` header.
- **HTTP 502 Bad Gateway**: Upstream host unreachable or timed out. That provider is discarded while other scrapers continue running.
- **SSE Disconnect**: Network drop or backgrounded tab. If media is already buffering, do not interrupt. Otherwise, reconnect after a brief delay.
- **HLS Fatal Media Error**: Buffer stall or missing encryption key (`fatal: true`). Destroy the player instance and mount the next queued fallback stream.
---
## Client Fallback Queue Strategy
## Client Fallback Strategy
Always treat streaming candidate resolution as a prioritized queue rather than a single static link:
Treat stream candidates as a prioritized queue rather than a single static link:
### Queue Priority Assignment
1. **Mount First Candidate**: Mount the first verified source that arrives over SSE.
2. **Queue Subsequent Sources**: Push subsequent incoming `source` events into a fallback array.
3. **Capture Timestamp on Error**: If a fatal error occurs, record `video.currentTime`.
4. **Teardown & Reconnect**: Call `hls.destroy()`, dequeue the next candidate, mount it, and seek back to the saved timestamp once metadata loads.
| 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
## Implementation Example
```javascript
function attachHlsPlayer(videoElement, candidate, fallbackQueue) {
@@ -57,15 +45,11 @@ function attachHlsPlayer(videoElement, candidate, fallbackQueue) {
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:
@@ -73,7 +57,6 @@ function attachHlsPlayer(videoElement, candidate, fallbackQueue) {
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', () => {
@@ -81,7 +64,7 @@ function attachHlsPlayer(videoElement, candidate, fallbackQueue) {
videoElement.play();
}, { once: true });
} else {
showErrorOverlay("All available streaming providers have failed.");
showErrorOverlay("All available streaming providers failed.");
}
break;
}
@@ -93,20 +76,7 @@ function attachHlsPlayer(videoElement, candidate, fallbackQueue) {
---
## Client-Side Timeout Budgets
## 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.
- **First Candidate Timeout (8–10s)**: If no source event arrives within 8 seconds, inform the user that extended search is underway.
- **Hard Abort Timeout (20–25s)**: If no viable source is verified before the watchdog timer expires, close the connection and display an empty state with a retry button.

View File

@@ -127,20 +127,7 @@
<link rel="preload" as="image" href="/src/assets/images/wallpaper-media/tree-mobile.webp" type="image/webp"
media="(max-width: 768px)" fetchpriority="high" />
<link rel="ard" href="/.well-known/ard.json" />
<script defer src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-core.min.js"></script>
<script defer
src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/plugins/autoloader/prism-autoloader.min.js"></script>
<script>
(function () {
var saved = localStorage.getItem('vyla-theme') || 'auto';
if (saved === 'auto') {
var d = new Date();
var m = d.getMonth();
saved = m === 9 ? 'halloween' : m === 10 ? 'autumn' : m === 11 ? 'christmas' : (m === 0 || m === 1) ? 'winter' : (m === 8 && d.getDate() >= 22) ? 'autumn' : 'dark';
}
document.documentElement.setAttribute('data-theme', saved);
})();
</script>
<script>if(localStorage.getItem('vyla-banner-dismissed')==='true'){document.documentElement.classList.add('banner-dismissed');}</script>
</head>
<body class="is-home">
@@ -823,6 +810,7 @@
</div>
</div>
</div>
<script defer src="/src/js/prism.js"></script>
<script defer src="/src/js/main.js"></script>
<script defer src="/src/js/api/contribute.js"></script>
<script defer src="/src/js/api/donations.js"></script>

View File

@@ -2,105 +2,83 @@
<p class="page-description">The native Android mobile client for the Vyla ecosystem, combining hardware-accelerated shaders, automated runtime management, and mobile streaming.</p>
<img class="vyla-banner" src="/src/assets/images/android/site_preview.webp" alt="Vyla Go mobile streaming application interface" loading="lazy" decoding="async" fetchpriority="low" />
<img class="vyla-banner" src="/src/assets/images/android/site_preview.webp" alt="Vyla Go mobile application" loading="lazy" decoding="async" fetchpriority="low" />
## Overview
**Vyla Go** brings the power of the Vyla media ecosystem directly to Android smartphones and tablets. Built using **Tauri v2 Mobile**, Vyla Go delivers a fluid, responsive touch experience backed by native hardware acceleration and custom WebGL shaders.
Designed for mobile reliability on varying cellular and Wi-Fi networks, Vyla Go automatically coordinates local resource extraction, service fallbacks, and remote update fetching without requiring constant manual APK re-installations.
**Vyla Go** brings the Vyla media ecosystem to Android phones and tablets. Built with **Tauri v2 Mobile**, Vyla Go delivers a responsive touch interface with WebGL hardware shaders and background playback support.
---
## Downloads
Official APK packages are published on the [Vyla Go GitLab Releases](https://gitlab.com/vyla-entertainment/go/-/releases):
Official APKs are available on [GitLab Releases](https://gitlab.com/vyla-entertainment/go/-/releases):
| Package | Target Architectures | Compatibility | Download Link |
| :--- | :--- | :--- | :--- |
| **Universal APK** | `arm64-v8a`, `armeabi-v7a`, `x86_64` | Android 7.0+ (API 24+) | [Download Universal APK](https://gitlab.com/vyla-entertainment/go/-/releases) |
| **ARM64 APK** | `aarch64` | Modern 64-bit Android Devices | [Download ARM64 APK](https://gitlab.com/vyla-entertainment/go/-/releases) |
- **[Universal APK](https://gitlab.com/vyla-entertainment/go/-/releases)**: Supports `arm64-v8a`, `armeabi-v7a`, and `x86_64` on Android 7.0+ (API 24+).
- **[ARM64 APK](https://gitlab.com/vyla-entertainment/go/-/releases)**: Optimized for modern 64-bit Android devices.
---
## Key Features
- **Tauri v2 Mobile Architecture**: Native Rust-backed Android application layer with secure Chromium WebView rendering.
- **Hardware-Accelerated WebGL Shaders**: Real-time dynamic glassmorphism, responsive ambient backdrops, and fluid gesture navigation.
- **Automated Resource Management**: Downloads and unpacks core service updates directly from official release channels, keeping scraper definitions up to date.
- **Low-Latency Mobile Playback**: Supports HLS and direct stream resolution with adaptive mobile bitrate handling.
- **Tauri v2 Mobile**: Native Rust Android layer with secure Chromium WebView rendering.
- **WebGL Hardware Shaders**: Dynamic backdrops and smooth gesture animations.
- **Automated Resource Updates**: Fetches and applies runtime payload updates directly from release channels without requiring full APK reinstalls for minor script updates.
- **Low-Latency Streaming**: Adaptive mobile bitrate switching with background audio playback.
---
## Building from Source
### Prerequisites
Before compiling Vyla Go, ensure your development environment has:
- **Node.js**: v20 or newer
- **Rust & Cargo**: Latest stable toolchain
- **Android Studio**: Android SDK & NDK (v29+)
- **Cross-Compilation Targets**:
- Node.js 20+
- Rust & Cargo
- Android Studio with Android SDK & NDK (API 29+)
Add cross-compilation targets:
```bash
rustup target add aarch64-linux-android x86_64-linux-android
```
### Build Commands
```bash
# 1. Install project dependencies
npm install
# 2. Package the runtime update payload
npm run build:payload
# 3. Compile the release APKs
npm run build:apk
```
The compiled release packages are output to:
Output APK location:
```
src-tauri/gen/android/app/build/outputs/apk/universal/release/app-universal-release.apk
```
---
## Developer & Debugging Guide (ADB)
## Debugging with ADB
The Vyla Go repository provides complete ADB integration for debugging, log collection, and direct device installation:
### Streaming Device Logs
Monitor the application logs in real time via ADB:
### Stream Logs
```powershell
# Stream live application logs tagged VylaGo
& "$env:LOCALAPPDATA\Android\Sdk\platform-tools\adb.exe" logcat -s VylaGo
```
# Clear the log buffer and stream fresh output
Clear and re-stream:
```powershell
& "$env:LOCALAPPDATA\Android\Sdk\platform-tools\adb.exe" logcat -c
& "$env:LOCALAPPDATA\Android\Sdk\platform-tools\adb.exe" logcat -s VylaGo
```
### Crash Diagnostics
Inspect unhandled exceptions, native SIGSEGV errors, and runtime failures:
```powershell
& "$env:LOCALAPPDATA\Android\Sdk\platform-tools\adb.exe" logcat -d | Select-String "VylaGo|AndroidRuntime|FATAL"
```
### Direct Device Installation
Install your newly built APK directly to a connected USB or Wi-Fi debug device:
### Install to Connected Device
```powershell
& "$env:LOCALAPPDATA\Android\Sdk\platform-tools\adb.exe" install -r "src-tauri\gen\android\app\build\outputs\apk\universal\release\app-universal-release.apk"
```
### Resetting App State
To completely terminate and purge cached app state:
### Reset App State
```powershell
& "$env:LOCALAPPDATA\Android\Sdk\platform-tools\adb.exe" shell am force-stop com.vyla.go
& "$env:LOCALAPPDATA\Android\Sdk\platform-tools\adb.exe" shell pm clear com.vyla.go
```
### Fixing Emulator DNS Timeouts
When running Android emulators that fail to reach remote release endpoints:
```powershell
& "$env:LOCALAPPDATA\Android\Sdk\platform-tools\adb.exe" shell settings put global private_dns_mode off
```

View File

@@ -2,75 +2,51 @@
<p class="page-description">The native desktop control center and player runtime for Windows, macOS, and Linux built on Tauri v2 and an embedded Node.js sidecar.</p>
<img class="vyla-banner" src="/src/assets/images/desktop/site_preview.webp" alt="Vyla Home desktop application preview displaying streaming interface" loading="lazy" decoding="async" fetchpriority="low" />
<img class="vyla-banner" src="/src/assets/images/desktop/site_preview.webp" alt="Vyla Home desktop application preview" loading="lazy" decoding="async" fetchpriority="low" />
## Overview
**Vyla Home** is the flagship desktop application of the Vyla ecosystem. Engineered for performance and privacy, Vyla Home packages the entire Vyla streaming runtime into a lightweight native shell using **Tauri v2** and an embedded Node.js sidecar process.
Instead of running heavy web browsers with excessive RAM overhead, Vyla Home leverages system-native WebViews (WebView2 on Windows, WebKit on macOS/Linux), ensuring instant boot times, hardware-accelerated video decoding, and tiny background resource footprints.
**Vyla Home** is the desktop client for the Vyla ecosystem. Built with **Tauri v2** and an embedded Node.js sidecar process, Vyla Home uses native system WebViews (WebView2 on Windows, WebKit on macOS/Linux) for fast startups and hardware-accelerated video decoding with minimal memory usage.
---
## Downloads
Official binary releases are published directly to the [Vyla Home GitLab Releases](https://gitlab.com/vyla-entertainment/home/-/releases):
Binary packages are available on [GitLab Releases](https://gitlab.com/vyla-entertainment/home/-/releases):
| Platform | Package Format | Architecture | Download Link |
| :--- | :--- | :--- | :--- |
| **Windows** | Setup Installer (`.exe`) / Portable | x86_64 (64-bit) | [Download for Windows (.exe)](https://gitlab.com/vyla-entertainment/home/-/releases) |
| **macOS** | Disk Image (`.dmg`) | Universal (Apple Silicon & Intel) | [Download for macOS (.dmg)](https://gitlab.com/vyla-entertainment/home/-/releases) |
| **Linux** | Source Build / Flatpak (in progress) | x86_64, aarch64 | [Build from Source](#linux-build-guide) |
:::callout-info
When launching Vyla Home for the first time, the application will automatically perform a self-check, initialize local caches, and establish secure RPC connections.
:::
- **Windows**: [Download Setup Installer / Portable (.exe)](https://gitlab.com/vyla-entertainment/home/-/releases) (x86_64)
- **macOS**: [Download Disk Image (.dmg)](https://gitlab.com/vyla-entertainment/home/-/releases) (Universal: Apple Silicon & Intel)
- **Linux**: Build from source using the steps below.
---
## Architectural Implementation
## Architecture
Vyla Home operates via a dual-process architecture:
- **Tauri Main Process (Rust)**: Manages window state, system tray icons, native menus, and hosts the WebView.
- **Stdio IPC**: Bi-directional JSON-RPC stream passing messages between Rust and the Node.js sidecar over `stdin` and `stdout`.
- **Node.js Sidecar**: Runs `ServiceManager` for local/remote failover, verifies update signatures via `UpdateClient`, manages Discord Rich Presence, and caches local media metadata.
| Process Layer | Tech Stack | Responsibilities & Capabilities |
| :--- | :--- | :--- |
| **Tauri Main Process** | Rust | Manages native OS window lifecycle, system tray icons, native menus, and hosts the secure WebView renderer. |
| **Inter-Process Channel** | Stdio IPC | Bi-directional JSON-RPC stream communicating over standard input and standard output (`stdin` / `stdout`). |
| **Node.js Sidecar Process** | Node.js (`main.js`) | Orchestrates `ServiceManager` for local/remote fallback, verifies update cryptographic signatures via `UpdateClient`, manages Discord Rich Presence, and handles local cached data. |
### Storage Locations
- **Windows**: `%APPDATA%\vyla-home\vyla-home-files\`
- **macOS**: `~/Library/Application Support/vyla-home/vyla-home-files/`
- **Linux**: `~/.local/share/vyla-home/vyla-home-files/`
### File Locations & Directory Structure
Vyla Home stores its configuration, cryptographic keys, cached catalogs, and logs in standard operating system directories:
| OS | Default Storage Path |
| :--- | :--- |
| **Windows** | `%APPDATA%\vyla-home\vyla-home-files\` |
| **macOS** | `~/Library/Application Support/vyla-home/vyla-home-files/` |
| **Linux** | `~/.local/share/vyla-home/vyla-home-files/` |
### Diagnostics & Logs
If you encounter runtime issues, inspect the sidecar debug log:
### Sidecar Logs
- **Windows**: `%APPDATA%\vyla-home\vyla-home-files\logs\node-debug.log`
- **macOS/Linux**: `<AppDataDir>/logs/node-debug.log`
---
## Windows Troubleshooting: WebView2 Runtime
## Windows Troubleshooting
On Windows systems, Vyla Home utilizes Microsoft's Evergreen WebView2 runtime. If your installation encounters a `WebView2 runtime initialization error`:
1. Ensure the Microsoft Edge WebView2 Evergreen Runtime is installed from [Microsoft's official site](https://developer.microsoft.com/en-us/microsoft-edge/webview2/).
2. For portable or standalone deployments, verify that `WebView2Loader.dll` exists in the `bin/` directory adjacent to the binary:
```bash
vyla-home\bin\WebView2Loader.dll
```
If you encounter a `WebView2 runtime initialization error`:
1. Install the Microsoft Edge WebView2 Evergreen Runtime from Microsoft.
2. For portable installs, confirm `WebView2Loader.dll` is present in the `bin/` directory adjacent to the executable.
---
## Linux Build Guide
To build and run Vyla Home natively on Linux distributions (such as Fedora, Ubuntu, Debian, or Arch):
### 1. Install System Dependencies
:::code-group
@@ -106,20 +82,11 @@ source "$HOME/.cargo/env"
### 3. Clone and Build
```bash
# Clone the repository
git clone https://gitlab.com/vyla-entertainment/home.git vyla-home
cd vyla-home
# Install Node dependencies
npm install
# Package runtime payload
npm run build:payload
# Launch in developer mode
npm run dev
# Or compile production binary
npm run build
```
@@ -127,30 +94,14 @@ npm run build
## Developer Workflows
The Vyla Home repository includes specialized scripts for continuous development and release management:
```bash
# Start the full application in development mode with hot reload
npm run dev
```
```bash
# Package the local application payload into update-payload.zip
npm run build:payload
```
```bash
# Test the UI shell with a simulated test harness
npm run dev -- -- --env VYLA_TEST_UI=1
```
```bash
# Compile and package a full production installer release
npm run release:full
```
### Resolving File Locks (OS Error 32)
If a hung process locks the binary during development on Windows:
If a hung process locks the binary during Windows development:
```powershell
Stop-Process -Name "vyla-home", "node", "cargo" -Force -ErrorAction SilentlyContinue
```

View File

@@ -1,71 +1,38 @@
# Building a Custom Player
<p class="page-description">Architectural principles, format handling, and engineering guidelines for building resilient video players backed by Vyla infrastructure.</p>
<p class="page-description">Architectural principles, format handling, and engineering guidelines for building video players backed by Vyla infrastructure.</p>
## Overview
In the Vyla ecosystem, you are never locked into an official frontend. Vyla provides the discovery, resolution, and proxy layers; your application owns the player experience. Whether you are building a custom web portal, desktop client, or mobile app, this guide covers the core engineering patterns required for a robust player.
Vyla handles discovery, scraping, and proxying; your app owns the player. Whether building a web player, desktop shell, or mobile UI, this guide outlines the core playback patterns.
---
## Integration Boundaries
## Integration Paths
Choose your integration point based on your architecture:
| Architecture | Integration Point | Data Flow | Best For |
| :--- | :--- | :--- | :--- |
| **Node.js Backend** | **Vyla SDK Directly** | `sdk.getStream()` in-process | Applications with full backend control, custom authentication, and server-side session tracking. |
| **Browser / Mobile** | **Stream API Gateway** | SSE `/movie` or `/tv` over HTTP | Web applications, smart TV frontends, or lightweight mobile clients requiring an HTTP/SSE source feed. |
- **Node.js Backends**: Use the Vyla SDK directly in-process via `sdk.getStream()`. Ideal for custom authentication and server-side tracking.
- **Web / Mobile / TV**: Connect to the Stream API gateway via HTTP/SSE (`/movie` or `/tv`). Ideal for frontend players requiring a live streaming feed.
---
## Playback Formats: HLS vs Direct Video
## Playback Formats
Resolved candidate streams come in two primary container types:
1. **HLS Streams (`type: "hls"`)**:
- Master M3U8 playlists containing multiple bitrate/resolution variants.
- Proxied through Stream API (`/api?url=...`), which automatically rewrites chunk URLs and AES-128 encryption key URIs.
- Requires native HLS support (Safari on macOS/iOS) or a JavaScript HLS MSE library such as [HLS.js](https://github.com/video-dev/hls.js/) in modern browsers.
2. **Direct Media Files (`type: "mp4"`)**:
- Progressive MP4 or MKV streams.
- Proxied with HTTP Range header forwarding (`206 Partial Content`), allowing smooth scrubbing and seeking.
- Directly assignable to the HTML5 `<video>` element `src` attribute.
- **HLS Streams (`type: "hls"`)**: Adaptive master playlists (`.m3u8`). Proxied via Stream API (`/api?url=...`), which rewrites chunk URIs and AES encryption keys with permissive CORS headers. Handled via HLS.js or native Safari.
- **Direct Video (`type: "mp4"`)**: Direct MP4 or MKV files. Proxied with HTTP Range forwarding (`206 Partial Content`) for scrubbing.
---
## Resilient Player Architecture
## Player Guidelines
Upstream streaming hosts are dynamic: network latencies fluctuate, upstream proxies fail, and geo-restrictions vary. A production-ready Vyla player must adhere to five design rules:
### 1. Start on the First Candidate
Never wait for all providers to finish. Stream API emits verified sources over Server-Sent Events (SSE). The moment the first `source` event arrives (typically within 3 to 8 seconds):
- Initialize your player immediately.
- Store any subsequent incoming `source` events in a priority fallback array.
### 2. Maintain a Fallback Priority Array
If the active stream stalls, encounters an unrecoverable network error, or buffer-underruns:
- Catch the error event (e.g. `Hls.Events.ERROR` with fatal flag).
- Pop the next candidate from your fallback queue.
- Re-attach the media source without disrupting the user's viewing session.
### 3. Decouple Subtitles from Video Playback
Subtitles are retrieved independently from the video stream. Never block initial video buffering waiting for subtitle tracks to download. Load the video stream immediately and inject subtitle tracks (`<track kind="subtitles">`) asynchronously once resolved.
### 4. Implement a Client-Side Timeout
Always set a client-side watchdog timer (typically 15 to 20 seconds). If no viable candidate emits a valid video stream before the timer expires, render an informative empty state allowing the user to retry or switch servers.
### 5. Clean Up on Title Switch
When a viewer selects a new movie or episode:
- Call `eventSource.close()` on the active SSE connection.
- Invoke `hls.destroy()` or `video.removeAttribute('src')`.
- Clear pending retry timers and abort in-flight fetch requests.
1. **Start on First Source**: Begin playback as soon as the first `source` event arrives (3–8s). Queue later events as fallbacks.
2. **Auto-Failover**: If the current stream fails, catch fatal playback errors and immediately switch to the next queued source.
3. **Async Subtitles**: Fetch subtitles in the background without blocking video buffering.
4. **Watchdog Timer**: If no working source resolves within 15–20s, show an empty state with a retry option.
5. **Clean Teardown**: When switching media, close the SSE connection and destroy the HLS instance before opening a new stream.
---
## Reference Implementation: Minimal Web Player
Below is a complete, copyable implementation using native HTML5 and HLS.js:
## Minimal Web Player Example
```html
<!DOCTYPE html>
@@ -84,7 +51,7 @@ Below is a complete, copyable implementation using native HTML5 and HLS.js:
<div id="status" class="status">Connecting to Stream API...</div>
<script>
const API_BASE = "https://your-stream-api.example.com";
const API_BASE = "https://api.vyla.cc";
const TMDB_ID = 155;
const video = document.getElementById("vyla-video");
@@ -137,7 +104,6 @@ Below is a complete, copyable implementation using native HTML5 and HLS.js:
hlsInstance.on(Hls.Events.ERROR, (event, errorData) => {
if (errorData.fatal) {
console.warn("HLS fatal error encountered. Switching to fallback candidate...");
hlsInstance.destroy();
isPlaying = false;
if (fallbackSources.length > 0) playCandidate(fallbackSources.shift());
@@ -167,11 +133,3 @@ Below is a complete, copyable implementation using native HTML5 and HLS.js:
</body>
</html>
```
---
### Next Steps
- Explore the complete [Client Integration Guide](/guides/integrations) for React, Vue, and ExoPlayer examples.
- Review error classification in the [Error Handling Guide](/guides/error-handling).
- Review the SSE event protocol in [Paths & Playback Events](/stream-api/reference/paths).

View File

@@ -2,54 +2,41 @@
<p class="page-description">The native 10-foot Leanback streaming experience for Android TV and Google TV, built with Jetpack Compose and ExoPlayer.</p>
<img class="vyla-banner" src="/src/assets/images/android-tv/site-preview.webp" alt="Vyla TV application Leanback interface on television display" loading="lazy" decoding="async" fetchpriority="low" />
<img class="vyla-banner" src="/src/assets/images/android-tv/site-preview.webp" alt="Vyla TV application Leanback interface" loading="lazy" decoding="async" fetchpriority="low" />
## Overview
**Vyla TV** is the living-room client in the Vyla ecosystem. Built entirely as a native Android application using modern **Jetpack Compose for TV** and Google's high-performance **ExoPlayer (Media3)** engine, Vyla TV brings the decentralized media experience to large displays.
Every interface component in Vyla TV is designed specifically for remote control navigation, ensuring seamless D-pad focus management, rapid title browsing, and cinema-grade playback.
**Vyla TV** is the living-room client in the Vyla ecosystem. Built as a native Android app using **Jetpack Compose for TV** and Google's **ExoPlayer (Media3)** engine, Vyla TV brings streaming to televisions and smart boxes with directional remote navigation.
---
## Downloads
Official TV APK builds are published to the [Vyla TV GitLab Releases](https://gitlab.com/vyla-entertainment/TV/-/releases):
Official TV APK builds are published on [GitLab Releases](https://gitlab.com/vyla-entertainment/TV/-/releases):
| Platform | Format | Recommended Target Devices | Download Link |
| :--- | :--- | :--- | :--- |
| **Android TV / Google TV** | `.apk` | Sony Bravia, Chromecast with Google TV, NVIDIA Shield TV, Xiaomi Mi Box | [Download Vyla TV APK](https://gitlab.com/vyla-entertainment/TV/-/releases) |
| **Amazon Fire TV** | `.apk` | Fire TV Stick 4K / Max, Fire TV Cube | [Download Vyla TV APK](https://gitlab.com/vyla-entertainment/TV/-/releases) |
- **[Vyla TV APK for Android TV / Google TV](https://gitlab.com/vyla-entertainment/TV/-/releases)**: Compatible with Sony Bravia, Chromecast with Google TV, NVIDIA Shield TV, and Xiaomi Mi Box.
- **[Vyla TV APK for Fire TV](https://gitlab.com/vyla-entertainment/TV/-/releases)**: Compatible with Fire TV Stick 4K / Max and Fire TV Cube.
---
## TV Experience & Features
## Features
- **D-Pad Directional Navigation**: Engineered from the ground up for directional remotes. No mouse pointers, virtual cursors, or awkward touch adaptations.
- **ExoPlayer Media3 Engine**: Hardware-accelerated video decoding with direct support for adaptive HLS (m3u8), MP4, and MKV streams.
- **Playback Control Matrix**:
- Variable playback speeds (0.5x, 0.75x, 1.0x, 1.25x, 1.5x, 2.0x).
- Multi-track subtitle selector with custom sync adjustment and font sizing.
- Multi-stream source switcher to toggle between upstream providers without leaving the player.
- **Catalog & Library**:
- TMDB catalog integration with detailed cast, plot synopses, and genre filters.
- "Watch Later" watchlist and persistent "Favorites" library stored locally on device.
- Instant resume tracking with automatic timestamp memory.
- **D-Pad Navigation**: Optimized exclusively for remote controls with focus management.
- **ExoPlayer Media3**: Hardware-accelerated decoding for HLS (`.m3u8`), MP4, and MKV streams up to 4K/HDR.
- **Playback Controls**: Variable playback speeds (0.5x to 2.0x), multi-track subtitle selector with sync offsets, and in-player source switching.
- **Local Library**: TMDB catalog integration, Watch Later watchlist, and automatic resume playback timestamps.
---
## Sideloading on Android TV / Fire TV
If your device does not have a direct browser installer:
## Sideloading via Downloader
1. Install **Downloader by AFTVnews** from the Google Play Store or Amazon Appstore.
2. In your TV's settings, navigate to **Settings > System > Developer Options** (or **Device & Software > Developer Options**) and enable **Install Unknown Apps** for Downloader.
3. Open Downloader and enter the direct APK URL from our releases:
```bash
2. In TV settings, navigate to Developer Options and enable **Install Unknown Apps** for Downloader.
3. Open Downloader and enter the release link:
```
https://gitlab.com/vyla-entertainment/TV/-/releases
```
4. Download the latest `.apk` and select **Install**.
4. Download the latest `.apk` and click **Install**.
---
@@ -57,30 +44,21 @@ https://gitlab.com/vyla-entertainment/TV/-/releases
### Prerequisites
- Node.js 20+
- Java Development Kit (JDK 17 or JDK 21)
- JDK 17 or JDK 21
- Android SDK with Android TV API 26+
### Build Steps
```bash
# Clone the repository
git clone https://gitlab.com/vyla-entertainment/TV.git vyla-tv
cd vyla-tv
# Install JavaScript asset bundling tools
npm install
# Build JS resolver bundle and compile debug APK via Gradle
npm run dev
```
The resulting package will be generated at:
Output APK location:
```
android/app/build/outputs/apk/debug/vyla.apk
```
### Developing in Android Studio
To inspect Kotlin source code or UI composables directly:
1. Launch Android Studio.
2. Choose **Open an Existing Project** and select the `android/` subfolder inside the TV repository.
3. Allow Gradle to sync and build targets.
### Android Studio
Open the `android/` subfolder in Android Studio to inspect Kotlin source code and Jetpack Compose layouts.

View File

@@ -15,7 +15,7 @@
- **Resolution**: Terminate any dangling processes in PowerShell:
```powershell
Stop-Process -Name "vyla-home", "node", "cargo" -Force -ErrorAction SilentlyContinue
Stop-Process -Name "vyla-home", "node", "cargo" -Force -ErrorAction SilentlyContinue
```
### 3. Linux: Build Fails with Missing WebKit or AppIndicator
@@ -38,7 +38,8 @@ If Vyla Home launches but media fails to resolve:
- **Resolution**: Disable private DNS mode via ADB:
```powershell
& "$env:LOCALAPPDATA\Android\Sdk\platform-tools\adb.exe" shell settings put global private_dns_mode off
& "$env:LOCALAPPDATA\Android\Sdk\platform-tools\adb.exe"
shell settings put global private_dns_mode off
```
### 2. Dumping Crash Logs from Device
@@ -84,10 +85,8 @@ To capture a full logcat dump of crash traces:
- **Resolution**:
```bash
# Find PID on Linux
lsof -i :7860
# Kill hung process
kill -9 <PID>
lsof -i :7860
kill -9 <PID>
```
### 3. TMDB API Warnings in Console

View File

@@ -15,6 +15,7 @@ const CSS_ORDER = [
'features.css',
'overlay.css',
'prose.css',
'highlight.css',
'404.css'
];

View File

@@ -1,40 +1,36 @@
# Contributing a Source Provider
<p class="page-description">Complete technical guide for authoring, testing, and contributing a new video scraping resolver to the Vyla SDK.</p>
<p class="page-description">Technical guide for authoring, testing, and contributing a new video scraping resolver to the Vyla SDK.</p>
## Overview
The Vyla SDK maintains its high reliability through a distributed collection of independent provider scrapers located in `src/sources/`. We welcome contributions of new sources that expand coverage for movies, television, and international content.
The Vyla SDK uses independent provider scrapers located in `src/sources/`. We welcome contributions that expand coverage for movies and television series.
---
## Scraper Architecture
## Scraper Contract
Every provider module is a self-contained ES Module adhering to a single exported contract:
Every provider module is an ES Module exporting a single function:
```typescript
export async function getStream(args: StreamArgs): Promise<StreamResult | { allUrls: StreamResult[] }>
```
### The `args` Object
When your scraper's `getStream()` is invoked, the SDK passes a normalized `args` context containing:
| Field | Type | Description |
| :--- | :--- | :--- |
| `id` | `string \| number` | The TMDB numeric media ID or string IMDb ID. |
| `s` | `number \| null` | Season number for television episodes (or `null` for movies). |
| `e` | `number \| null` | Episode number for television episodes (or `null` for movies). |
| `clientIP` | `string \| null` | The viewer's client IP address (if passed from an upstream proxy). |
| `fetch` | `Function` | Node.js fetch wrapper pre-configured with timeout controls. |
| `imdbId` | `string \| null` | Automatically resolved IMDb ID (if TMDB API key is configured). |
| `title` | `string \| null` | Verified media title string. |
| `year` | `number \| null` | Media release year. |
### Context Arguments (`args`)
- `id` *(string | number)* — TMDB numeric ID or string IMDb ID.
- `s` *(number | null)* — Season number for television series (or `null` for movies).
- `e` *(number | null)* — Episode number for television series (or `null` for movies).
- `clientIP` *(string | null)* — Client IP address forwarded for geo-token generation.
- `fetch` *(Function)* — Pre-configured fetch wrapper with timeout controls.
- `imdbId` *(string | null)* — Auto-resolved IMDb ID.
- `title` *(string | null)* — Verified title string.
- `year` *(number | null)* — Media release year.
---
## Step-by-Step Implementation Guide
## Implementation Steps
### Step 1: Create the Scraper Module
### 1. Create the Scraper Module
Create a new file in `src/sources/<source_name>.js`:
```javascript
@@ -43,7 +39,7 @@ const BASE_URL = 'https://provider-domain.example.com';
const DEFAULT_HEADERS = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36',
'Referer': `${BASE_URL}/`,
'Origin': BASE_URL,
'Origin': BASE_URL
};
export async function getStream({ id, s, e, imdbId }) {
@@ -80,9 +76,9 @@ export async function getStream({ id, s, e, imdbId }) {
---
### Step 2: Register in `src/config.js`
### 2. Register in `src/config.js`
Add your provider configuration to the `SOURCES` array in `src/config.js`:
Add the source configuration:
```javascript
{
@@ -99,41 +95,36 @@ Add your provider configuration to the `SOURCES` array in `src/config.js`:
}
```
#### Configuration Field Guidelines:
- `key`: Lowercase alphanumeric identifier without spaces (e.g. `vidlink`, `cinecat`).
- `label`: Clean display title shown in UI menus.
- `proxyParam`: Unique 2-to-4 character query flag used by the Stream API HLS proxy (must not collide with existing flags).
- `timeout`: Maximum execution budget before aborting (typically 8,000ms–15,000ms).
- `jitter`: Random delay ceiling (typically 300ms–500ms) to avoid server burst locks.
**Key Fields:**
- `key`: Lowercase identifier without spaces (e.g. `mysource`).
- `label`: Clean display label for client UIs.
- `proxyParam`: Unique 2-to-4 character query flag for the HLS proxy.
- `timeout`: Maximum execution budget in ms (typically 8,000–15,000).
- `jitter`: Random delay ceiling in ms (typically 300–500).
---
### Step 3: Local Testing
### 3. Local Testing
Run your scraper against known TMDB titles using the test runner:
Run your scraper against known TMDB titles:
```bash
# Test a known movie (The Dark Knight - TMDB 155)
node test/index.js mysource 155
# Test a known TV episode (Breaking Bad S01E01 - TMDB 1396)
node test/index.js mysource 1396 1 1
```
Verify that:
1. A valid URL is printed.
2. The `type` is correctly identified as `'hls'` or `'mp4'`.
1. A valid URL is returned.
2. The format is correctly identified as `hls` or `mp4`.
3. Required upstream headers (`Referer`, `Origin`, `User-Agent`) are included.
4. Testing an invalid ID throws a clean error without crashing the process.
4. An invalid ID throws a clean error without crashing.
---
## Contribution Rules & Checklist
Before submitting a Pull Request to the GitLab repository:
## Contribution Checklist
- [ ] Code is formatted with clean ES Module syntax.
- [ ] No hardcoded personal API keys or credentials.
- [ ] Provider does not execute external untrusted `eval()` or un-sanitized script execution.
- [ ] Upstream stream candidate URLs must be verified for playback.
- [ ] Errors are thrown with informative messages (`throw new Error(...)`).
- [ ] No untrusted `eval()` or un-sanitized script execution.
- [ ] Upstream stream candidate URLs are verified for playback.
- [ ] Descriptive errors are thrown with `throw new Error(...)`.

View File

@@ -2,50 +2,32 @@
<p class="page-description">The official Node.js scraping and resolver library for movies, television series, subtitles, downloads, and manga.</p>
<img class="vyla-banner" src="/src/assets/images/devices.webp" alt="Vyla SDK developer tools and multi-platform support" loading="lazy" decoding="async" fetchpriority="low" />
<img class="vyla-banner" src="/src/assets/images/devices.webp" alt="Vyla SDK developer tools" loading="lazy" decoding="async" fetchpriority="low" />
## Overview
`@vyla-entertainment/sdk` is the core media resolver engine powering the entire Vyla ecosystem. Available as a pure ES Module for Node.js environments, the SDK aggregates over 60 streaming providers, extracts multi-language subtitle tracks, discovers direct download packages, and interfaces with manga archives.
Whether you are building a custom backend API, an automated scraper pipeline, a command-line tool, or an embeddable player backend, the SDK provides a clean, typed JavaScript and TypeScript interface.
`@vyla-entertainment/sdk` is the core media resolver engine for Vyla. It runs in Node.js (ES Modules) to query 60+ streaming scrapers, extract subtitles, find media downloads, and browse manga archives.
---
## Installation
Install the package via npm, pnpm, or yarn:
```bash
npm install @vyla-entertainment/sdk
```
### System Requirements
- **Node.js**: v20.0.0 or higher (ESM native)
- **TMDB API Key (Optional but Recommended)**: Required for sources that query metadata by IMDb ID or episode titles. Obtain a free key from [The Movie Database](https://www.themoviedb.org/).
**Requirements:**
- Node.js v20+ (ES Modules)
- Optional: TMDB API key for title metadata
---
## SDK Capabilities
## Capabilities
| Functional Engine | Primary Methods | Output Data & Features |
| :--- | :--- | :--- |
| **Stream Engine** | `getStream()`, `getSources()`, `probeSource()` | Direct MP4 and adaptive HLS (`.m3u8`) links, quality tags, and verified playback flags. |
| **Subtitle Parser** | `getSubtitles()` | Multi-language WebVTT (`.vtt`) and SubRip (`.srt`) track descriptors with language labels. |
| **Download Engine** | `getDownloads()` | Direct high-speed download links, file sizes in bytes, and container specifications (MKV/MP4). |
| **Manga Engine** | `searchManga()`, `getManga()`, `getMangaChapter()` | Titles, covers, chapter indexes, and image page URLs bypassing TLS fingerprint protection. |
### 1. Video Stream Resolution
Queries active providers for direct MP4 and adaptive HLS streaming links based on TMDB movie or TV identifiers.
### 2. Multi-Language Subtitles
Extracts clean `.vtt` and `.srt` subtitle tracks from multiple open subtitle providers, categorized by language label.
### 3. Media Downloads
Resolves high-speed direct media download packages (including MKV containers with file size metadata).
### 4. Manga Discovery & Reading
Features a dedicated manga scraping module leveraging `node-tls-client` with Chrome 131 TLS fingerprinting to search manga, view chapters, and retrieve high-resolution page assets.
- **Streaming**: Resolve direct MP4 and adaptive HLS (`.m3u8`) links with `getStream()`, `getSources()`, and `probeSource()`.
- **Subtitles**: Fetch `.vtt` and `.srt` subtitle tracks with `getSubtitles()`.
- **Downloads**: Find direct video files with file size metadata using `getDownloads()`.
- **Manga**: Search titles, list chapters, and fetch page images with `searchManga()` and `getMangaChapter()`.
---
@@ -59,25 +41,24 @@ const sdk = new VylaSDK({
});
const sources = sdk.getSources(true);
console.log(`Active providers: ${sources.length}`);
console.log(`Active sources: ${sources.length}`);
try {
const stream = await sdk.getStream('vidlink', 155);
console.log('Stream resolved:', stream.url);
console.log('Format:', stream.type);
console.log(stream.url, stream.type);
} catch (err) {
console.error('Resolution error:', err.message);
console.error(err.message);
}
const subtitles = await sdk.getSubtitles(155);
console.log(`Found ${subtitles.length} subtitle tracks`);
console.log(`Subtitles found: ${subtitles.length}`);
```
---
## SDK Navigation
## Documentation Sections
- **[SDK Quickstart](/sdk/vyla-sdk/quickstart)**: Practical, step-by-step tutorial for resolving movies, TV episodes, and handling errors.
- **[Complete API Reference](/sdk/vyla-sdk/reference)**: Exhaustive documentation of all classes, methods, parameters, and TypeScript types.
- **[Manga Module Guide](/sdk/vyla-sdk/manga)**: Guide for manga search, chapter page extraction, and aspect-ratio rendering.
- **[Contributing a Source](/sdk/vyla-sdk/contributing)**: How to author, test, and contribute a new provider scraper.
- **[Quickstart](/sdk/vyla-sdk/quickstart)** — Step-by-step tutorial for movies, TV episodes, and error handling.
- **[API Reference](/sdk/vyla-sdk/reference)** — Complete methods, arguments, return shapes, and TypeScript types.
- **[Manga Module](/sdk/vyla-sdk/manga)** — Manga search, chapter indexes, and page loading.
- **[Contributing Scrapers](/sdk/vyla-sdk/contributing)** — How to write and test new source scrapers.

View File

@@ -1,10 +1,10 @@
# Vyla SDK API Reference
<p class="page-description">Complete technical reference for the `@vyla-entertainment/sdk` package: classes, methods, parameters, interfaces, and TypeScript definitions.</p>
<p class="page-description">Technical reference for `@vyla-entertainment/sdk`: classes, methods, parameters, and TypeScript definitions.</p>
## Class: `VylaSDK`
The primary entry point of the package. Instantiated to manage scraper execution, health probing, subtitle retrieval, downloads, and manga discovery.
The primary entry point for media scraping, subtitle retrieval, downloads, and manga discovery.
```typescript
import VylaSDK from '@vyla-entertainment/sdk';
@@ -18,15 +18,10 @@ import VylaSDK from '@vyla-entertainment/sdk';
constructor(options?: VylaSDKOptions)
```
Creates a new instance of `VylaSDK`.
**Parameters:**
- `options.tmdbApiKey` *(string, optional)* — TMDB v3 API Key for metadata lookups.
- `options.penguManifest` *(any, optional)* — Custom manifest for the Pengu provider source.
#### Parameters
| Parameter | Type | Required | Default | Description |
| :--- | :--- | :--- | :--- | :--- |
| `options.tmdbApiKey` | `string` | Optional | `null` | Your TMDB v3 API Key. Highly recommended; required by sources that query metadata by IMDb ID or episode name. |
| `options.penguManifest` | `any` | Optional | `null` | Custom manifest definition for the Pengu provider source. |
#### Example
```javascript
const sdk = new VylaSDK({
tmdbApiKey: '9a8b7c6d5e4f3a2b1c0d9e8f7a6b5c4d'
@@ -41,20 +36,16 @@ const sdk = new VylaSDK({
getSources(excludeDisabled?: boolean): SourceConfig[]
```
Returns the registered list of provider scraper configurations.
Returns registered provider scraper configurations.
#### Parameters
| Parameter | Type | Required | Default | Description |
| :--- | :--- | :--- | :--- | :--- |
| `excludeDisabled` | `boolean` | Optional | `false` | When `true`, filters out providers marked with `disabled: true`. |
**Parameters:**
- `excludeDisabled` *(boolean, optional)* — When `true`, filters out scrapers with `disabled: true`. Default: `false`.
#### Returns
Array of [`SourceConfig`](#interface-sourceconfig) objects.
**Returns:** Array of [`SourceConfig`](#interface-sourceconfig) objects.
#### Example
```javascript
const activeSources = sdk.getSources(true);
console.log(`Available active providers: ${activeSources.map(s => s.key).join(', ')}`);
console.log(activeSources.map(s => s.key).join(', '));
```
---
@@ -71,29 +62,19 @@ async getStream(
): Promise<StreamResult | { allUrls: StreamResult[] }>
```
Executes the scraper module for a specified provider and resolves streaming candidate URLs.
Executes a provider scraper module to resolve candidate stream URLs.
#### Parameters
| Parameter | Type | Required | Default | Description |
| :--- | :--- | :--- | :--- | :--- |
| `key` | `string` | Required | — | The source key identifier (e.g. `'vidlink'`, `'vixsrc'`). |
| `id` | `string \| number` | Required | — | Numeric TMDB ID or string IMDb ID (for movies and TV shows). |
| `s` | `number \| null` | Optional | `null` | Season number (required for TV episodes). |
| `e` | `number \| null` | Optional | `null` | Episode number (required for TV episodes). |
| `clientIP` | `string \| null` | Optional | `null` | Client IP address passed for upstream geo-token generation. |
**Parameters:**
- `key` *(string, required)* — Provider key (e.g. `'vidlink'`, `'vixsrc'`).
- `id` *(string | number, required)* — TMDB ID or IMDb ID.
- `s` *(number, optional)* — Season number (for TV series). Default: `null`.
- `e` *(number, optional)* — Episode number (for TV series). Default: `null`.
- `clientIP` *(string, optional)* — Client IP address passed for upstream geo-token generation. Default: `null`.
#### Returns
A Promise resolving to a [`StreamResult`](#interface-streamresult) object, or `{ allUrls: StreamResult[] }` if the source supports multi-URL extraction.
**Returns:** A Promise resolving to a [`StreamResult`](#interface-streamresult) object.
#### Throws
- `Error('Source with key "..." not found')` if the key does not exist.
- `Error('Source module for key "..." not found')` if the source module is disabled or failed to import.
- Upstream network errors or scraper extraction errors if the title cannot be found.
#### Example
```javascript
const movieStream = await sdk.getStream('vidlink', 155);
const episodeStream = await sdk.getStream('vixsrc', 1396, 1, 1);
```
@@ -109,17 +90,14 @@ async getSubtitles(
): Promise<SubtitleResult[]>
```
Discovers and parses all available subtitle tracks for a movie or TV episode.
Retrieves parsed subtitle tracks for a movie or TV episode.
#### Parameters
| Parameter | Type | Required | Default | Description |
| :--- | :--- | :--- | :--- | :--- |
| `id` | `string \| number` | Required | — | TMDB media identifier. |
| `s` | `number \| null` | Optional | `null` | Season number (TV shows). |
| `e` | `number \| null` | Optional | `null` | Episode number (TV shows). |
**Parameters:**
- `id` *(string | number, required)* — TMDB media ID.
- `s` *(number, optional)* — Season number. Default: `null`.
- `e` *(number, optional)* — Episode number. Default: `null`.
#### Returns
A Promise resolving to an array of [`SubtitleResult`](#interface-subtitleresult) objects.
**Returns:** Array of [`SubtitleResult`](#interface-subtitleresult) objects.
---
@@ -135,8 +113,7 @@ async getDownloads(
Resolves direct media download packages and torrent/debrid links.
#### Returns
A Promise resolving to an array of [`DownloadResult`](#interface-downloadresult) objects.
**Returns:** Array of [`DownloadResult`](#interface-downloadresult) objects.
---
@@ -146,15 +123,12 @@ A Promise resolving to an array of [`DownloadResult`](#interface-downloadresult)
async probeSource(key: string): Promise<ProbeResult>
```
Sends a lightweight probe request against the designated source to measure responsiveness and availability.
Sends a probe request against a single provider to check responsiveness.
#### Parameters
| Parameter | Type | Required | Description |
| :--- | :--- | :--- | :--- |
| `key` | `string` | Required | The provider key to probe. |
**Parameters:**
- `key` *(string, required)* — Provider key to probe.
#### Returns
A Promise resolving to a [`ProbeResult`](#interface-proberesult) object: `{ ok: boolean, ms: number | null }`.
**Returns:** `{ ok: boolean, ms: number | null }`.
---
@@ -164,10 +138,7 @@ A Promise resolving to a [`ProbeResult`](#interface-proberesult) object: `{ ok:
async probeAllSources(): Promise<ProbeAllResult>
```
Concurrently probes all active (non-disabled) sources and compiles a live health map.
#### Returns
A Promise resolving to a [`ProbeAllResult`](#type-probeallresult) dictionary mapping source keys to their probe results.
Concurrently probes all active sources and returns a health map.
---
@@ -177,19 +148,19 @@ A Promise resolving to a [`ProbeAllResult`](#type-probeallresult) dictionary map
```typescript
async searchManga(query: string, page?: number, perPage?: number): Promise<any>
```
Queries the manga catalog via `atsu.moe` using Chrome 131 TLS fingerprinting. Returns paginated title listings.
Queries the manga catalog via `atsu.moe` using Chrome 131 TLS fingerprinting.
#### `getManga(id)`
```typescript
async getManga(id: string): Promise<any>
```
Retrieves full manga details including description, genres, author, status, and chapter list.
Retrieves full manga details, author, genres, and chapter list.
#### `getMangaChapter(mangaId, chapterId)`
```typescript
async getMangaChapter(mangaId: string, chapterId: string): Promise<any>
```
Resolves ordered image URLs, dimensions, and aspect ratios for all pages in a given chapter.
Resolves ordered image URLs and dimensions for all pages in a chapter.
---

View File

@@ -455,10 +455,16 @@
--hl-inserted-bg: rgba(0, 255, 204, 0.2);
}
html.banner-dismissed,
body.banner-dismissed {
--header-height: 109px;
}
html.banner-dismissed #banner,
body.banner-dismissed #banner {
display: none !important;
}
* {
box-sizing: border-box;
}
@@ -963,8 +969,6 @@ footer#site-footer {
padding: 8px;
border-radius: 16px;
background: var(--bg-menu);
border: 1px solid var(--border);
box-shadow: 0 16px 40px rgba(0, 0, 0, 0.5);
z-index: 50;
opacity: 0;
visibility: hidden;
@@ -2303,6 +2307,41 @@ main.prose h4 {
}
/* --- features.css --- */
.eyebrow-actions {
display: inline-flex;
align-items: center;
gap: 4px;
}
.copy-md-btn {
display: flex;
align-items: center;
justify-content: center;
width: 30px;
height: 30px;
padding: 0;
border: none;
border-radius: 50%;
background: none;
color: var(--muted);
cursor: pointer;
transition: color 0.15s ease, background 0.15s ease;
}
.copy-md-btn .ico {
width: 17px;
height: 17px;
}
.copy-md-btn:hover {
background: var(--hover-bg);
color: var(--text);
}
.copy-md-btn.copied {
color: #10b981;
}
.bm-btn {
display: flex;
align-items: center;
@@ -3729,6 +3768,85 @@ aside#toc li a.active {
}
}
/* --- highlight.css --- */
pre[class*="language-"],
code[class*="language-"] {
color: var(--code-text);
}
.token.comment,
.token.prolog,
.token.doctype,
.token.cdata {
color: var(--hl-comment) !important;
font-style: italic;
}
.token.punctuation {
color: var(--hl-punct) !important;
}
.token.namespace {
opacity: 0.7;
}
.token.property,
.token.tag,
.token.boolean,
.token.number,
.token.constant,
.token.symbol,
.token.deleted {
color: var(--hl-num) !important;
}
.token.selector,
.token.attr-name,
.token.string,
.token.char,
.token.builtin,
.token.inserted {
color: var(--hl-string) !important;
}
.token.operator,
.token.entity,
.token.url,
.language-css .token.string,
.style .token.string {
color: var(--hl-op) !important;
}
.token.atrule,
.token.attr-value,
.token.keyword {
color: var(--hl-keyword) !important;
font-weight: 600;
}
.token.function,
.token.function-name,
.token.class-name {
color: var(--hl-func) !important;
font-weight: 500;
}
.token.regex,
.token.important,
.token.variable,
.token.parameter {
color: var(--hl-var) !important;
}
.token.bold {
font-weight: 700;
}
.token.italic {
font-style: italic;
}
/* --- 404.css --- */
body.is-404 #sidebar,
body.is-404 #toc,

File diff suppressed because one or more lines are too long

View File

@@ -1,3 +1,38 @@
.eyebrow-actions {
display: inline-flex;
align-items: center;
gap: 4px;
}
.copy-md-btn {
display: flex;
align-items: center;
justify-content: center;
width: 30px;
height: 30px;
padding: 0;
border: none;
border-radius: 50%;
background: none;
color: var(--muted);
cursor: pointer;
transition: color 0.15s ease, background 0.15s ease;
}
.copy-md-btn .ico {
width: 17px;
height: 17px;
}
.copy-md-btn:hover {
background: var(--hover-bg);
color: var(--text);
}
.copy-md-btn.copied {
color: #10b981;
}
.bm-btn {
display: flex;
align-items: center;

View File

@@ -7,66 +7,70 @@ code[class*="language-"] {
.token.prolog,
.token.doctype,
.token.cdata {
color: var(--hl-comment);
color: var(--hl-comment) !important;
font-style: italic;
}
.token.punctuation {
color: var(--hl-punct);
color: var(--hl-punct) !important;
}
.token.function,
.token.function-name {
color: var(--hl-func);
font-weight: 500;
.token.namespace {
opacity: 0.7;
}
.token.keyword,
.token.selector {
color: var(--hl-keyword);
font-weight: 600;
.token.property,
.token.tag,
.token.boolean,
.token.number,
.token.constant,
.token.symbol,
.token.deleted {
color: var(--hl-num) !important;
}
.token.string,
.token.url {
color: var(--hl-string);
}
.token.variable,
.token.selector,
.token.attr-name,
.token.property {
color: var(--hl-var);
.token.string,
.token.char,
.token.builtin,
.token.inserted {
color: var(--hl-string) !important;
}
.token.operator,
.token.entity {
color: var(--hl-op);
.token.entity,
.token.url,
.language-css .token.string,
.style .token.string {
color: var(--hl-op) !important;
}
.token.number,
.token.boolean,
.token.constant {
color: var(--hl-num);
}
.token.parameter {
color: var(--hl-param);
.token.atrule,
.token.attr-value,
.token.keyword {
color: var(--hl-keyword) !important;
font-weight: 600;
}
.token.function,
.token.function-name,
.token.class-name {
color: var(--hl-class);
color: var(--hl-func) !important;
font-weight: 500;
}
.token.tag {
color: var(--hl-tag);
.token.regex,
.token.important,
.token.variable,
.token.parameter {
color: var(--hl-var) !important;
}
.token.deleted {
color: var(--hl-deleted-color);
background-color: var(--hl-deleted-bg);
.token.bold {
font-weight: 700;
}
.token.inserted {
color: var(--hl-inserted-color);
background-color: var(--hl-inserted-bg);
}
.token.italic {
font-style: italic;
}

View File

@@ -443,8 +443,6 @@ footer#site-footer {
padding: 8px;
border-radius: 16px;
background: var(--bg-menu);
border: 1px solid var(--border);
box-shadow: 0 16px 40px rgba(0, 0, 0, 0.5);
z-index: 50;
opacity: 0;
visibility: hidden;

View File

@@ -454,10 +454,16 @@
--hl-inserted-bg: rgba(0, 255, 204, 0.2);
}
html.banner-dismissed,
body.banner-dismissed {
--header-height: 109px;
}
html.banner-dismissed #banner,
body.banner-dismissed #banner {
display: none !important;
}
* {
box-sizing: border-box;
}

View File

@@ -487,7 +487,11 @@ function cgSwitch(btn) {
t.classList.toggle("active", t === btn);
});
group.querySelectorAll(".code-group-panel").forEach(function (p, i) {
p.classList.toggle("active", String(i) === index);
var on = String(i) === index;
p.classList.toggle("active", on);
if (on && window.Prism) {
window.Prism.highlightAllUnder(p);
}
});
window.dispatchEvent(new Event("resize"));
}
@@ -663,6 +667,63 @@ function isBookmarked(href) {
return getBookmarks().some(function (b) { return b.href === href; });
}
function buildCopyMarkdownButton(rawMd) {
var b = document.createElement("button");
b.type = "button";
b.className = "copy-md-btn";
b.setAttribute("aria-label", "Copy Markdown");
b.title = "Copy page as Markdown";
b.innerHTML = icon("copy");
b.addEventListener("click", function (e) {
e.stopPropagation();
var text = rawMd || "";
var onSuccess = function () {
b.innerHTML = icon("check");
b.classList.add("copied");
setTimeout(function () {
b.innerHTML = icon("copy");
b.classList.remove("copied");
}, 2000);
};
if (navigator.clipboard && navigator.clipboard.writeText) {
navigator.clipboard.writeText(text).then(onSuccess).catch(function () {
fallbackCopyText(text, onSuccess);
});
} else {
fallbackCopyText(text, onSuccess);
}
});
return b;
}
function fallbackCopyText(text, cb) {
var ta = document.createElement("textarea");
ta.value = text;
ta.style.position = "fixed";
ta.style.opacity = "0";
document.body.appendChild(ta);
ta.focus();
ta.select();
try {
document.execCommand("copy");
if (cb) cb();
} catch (e) { }
document.body.removeChild(ta);
}
function highlightAllCode(container) {
var target = container || document;
if (window.Prism && typeof window.Prism.highlightAllUnder === "function") {
window.Prism.highlightAllUnder(target);
} else {
setTimeout(function () {
if (window.Prism && typeof window.Prism.highlightAllUnder === "function") {
window.Prism.highlightAllUnder(target);
}
}, 60);
}
}
function buildBookmarkButton(route) {
var b = document.createElement("button");
b.type = "button";
@@ -858,6 +919,7 @@ function parseMarkdown(md) {
var inCodeGroup = false;
var codeGroupTabs = [];
var codeGroupPanels = [];
var pendingTabTitle = "";
function flushList() {
if (inList) {
@@ -921,9 +983,24 @@ function parseMarkdown(md) {
inCodeGroup = true;
codeGroupTabs = [];
codeGroupPanels = [];
pendingTabTitle = "";
continue;
}
if (inCodeGroup && !inPre) {
var tabCommentMatch = line.match(/<!--\s*tab:\s*([^-]+?)\s*-->/i);
if (tabCommentMatch) {
pendingTabTitle = tabCommentMatch[1].trim();
continue;
}
if (line.trim().startsWith("<!--") && line.trim().endsWith("-->")) {
continue;
}
if (line.trim().length === 0) {
continue;
}
}
if (inCodeGroup && line === ":::") {
flushList();
flushTable();
@@ -936,13 +1013,14 @@ function parseMarkdown(md) {
codeGroupPanels.forEach(function (panel, idx) {
var activeClass = idx === 0 ? " active" : "";
var langClass = panel.lang ? ' class="language-' + panel.lang + '"' : ' class="language-none"';
cgHtml += '<div class="code-group-panel' + activeClass + '"><pre><code' + langClass + '>' + esc(panel.code) + '</code></pre></div>';
cgHtml += '<div class="code-group-panel' + activeClass + '"><pre' + langClass + '><code' + langClass + '>' + esc(panel.code) + '</code></pre></div>';
});
cgHtml += '</div></div>';
out.push(cgHtml);
inCodeGroup = false;
codeGroupTabs = [];
codeGroupPanels = [];
pendingTabTitle = "";
continue;
}
@@ -953,27 +1031,37 @@ function parseMarkdown(md) {
inPre = true;
var info = line.slice(3).trim();
var tabMatch = info.match(/\[([^\]]+)\]/);
var tabTitle = tabMatch ? tabMatch[1] : "";
var tabTitle = tabMatch ? tabMatch[1] : (pendingTabTitle || "");
pendingTabTitle = "";
preLang = info.replace(/\[([^\]]+)\]/, "").trim();
if (inCodeGroup && tabTitle) {
codeGroupTabs.push(tabTitle);
if (inCodeGroup) {
codeGroupTabs.push(tabTitle || preLang || ("Tab " + (codeGroupTabs.length + 1)));
}
preCode = [];
} else {
inPre = false;
var codeContent = preCode.join("\n");
var resolvedLang = preLang;
var resolvedLang = (preLang || "").toLowerCase().trim();
if (resolvedLang === "js") resolvedLang = "javascript";
else if (resolvedLang === "ts") resolvedLang = "typescript";
else if (resolvedLang === "sh" || resolvedLang === "shell" || resolvedLang === "zsh") resolvedLang = "bash";
else if (resolvedLang === "ps" || resolvedLang === "ps1") resolvedLang = "powershell";
else if (resolvedLang === "py") resolvedLang = "python";
else if (resolvedLang === "rs") resolvedLang = "rust";
else if (resolvedLang === "kt") resolvedLang = "kotlin";
else if (resolvedLang === "yml") resolvedLang = "yaml";
else if (resolvedLang === "md") resolvedLang = "markdown";
if (!resolvedLang) {
if (codeContent.trim().startsWith("{") || codeContent.trim().startsWith("[")) resolvedLang = "json";
else if (codeContent.indexOf("=") !== -1 && !codeContent.includes("function") && !codeContent.includes(";")) resolvedLang = "ini";
else if (codeContent.startsWith("http://") || codeContent.startsWith("https://")) resolvedLang = "http";
else if (codeContent.startsWith("http://") || codeContent.startsWith("https://") || codeContent.startsWith("GET ") || codeContent.startsWith("POST ")) resolvedLang = "http";
else resolvedLang = "bash";
}
if (inCodeGroup) {
codeGroupPanels.push({ lang: resolvedLang, code: codeContent });
} else {
var langClass = resolvedLang ? ' class="language-' + resolvedLang + '"' : ' class="language-none"';
out.push('<div class="code-wrap"><pre><code' + langClass + ">" + esc(codeContent) + "</code></pre></div>");
out.push('<div class="code-wrap"><pre' + langClass + '><code' + langClass + ">" + esc(codeContent) + "</code></pre></div>");
}
}
continue;
@@ -1685,16 +1773,18 @@ function loadPage() {
var hasApiToken = md.indexOf("YOUR_API_URL") !== -1;
contentEl.innerHTML = parseMarkdown(personalize(md));
if (window.Prism) {
window.Prism.highlightAllUnder(contentEl);
}
var eyebrow = document.createElement("div");
eyebrow.className = "eyebrow";
var eyebrowText = document.createElement("span");
eyebrowText.textContent = route.group.title;
eyebrow.appendChild(eyebrowText);
eyebrow.appendChild(buildBookmarkButton(route));
var actions = document.createElement("div");
actions.className = "eyebrow-actions";
actions.appendChild(buildCopyMarkdownButton(md));
actions.appendChild(buildBookmarkButton(route));
eyebrow.appendChild(actions);
var h1 = contentEl.querySelector("h1");
if (h1) h1.parentNode.insertBefore(eyebrow, h1);
@@ -1725,6 +1815,7 @@ function loadPage() {
updateFooterNav(route.item);
updateToc();
addCopyButtons();
highlightAllCode(contentEl);
initCodeFades();
wrapTables();
resolveLatestDownloads();
@@ -2183,7 +2274,6 @@ document.addEventListener("DOMContentLoaded", function () {
buildSearch();
buildSettings();
initEasterEggs();
initContribSheet();
bindLightboxClicks();
loadPage();

28
src/js/prism.js Normal file

File diff suppressed because one or more lines are too long

View File

@@ -1,143 +1,101 @@
# Architecture & System Design
<p class="page-description">Deep architectural breakdown of the Vyla Ecosystem: component boundaries, communication protocols, video delivery pipelines, and failover topologies.</p>
<p class="page-description">Component boundaries, communication protocols, and media pipelines across the Vyla ecosystem.</p>
## High-Level Architecture
## System Layers
The Vyla architecture cleanly separates **presentation**, **gateway mediation**, and **content resolution**. This decoupling ensures that any individual layer can be replaced, scaled, or run independently.
Vyla decouples presentation, streaming gateway mediation, and provider scraping into three distinct tiers:
| Architecture Tier | Components | Protocols & Interfaces | Core Responsibilities |
| :--- | :--- | :--- | :--- |
| **Presentation Tier** | Vyla Home, Vyla Go, Vyla TV, Third-Party Web Players | SSE (HTTP/1.1), REST (JSON), HLS / DASH | Render user interface, manage local state, execute video playback via HLS.js or ExoPlayer Media3. |
| **Gateway Tier** | Stream API (`server.js`) | Node.js Cluster IPC, HTTP/REST, SSE, Reverse Proxy | Expose public endpoints, manage 1,500-entry in-memory cache, rewrite M3U8 playlists & AES keys, strip 120-byte obfuscation masks. |
| **Resolver Tier** | Vyla SDK (`@vyla-entertainment/sdk`) | Direct ESM function calls, HTTP/HTTPS scrapers | Scrape 60+ upstream providers concurrently, enforce random jitter delays, run two-phase stream validation. |
| **Upstream Hosts** | Public Third-Party CDNs & Video Hosts | Encrypted TLS/HTTPS, M3U8, MP4 | Deliver raw media segments and video assets. |
- **Presentation Tier** (`Vyla Home`, `Vyla Go`, `Vyla TV`, custom web players): Handles the UI, player state, and media rendering using ExoPlayer Media3 or HLS.js. Connects via Server-Sent Events (SSE) and HTTP.
- **Gateway Tier** (`Stream API`): A Node.js cluster gateway. Manages in-memory LRU caching (1,500 entries, 5-minute TTL), rewrites HLS manifests and AES keys, and strips obfuscation headers.
- **Resolver Tier** (`@vyla-entertainment/sdk`): Directly queries 60+ scrapers with jitter delays, performs two-phase stream verification, and resolves subtitle tracks.
- **Upstream Hosts**: External CDNs and video hosts serving raw HLS manifests, video segments, and subtitle files.
---
## Component Boundaries & Responsibilities
## Component Details
### 1. Vyla Home (Desktop)
- **Runtime Environment**: Built on [Tauri v2](https://v2.tauri.app/) using Rust and an embedded Node.js sidecar service.
- **IPC Protocol**: Communicates with the sidecar process via standard input/output (`stdin`/`stdout`) passing JSON-RPC messages.
- **Responsibilities**:
- Manages application lifecycle, window management, and native system tray.
- Automatically checks, verifies cryptographic signatures (`update-signing.pem`), and extracts delta updates from GitLab releases.
- Houses the local `ServiceManager` and `Patcher` for offline and local service fallback.
- Dispatches Discord Rich Presence activity updates (`discord-rpc.js`).
- Persists configuration and cache to platform-specific AppData locations.
### Vyla Home (Desktop)
- **Runtime**: Built with Tauri v2, Rust, and a local Node.js sidecar service.
- **IPC**: Communicates with the sidecar over standard I/O passing JSON-RPC messages.
- **Features**: System tray integration, automated delta updates, Discord Rich Presence, and local cache persistence.
### 2. Vyla Go (Mobile)
- **Runtime Environment**: Tauri v2 for Android (`tauri-android`) targeting ARM64 (`aarch64`) and x86_64 architectures.
- **Responsibilities**:
- Touch-optimized interface utilizing WebGL hardware shaders and glassmorphism styling.
- Automated resource management: fetches, unpacks, and hot-patches runtime assets directly from remote releases without requiring full APK reinstallations for minor script updates.
- Background playback coordination and media notification integration.
### Vyla Go (Mobile)
- **Runtime**: Tauri v2 Mobile targeting Android ARM64 and x86_64 architectures.
- **Features**: Touch UI with WebGL shaders, background audio playback, and runtime asset patching.
### 3. Vyla TV (Leanback)
- **Runtime Environment**: Native Android application written in Kotlin utilizing Jetpack Compose and Google's [ExoPlayer (Media3)](https://developer.android.com/media/media3/exoplayer).
- **Responsibilities**:
- Strictly optimized for D-pad remote controls, 10-foot viewing distances, and Android TV / Google TV operating systems.
- Native HLS stream parsing, hardware-accelerated 4K/HDR decoding, playback speed manipulation, audio track selection, and subtitle overlay.
- Local client storage for user favorites, watch progress resume, and TMDB catalog browsing.
### Vyla TV (Leanback)
- **Runtime**: Native Android app written in Kotlin with Jetpack Compose and ExoPlayer (Media3).
- **Features**: D-pad remote navigation, hardware-accelerated 4K/HDR decoding, audio track selection, and watch progress tracking.
### 4. Stream API (Gateway)
- **Runtime Environment**: Node.js microservice utilizing the native `cluster` module for multi-core process forking.
- **Responsibilities**:
- Acts as an HTTP/SSE bridge for clients that cannot run the Node.js SDK directly (e.g. mobile apps, TV apps, browser frontends).
- Emits real-time playback events (`meta`, `source`, `done`) over Server-Sent Events.
- Proxies HLS media streams (`/api?url=...`), automatically rewriting playlist chunk URIs and AES encryption key endpoints to bypass browser CORS and network restrictions.
- Strips obfuscation masks (e.g. 120-byte fake PNG/JPEG headers used by ByteDance/TikTok-based content delivery networks).
- Caches verified results for 5 minutes (TTL = 300,000 ms) in a shared inter-process memory map.
### Stream API (Gateway)
- **Runtime**: Node.js microservice using the native `cluster` module.
- **Features**: Emits real-time playback events (`meta`, `source`, `done`) over SSE. Proxies HLS streams, rewrites encryption keys, strips 120-byte fake image masks, and caches verified sources for 5 minutes.
### 5. Vyla SDK (`@vyla-entertainment/sdk`)
- **Runtime Environment**: Pure ES Module Node.js library.
- **Responsibilities**:
- Houses the core provider scraping modules (60+ scrapers in `src/sources/`).
- Implements jitter scheduling, retry backoff, and custom header injection (`User-Agent`, `Referer`, `Origin`).
- Performs two-phase verification on resolved candidate URLs.
- Houses the Manga resolver engine leveraging `node-tls-client` to bypass TLS fingerprint blocking on `atsu.moe`.
### Vyla SDK (`@vyla-entertainment/sdk`)
- **Runtime**: Pure ES Module Node.js package.
- **Features**: Contains 60+ modular scrapers in `src/sources/`, random jitter scheduling, retry backoff, two-phase validation (`verifyStream` / `verifyPlayable`), and Manga scraping with TLS client fingerprint spoofing.
---
## Playback Data Flow: Step-by-Step
## Playback Pipeline
Understanding how a media stream is discovered, verified, and played:
1. **Client Establishes SSE Connection**
The viewer selects a title (e.g., TMDB ID `155`), and the client initiates an HTTP Server-Sent Events handshake:
1. **Client Handshake**: The player initiates an HTTP Server-Sent Events connection with a TMDB ID:
```http
GET /movie?id=155 HTTP/1.1
Host: api.vyla.cc
Accept: text/event-stream
```
2. **Stream API Emits Initial Metadata**
Stream API validates the identifier, queries TMDB for metadata and artwork, queries subtitle providers, and immediately delivers a `meta` event:
2. **Metadata Delivery**: Stream API fetches TMDB details and subtitle tracks, emitting the first event immediately:
```json
data: {"type": "meta", "meta": {"title": "The Dark Knight", "year": 2008}, "subtitles": [{"lang": "English", "url": "..."}]}
```
3. **SDK Initiates Concurrent Provider Queries**
The SDK filters active provider scrapers from configuration, applies randomized per-source jitter (0–500 ms) to avoid burst-rate triggers, and initiates upstream HTTP scraping requests concurrently.
3. **Concurrent Scraper Queries**: The SDK queries active scrapers concurrently with randomized jitter (0–500 ms) to prevent rate limits.
4. **Two-Phase Stream Verification**
- **Phase A (`verifyStream`)**: Executes an HTTP `HEAD` or range `GET` request against candidate stream URLs. Responses with status $\ge 400$ are discarded immediately.
- **Phase B (`verifyPlayable`)**: Downloads and parses the master HLS manifest (`#EXT-X-STREAM-INF`), validates chunk resolution, and tests segment availability.
4. **Two-Phase Validation**:
- `verifyStream`: Tests candidate URLs with HTTP `HEAD` or range requests. Any response with status >= 400 is discarded.
- `verifyPlayable`: Validates the master HLS playlist and confirms video segment availability.
5. **Progressive Source Delivery**
As each provider scraper completes two-phase verification, Stream API immediately pushes a `source` event over the active SSE stream:
5. **Progressive Source Emission**: As each scraper verifies a working link, Stream API pushes a `source` event:
```json
data: {"type": "source", "source": "vidlink", "url": "https://api.vyla.cc/api?url=...", "quality": "1080p", "type": "hls"}
```
6. **Instant Client Playback**
The client media player (ExoPlayer, HLS.js, or AVPlayer) immediately begins playback upon receiving the first valid source event (typically within 3–8 seconds). Subsequent incoming sources are enqueued into a client-side fallback list.
6. **Instant Playback**: The player starts playback on the very first source (typically within 3 to 8 seconds) and retains subsequent sources as automatic fallbacks.
7. **Stream Completion**
Once all configured source scrapers have either returned verified streams or reached their timeout thresholds (typically 15–25 seconds), Stream API emits the completion event and closes the connection:
7. **Stream Close**: When all scrapers finish or time out, Stream API emits the completion event:
```json
data: {"type": "done", "total": 4}
```
---
## The HLS Proxy & De-Obfuscation Mechanics
## Reverse Proxy & Mask Stripping
Many upstream streaming hosts employ strict CORS restrictions, IP-bound tokens, or binary header obfuscation. Vyla resolves this transparently:
Many video hosts enforce strict CORS policies or mask video chunks behind fake image headers. Stream API handles this transparently:
### Playlist & Key Rewriting
When a stream URL is proxied through `/api?url=<URL>&<param>=1`, the gateway:
1. Requests the upstream playlist with provider-mandated `Referer` and `Origin` headers.
2. Intercepts `#EXT-X-KEY:METHOD=AES-128,URI="..."` and rewrites the key URI to route through the proxy.
3. Rewrites all chunk lines (`.ts` / `.m4s`) so that browser audio/video fetch calls pass through the proxy with valid `Access-Control-Allow-Origin: *` headers.
When a stream is routed through `/api?url=<URL>`:
1. Stream API fetches the playlist using upstream `Referer` and `Origin` headers.
2. It rewrites `#EXT-X-KEY` AES encryption endpoints to route through the local proxy.
3. It rewrites `.ts` chunk paths so the player can fetch them with permissive CORS headers.
### Byte-Mask Stripping
Certain modern providers mask `.ts` video chunks as fake image files (`image/png`, `image/jpeg`, `letsgocdn`, or TikTok CDN formats) by prepending a 120-byte image header before the MPEG-TS sync byte (`0x47`):
```
[ 120-byte Fake PNG Header (0x89 0x50 0x4E 0x47...) ] + [ MPEG-TS Payload (0x47...) ]
```
When Stream API detects these signatures via regular expressions (`TIKTOK_REGEX`, `STRIP_REGEX`), it slices off the first 120 bytes:
Certain hosts prepend a 120-byte fake image header (`image/png` or `image/jpeg`) before the MPEG-TS sync byte (`0x47`). Stream API detects these headers and strips the prefix:
```javascript
const stripped = (full[0] === 0x89 || full[0] === 0xFF || full[0] === 0x00)
? full.subarray(120)
: full;
```
The resulting clean MPEG-TS stream is delivered to players with `Content-Type: video/MP2T`, enabling seamless playback across all platforms.
The stripped payload is returned with `Content-Type: video/MP2T`.
---
## Deployment Topologies
## Common Deployments
Choose the deployment architecture that fits your infrastructure:
| Topology | Components Used | Best For | Security & Networking |
| :--- | :--- | :--- | :--- |
| **All-in-One Desktop** | Vyla Home | Casual users wanting local playback on a single PC | Fully local, runs on localhost, zero open ports |
| **Living Room TV** | Vyla TV + Hosted Stream API | Smart TVs, Chromecast, NVIDIA Shield | TV connects over LAN/WAN to a secure Stream API gateway |
| **Private Family Server** | Stream API on VPS/NAS | Multi-device households streaming across phones, PCs, and TVs | Run behind Nginx/Caddy with TLS, enable API key protection |
| **Custom Platform** | SDK in Node.js backend | Developers building bespoke streaming web applications | Complete control over DB, auth, caching, and playback logic |
- **Single Desktop**: Run Vyla Home locally on your PC. No external ports or public servers required.
- **Smart TV Setup**: Run Vyla TV on your Android TV device connected to a hosted Stream API gateway on your local network or VPS.
- **Private Home Server**: Run Stream API on a home NAS or VPS behind Nginx with TLS and API key authentication.
- **Custom Application**: Import `@vyla-entertainment/sdk` into your own Node.js backend.

View File

@@ -1,82 +1,47 @@
# Vyla Entertainment
# Vyla
<p class="page-description">A community-built, decentralized media ecosystem and developer platform focused on privacy, user sovereignty, and self-hosted streaming infrastructure.</p>
<p class="page-description">An open-source, modular media ecosystem. Native apps for desktop, mobile, and TV backed by a scraper SDK and self-hosted streaming gateway.</p>
<img class="vyla-banner" src="/src/assets/images/devices.webp" alt="The Vyla Ecosystem across Desktop, Android, and Android TV devices" loading="lazy" decoding="async" fetchpriority="low" />
<img class="vyla-banner" src="/src/assets/images/devices.webp" alt="Vyla across devices" loading="lazy" decoding="async" fetchpriority="low" />
## What is Vyla?
**Vyla** is an open-source, modular media ecosystem built on a simple foundation: **users and developers should have absolute sovereignty over their media experience.**
Vyla is an open-source media stack built for privacy and user control:
Unlike centralized streaming platforms that lock users into closed walled gardens, proprietary codecs, and telemetry tracking, Vyla separates the entire media stack into distinct, reusable layers:
1. **Client Frontends**: Native applications for Desktop (Windows, macOS, Linux), Mobile (Android), and Television (Android TV / Google TV).
2. **Scraper & Resolver SDK**: A high-performance Node.js scraping engine (`@vyla-entertainment/sdk`) aggregating over 60 distributed upstream providers with automatic jitter buffering and HLS playback verification.
3. **Stream API Gateway**: A self-hostable HTTP and Server-Sent Events (SSE) microservice proxy delivering low-latency progressive playback manifests, subtitle tracks, and media downloads.
- **Client Apps**: Native frontends for Desktop (Windows, macOS, Linux), Android Mobile, and Android TV.
- **Vyla SDK (`@vyla-entertainment/sdk`)**: A Node.js scraping engine querying 60+ upstream sources with jitter buffering and stream validation.
- **Stream API**: A self-hostable HTTP and Server-Sent Events (SSE) gateway delivering live stream manifests, subtitles, and downloads.
:::callout-note
**Core Policy**: Vyla does not host, upload, store, or distribute video files or copyrighted media. The ecosystem provides client applications and self-hosted scraping tools that resolve publicly accessible links on demand.
Vyla does not host, upload, or store video files. It resolves publicly accessible web links on demand.
:::
---
## Ecosystem Architecture at a Glance
## Ecosystem Components
The Vyla Ecosystem is designed around modularity. Each component functions autonomously or seamlessly interconnects with other components:
| Layer | Components | Core Capabilities |
| :--- | :--- | :--- |
| **User Interfaces** | **Vyla Home**, **Vyla Go**, **Vyla TV** | Desktop Tauri v2 with embedded sidecar, Android mobile client with WebGL shaders, and native Android TV (Jetpack Compose + ExoPlayer). |
| **Distribution Gateway** | **Stream API** | Node.js cluster gateway, SSE progressive source emitter, M3U8 playlist & key rewriting proxy, and 120-byte fake-image obfuscation stripper. |
| **Resolver Engine** | **Vyla SDK** (`@vyla-entertainment/sdk`) | 60+ modular source scrapers, two-phase stream verification (`verifyStream` / `verifyPlayable`), manga TLS-fingerprinted extraction, and subtitle normalization. |
- **[Vyla Home](/products/home)** — Desktop app built with Tauri v2 and Rust. Includes auto-updates, Discord RPC, and offline fallbacks.
- **[Vyla Go](/products/go)** — Android mobile client with shader animations and background playback.
- **[Vyla TV](/products/tv)** — Android TV and Fire TV app with D-pad navigation and ExoPlayer playback.
- **[Stream API](/stream-api/overview)** — High-concurrency Node.js streaming gateway with cluster caching and M3U8 reverse proxying.
- **[Vyla SDK](/sdk)** — Core resolver module for Node.js backends and scrapers.
- **[Web Player](/products/player)** — Lightweight HLS integration for custom web players.
---
## The Vyla Component Matrix
## Design Principles
| Component | Platform / Tech Stack | Primary Responsibility | Documentation |
| :--- | :--- | :--- | :--- |
| **Vyla Home** | Tauri v2, Node.js Sidecar, Rust | Full desktop control center, automatic updates, Discord RPC | [Vyla Home Guide](/products/home) |
| **Vyla Go** | Tauri v2 Mobile, WebGL Shaders | Android mobile runtime, automated payload download, background playback | [Vyla Go Guide](/products/go) |
| **Vyla TV** | Kotlin, Jetpack Compose, ExoPlayer | 10-foot Leanback television UI, D-pad navigation, 4K/HDR streaming | [Vyla TV Guide](/products/tv) |
| **Stream API** | Node.js, Cluster Workers, SSE | Self-hosted HTTP streaming gateway, M3U8 proxy, segment stripper | [Stream API Overview](/stream-api/overview) |
| **Vyla SDK** | Node.js (ESM), TLS Client, Cheerio | Multi-provider video resolver, subtitle extractor, manga engine | [SDK Overview](/sdk) |
| **Custom Player** | HLS.js, Video.js, HTML5 | Custom web and native playback integration layer | [Build a Player](/products/player) |
- **No Telemetry**: No mandatory accounts, analytics, or tracking.
- **Automatic Failover**: Queries multiple providers with randomized jitter. If a provider fails, the player moves to the next candidate instantly.
- **Progressive Delivery**: Stream API emits metadata and video candidates over Server-Sent Events (SSE) so playback starts in 3 to 8 seconds.
- **Self-Hostable**: Run every service locally or deploy to your own VPS.
---
## Core Principles
## Quick Navigation
- **Zero Centralized Telemetry**: Vyla frontends never require mandatory tracking, user account creation, or proprietary telemetry services.
- **Failover by Design**: Video resolution queries multiple providers concurrently with randomized jitter. If one upstream host goes down or throttles, the client seamlessly shifts to the next viable candidate.
- **Progressive Delivery**: Stream API delivers media metadata instantly, followed by source candidates streamed over Server-Sent Events (SSE) as each scraper resolves. Players start playback within 3 to 8 seconds without waiting for the slowest source.
- **Local Sovereignty**: Every part of Vyla can be self-hosted locally on your laptop, home server, NAS, or virtual private server (VPS).
---
## Where to Begin
Select your path based on your role:
:::code-group
<!-- tab: End Users -->
- **On Desktop (Windows, macOS, Linux)?** Download and run [Vyla Home](/products/home).
- **On Android Phone or Tablet?** Install the native [Vyla Go APK](/products/go).
- **On Android TV, Google TV, or Fire TV?** Install the [Vyla TV APK](/products/tv).
- **Need help with setup?** Read the [Troubleshooting Guide](/resources/troubleshooting).
<!-- tab: Backend Developers -->
- **Embedding scrapers in a Node.js project?** Check out the [SDK Quickstart](/sdk/vyla-sdk/quickstart) and [Complete API Reference](/sdk/vyla-sdk/reference).
- **Want to author a new provider?** Follow the [Contributing a Source Guide](/sdk/vyla-sdk/contributing).
- **Integrating Manga discovery?** Read the [Manga SDK Documentation](/sdk/vyla-sdk/manga).
<!-- tab: Frontend & App Developers -->
- **Connecting a web or mobile player?** Read [Paths & Playback Events](/stream-api/reference/paths) and the [Client Integration Guide](/guides/integrations).
- **Building fallback strategies?** Study the [Error Handling Guide](/guides/error-handling).
- **Self-hosting the streaming gateway?** Read the [Self-Hosting Guide](/stream-api/deployment/self-hosting).
---
## Legal Notice & DMCA Compliance
Vyla is self-hosted software intended for personal educational and development use. All stream resolution and scraper logic interacts with publicly available web sources. Rights holders can review our takedown policies in the [DMCA Policy](/misc/project/dmca).
- **Desktop Users**: Get started with [Vyla Home](/products/home).
- **Mobile & TV**: Check out [Vyla Go](/products/go) and [Vyla TV](/products/tv).
- **Node.js Developers**: See the [SDK Quickstart](/sdk/vyla-sdk/quickstart) and [API Reference](/sdk/vyla-sdk/reference).
- **Self-Hosters**: Read the [Self-Hosting Guide](/stream-api/deployment/self-hosting).
- **Need Help?**: Visit the [FAQ](/resources/faq) or [Troubleshooting](/resources/troubleshooting).

View File

@@ -4,40 +4,29 @@
## Overview
Stream API includes a built-in cryptographic key management and rate-limiting system backed by SQLite. When enabled, client applications must supply an authenticated key to establish Server-Sent Events connections or request proxied media streams.
Stream API includes a key management and rate-limiting system backed by SQLite. When enabled, client apps must supply an authenticated key to establish SSE streams or request proxied media.
---
## Key Structure & Prefixes
## Key Tiers
API keys are 32-character hexadecimal strings generated with a specific prefix indicating the client tier:
| Key Prefix | Key Type | Intended Use Case | Default Rate Limit |
| :--- | :--- | :--- | :--- |
| **`pk_`** | **Partner Key** | Verified high-volume partner frontends and applications | 1,000 req / minute |
| **`sk_`** | **Standard Key** | Private self-hosters and internal client instances | 500 req / minute |
| **`pub_`** | **Public Key** | Anonymous public trial access or open community frontends | 10 req / minute |
- **`pk_` (Partner Key)**: For high-volume frontends. Default: 1,000 req/min.
- **`sk_` (Standard Key)**: For private self-hosters and internal client apps. Default: 500 req/min.
- **`pub_` (Public Key)**: For public trial access or open community frontends. Default: 10 req/min.
---
## Generating API Keys via CLI
## Creating Keys
The Stream API repository includes a CLI tool (`add-key.mjs`) to generate and insert keys into the active SQLite database:
Generate keys using the CLI tool in the Stream API directory:
```bash
# Syntax: node add-key.mjs <type> <rate_limit_per_min> <client_name>
# 1. Create a Standard Key for a personal desktop client:
node add-key.mjs standard 500 my-desktop-client
# 2. Create a Partner Key for a high-traffic community player:
node add-key.mjs partner 1000 partner-frontend
# 3. Create a restricted Public Key:
node add-key.mjs public 10 public-guest
```
### CLI Output Example
### Output Example
```
Generated Key: sk_8f14b2d39a7e4c5b10e9f8a7b6c5d4e3
Type: standard
@@ -45,19 +34,18 @@ Limit: 500 req/min
Client: my-desktop-client
Status: Active
```
The key is printed once upon creation. Copy and deliver it to your client application securely.
---
## Managing Keys via JSON Database Export
## Database Export & Sync
For automated deployments or bulk key management, you can export the database to a JSON file:
For automated deployments, export the database to JSON:
### 1. Export Database
```bash
node export-db.mjs
```
This dumps the active SQLite database tables into `db.json`:
This dumps active keys to `db.json`:
```json
{
"keys": [
@@ -73,41 +61,40 @@ This dumps the active SQLite database tables into `db.json`:
}
```
### 2. Modify and Re-Import
Edit `db.json` to add, revoke, or modify limits, then commit the changes back to SQLite:
To sync edits from `db.json` back into SQLite:
```bash
node update-db.mjs
```
---
## Client Authorization Headers
## Client Authorization
Clients can authenticate requests by passing the key in one of the following locations:
Clients can send their key via headers or query parameters:
### 1. Authorization Header (Recommended)
### Authorization Header
```http
GET /movie?id=155 HTTP/1.1
Host: your-api.example.com
Host: api.vyla.cc
Authorization: Bearer sk_8f14b2d39a7e4c5b10e9f8a7b6c5d4e3
```
### 2. Custom API Key Header
### Custom Header
```http
X-API-Key: sk_8f14b2d39a7e4c5b10e9f8a7b6c5d4e3
```
### 3. Query Parameter (for EventSource & HTML5 Video)
Because native browser `EventSource` and `<video>` tags do not support custom request headers, you can pass the key as a query parameter:
### Query Parameter
Used for browser `EventSource` and native `<video>` elements that do not support custom request headers:
```http
GET /movie?id=155&key=sk_8f14b2d39a7e4c5b10e9f8a7b6c5d4e3 HTTP/1.1
```
---
## Rate Limit Headers
## Rate Limit Responses
When requests are evaluated, Stream API returns standard rate-limiting headers:
Responses include standard rate-limiting headers:
```http
HTTP/1.1 200 OK
@@ -116,7 +103,7 @@ X-RateLimit-Remaining: 498
X-RateLimit-Reset: 1731614460
```
If a client exceeds their tier quota, Stream API responds with HTTP 429:
When a quota is exceeded, Stream API returns HTTP `429`:
```json
{
"error": "Rate limit exceeded. Please retry in 32 seconds.",

View File

@@ -1,41 +1,34 @@
# Self-Hosting Stream API
<p class="page-description">Complete production self-hosting guide for Stream API: Docker, Node.js systemd, PM2, and Nginx reverse proxy configuration with SSE streaming support.</p>
<p class="page-description">Production self-hosting guide for Stream API using Docker, systemd, and Nginx with SSE streaming support.</p>
## Overview
Stream API is designed to be completely self-hostable on any Linux virtual private server (VPS), home server, or Docker container environment. Running your own instance gives you private access, eliminates shared IP rate limits, and allows you to tune worker concurrency to your hardware.
Stream API can be self-hosted on any Linux VPS, home server, or container environment. Self-hosting eliminates shared IP rate limits and allows custom worker tuning.
---
## Environment Variables
## Configuration
Configure your environment variables via a `.env` file in the project root:
Configure environment variables via a `.env` file in the project root:
| Variable | Required | Default | Description |
| :--- | :--- | :--- | :--- |
| `PORT` | Optional | `7860` | HTTP port on which the server binds. |
| `TMDB_API_KEY` | **Recommended** | `""` | The Movie Database v3 API key. Required for scrapers that resolve metadata via IMDb ID or episode titles. |
| `WORKER_COUNT` | Optional | `os.cpus().length` | Number of worker processes to fork in cluster mode. |
| `ENABLE_DEBUG_ROUTE` | Optional | `false` | Enables deep diagnostic traces at `/debug/:source`. Keep `false` in production. |
| `GA_MEASUREMENT_ID` | Optional | `""` | Google Analytics 4 Measurement ID for private usage telemetry. |
| `GA_API_SECRET` | Optional | `""` | Google Analytics 4 Measurement Protocol API Secret. |
| `FALLBACK_BASE` | Optional | `""` | Secondary Stream API instance URL used for failover redirects. |
| `IS_HF` | Optional | `false` | Enables HuggingFace Spaces compatibility constraints. |
- `PORT` *(number, optional)* — Bind port. Default: `7860`.
- `TMDB_API_KEY` *(string, recommended)* — TMDB v3 API key for metadata lookups.
- `WORKER_COUNT` *(number, optional)* — Number of cluster workers. Default: available CPU count.
- `ENABLE_DEBUG_ROUTE` *(boolean, optional)* — Enables `/debug/:source`. Keep `false` in production.
- `FALLBACK_BASE` *(string, optional)* — Secondary Stream API URL for failover redirects.
- `IS_HF` *(boolean, optional)* — Enables HuggingFace Spaces compatibility.
---
## Deployment Option 1: Docker (Recommended)
### 1. Dockerfile
Create a `Dockerfile` in the project directory:
## Deployment Option 1: Docker
### Dockerfile
```dockerfile
FROM node:20-alpine
WORKDIR /app
# Install native dependencies required for TLS and encryption
RUN apk add --no-cache python3 make g++
COPY package*.json ./
@@ -49,9 +42,7 @@ EXPOSE 7860
CMD ["node", "server.js"]
```
### 2. Docker Compose
Create a `docker-compose.yml` file:
### Docker Compose
```yaml
version: '3.8'
@@ -76,28 +67,23 @@ services:
max-file: "3"
```
Start the container in detached mode:
Start the container:
```bash
docker compose up -d
```
---
## Deployment Option 2: Direct Node.js & Systemd
## Deployment Option 2: Systemd
### 1. Installation
### 1. Install
```bash
# Clone the repository
git clone https://gitlab.com/vyla-entertainment/stream-api.git /opt/stream-api
cd /opt/stream-api
# Install dependencies
npm ci --omit=dev
```
### 2. Create Systemd Service
Create `/etc/systemd/system/stream-api.service`:
### 2. Service File: `/etc/systemd/system/stream-api.service`
```ini
[Unit]
Description=Vyla Stream API Service
@@ -115,7 +101,6 @@ Environment=PORT=7860
Environment=TMDB_API_KEY=your_tmdb_api_key_here
Environment=WORKER_COUNT=4
# Security Hardening
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=full
@@ -124,7 +109,7 @@ ProtectSystem=full
WantedBy=multi-user.target
```
Enable and start the service:
Enable and start:
```bash
sudo systemctl daemon-reload
sudo systemctl enable --now stream-api
@@ -133,34 +118,30 @@ sudo systemctl status stream-api
---
## Reverse Proxy: Nginx Configuration
## Reverse Proxy: Nginx
Because Stream API utilizes **Server-Sent Events (SSE)** and large **HLS video streams**, your reverse proxy must disable proxy buffering. Standard buffering will delay SSE events until the buffer fills, breaking progressive playback.
Disable proxy buffering so Server-Sent Events (SSE) stream progressively to clients.
Create an Nginx configuration at `/etc/nginx/sites-available/stream-api.conf`:
Create `/etc/nginx/sites-available/stream-api.conf`:
```nginx
server {
server_name stream-api.yourdomain.com;
# Maximum upload size for DB syncs
client_max_body_size 20M;
location / {
proxy_pass http://127.0.0.1:7860;
proxy_http_version 1.1;
# Mandatory for Server-Sent Events (SSE)
proxy_set_header Connection '';
proxy_buffering off;
proxy_cache off;
chunked_transfer_encoding off;
# Keep-Alive & Timeouts for long media streams
proxy_read_timeout 90s;
proxy_send_timeout 90s;
# Forward Client Identifiers for Geo-Token Verification
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
@@ -171,24 +152,20 @@ server {
}
```
Enable the configuration and obtain a free SSL certificate via Certbot:
Enable and provision SSL:
```bash
sudo ln -s /etc/nginx/sites-available/stream-api.conf /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
# Install Let's Encrypt SSL
sudo certbot --nginx -d stream-api.yourdomain.com
```
---
## Verifying Your Deployment
## Health Check
Once deployed, verify the installation by testing the `/health` endpoint:
Verify the deployment:
```bash
curl -i https://stream-api.yourdomain.com/health
```
You should receive an HTTP 200 (or 207) with JSON detailing the cluster status, cache count, and source probe results.

View File

@@ -6,62 +6,53 @@
## Overview
**Stream API** is the dedicated backend gateway for the Vyla ecosystem. While the Vyla SDK executes directly in Node.js environments, web browsers, mobile applications, and smart TVs require a standard HTTP/SSE bridge to communicate with upstream providers, circumvent browser CORS restrictions, and decrypt video streams.
**Stream API** is the streaming gateway for the Vyla ecosystem. While the Vyla SDK executes directly in Node.js, browsers, mobile devices, and TVs require an HTTP/SSE bridge to communicate with scrapers, bypass browser CORS, and decrypt video streams.
Stream API wraps `@vyla-entertainment/sdk` in a high-concurrency, self-hostable service designed for maximum throughput and zero playback stutter.
Stream API wraps `@vyla-entertainment/sdk` in a high-concurrency service built for real-time progressive playback.
---
## Architectural Highlights
## Core Capabilities
| Cluster Role | Process Type | Responsibilities & Functions |
| :--- | :--- | :--- |
| **Primary Process** | Node.js Cluster Master | Spawns and supervises worker processes across CPU cores, manages the shared 1,500-entry in-memory LRU cache (5-minute TTL), coordinates IPC messages (`cache:get` / `cache:set`), and executes cache cleanup every 30 seconds. |
| **Inter-Process Channel** | Node.js IPC | High-throughput bi-directional message pipe (`process.send` and `cluster.on('message')`) synchronizing cache entries across workers without Redis or external database overhead. |
| **Worker Processes** | Forked HTTP Workers (`0..N`) | Runs native HTTP request routers (`http.createServer`), pushes progressive Server-Sent Events (SSE), executes universal reverse proxying with live M3U8/key rewriting, and strips 120-byte image obfuscation masks. |
### Multi-Core Cluster
Stream API uses Node's native `cluster` module to fork workers across all available CPU cores. If an upstream network timeout occurs on one worker, other workers continue handling client traffic without interruption.
### 1. Multi-Core Cluster Architecture
Stream API leverages Node's native `cluster` module to fork independent worker processes across available CPU cores. If one worker experiences a heavy upstream network timeout, other workers continue handling client requests without interruption.
### Progressive Server-Sent Events (SSE)
Instead of waiting 20 seconds for all 60 scrapers to finish:
- **Instant Metadata**: Title info and subtitles arrive in the first `meta` event.
- **Progressive Sources**: Each verified source is pushed immediately as a `source` event.
- **Clean Finish**: A `done` event signals stream completion.
### 2. Progressive Streaming over Server-Sent Events (SSE)
Rather than blocking HTTP requests for 20 seconds while 60 providers resolve, Stream API utilizes Server-Sent Events:
- **Instant Metadata**: Title info and subtitle tracks arrive in the first event (`type: "meta"`).
- **Progressive Sources**: As each provider scraper finishes verification, a `type: "source"` event is pushed immediately.
- **Completion Signal**: When all sources complete, a `type: "done"` event closes the stream cleanly.
### Shared In-Memory Cache
- **Capacity**: 1,500 entries with LRU eviction.
- **TTL**: 5 minutes (300,000 ms).
- **Lookup Latency**: < 2 ms for active titles.
### 3. Shared In-Memory LRU Cache
All cluster workers communicate with a centralized in-memory cache managed by the primary process:
- **Cache TTL**: 5 minutes (300,000 ms).
- **Capacity**: 1,500 items max with LRU eviction.
- **Cache Hit Latency**: < 2 milliseconds for frequently requested titles.
### 4. Reverse Proxy & Chunk De-Obfuscator
- Transparently proxies HLS playlists (`/api?url=...`), rewriting AES encryption keys and `.ts` chunk URIs to inject permissive CORS headers.
- Strips 120-byte fake image headers prepended to video chunks by ByteDance/TikTok-based content delivery networks.
- Supports HTTP Range headers (`206 Partial Content`) for seeking large MP4 and MKV media containers.
### Reverse Proxy & Chunk Stripper
- Proxies HLS playlists (`/api?url=...`), rewriting AES encryption keys and `.ts` chunk URIs to inject permissive CORS headers.
- Strips 120-byte fake image headers prepended to video chunks by ByteDance and TikTok-based CDNs.
- Supports HTTP Range requests (`206 Partial Content`) for seeking MP4 and MKV containers.
---
## Route Overview
## Key Routes
| Route | Protocol | Primary Purpose | Reference |
| :--- | :--- | :--- | :--- |
| **`/movie?id=:id`** | GET (SSE) | Progressive movie source resolution | [Paths Reference](/stream-api/reference/paths) |
| **`/tv?id=:id&season=:s&episode=:e`** | GET (SSE) | Progressive TV episode source resolution | [Paths Reference](/stream-api/reference/paths) |
| **`/subtitles/movie/:id`** | GET (REST) | Multi-language subtitle tracks for movies | [Media Data Reference](/stream-api/reference/media-data) |
| **`/subtitles/tv/:id/:s/:e`** | GET (REST) | Subtitle tracks for TV episodes | [Media Data Reference](/stream-api/reference/media-data) |
| **`/downloads/movie/:id`** | GET (REST) | Direct media download packages for movies | [Media Data Reference](/stream-api/reference/media-data) |
| **`/downloads/tv/:id/:s/:e`** | GET (REST) | Direct media download packages for TV | [Media Data Reference](/stream-api/reference/media-data) |
| **`/api?url=:url`** | GET (Proxy) | Universal HLS & video chunk proxy | [Paths Reference](/stream-api/reference/paths) |
| **`/health`** | GET (REST) | Cluster status, cache size, source health | [Operations Reference](/stream-api/reference/operations) |
| **`/test/:source`** | GET (REST) | Direct test harness for individual sources | [Operations Reference](/stream-api/reference/operations) |
- `GET /movie?id=:id` — Progressive movie source resolution via SSE. [Docs](/stream-api/reference/paths)
- `GET /tv?id=:id&season=:s&episode=:e` — Progressive TV episode source resolution via SSE. [Docs](/stream-api/reference/paths)
- `GET /api?url=:url` — Universal HLS and video chunk reverse proxy. [Docs](/stream-api/reference/paths)
- `GET /subtitles/movie/:id` — Multi-language subtitle tracks for movies. [Docs](/stream-api/reference/media-data)
- `GET /subtitles/tv/:id/:s/:e` — Subtitle tracks for TV episodes. [Docs](/stream-api/reference/media-data)
- `GET /downloads/movie/:id` — Direct media download packages for movies. [Docs](/stream-api/reference/media-data)
- `GET /downloads/tv/:id/:s/:e` — Direct media download packages for TV. [Docs](/stream-api/reference/media-data)
- `GET /health` — Cluster status, cache metrics, and scraper health. [Docs](/stream-api/reference/operations)
- `GET /test/:source` — Direct test harness for individual scrapers. [Docs](/stream-api/reference/operations)
---
## Stream API Documentation Index
## Documentation Sections
- **[Paths & Playback Events](/stream-api/reference/paths)**: Complete SSE payload contracts, parameters, and proxy options.
- **[Subtitles & Media Downloads](/stream-api/reference/media-data)**: REST endpoints for subtitle extraction and direct media files.
- **[Operations, Health & Testing](/stream-api/reference/operations)**: Cluster health monitoring, probe IDs, and debug routes.
- **[API Keys & Authentication](/stream-api/authentication)**: Key management, SQLite database, and partner keys.
- **[Production Self-Hosting Guide](/stream-api/deployment/self-hosting)**: Docker containerization, PM2, systemd, and Nginx reverse proxy configurations.
- **[Subtitles & Media Downloads](/stream-api/reference/media-data)**: REST endpoints for subtitle extraction and direct downloads.
- **[Operations & Health](/stream-api/reference/operations)**: Cluster monitoring, probe IDs, and debug routes.
- **[API Keys & Authentication](/stream-api/authentication)**: Key management, SQLite database, and rate limiting.
- **[Self-Hosting Guide](/stream-api/deployment/self-hosting)**: Docker containerization, PM2, and Nginx reverse proxy setup.

View File

@@ -4,25 +4,22 @@
## Subtitle Endpoints
Stream API provides dedicated REST endpoints to fetch subtitle tracks independently from video streams. Subtitles are discovered and parsed into web-standard `.vtt` or `.srt` formats.
Retrieve subtitle tracks independently from video streams in `.vtt` or `.srt` formats.
---
### 1. Movie Subtitles
### 1. Movie Subtitles: `GET /subtitles/movie/:id`
```http
GET /subtitles/movie/:id HTTP/1.1
Host: your-api.example.com
GET /subtitles/movie/155 HTTP/1.1
Host: api.vyla.cc
```
#### Path Parameters
| Parameter | Type | Required | Description |
| :--- | :--- | :--- | :--- |
| `id` | `integer` | Required | Numeric TMDB Movie ID (e.g. `155`) |
**Parameters:**
- `id` *(number, required)* — TMDB Movie ID (e.g. `155`).
#### Example Request & Response
```bash
curl https://your-api.example.com/subtitles/movie/155
curl https://api.vyla.cc/subtitles/movie/155
```
```json
@@ -30,21 +27,15 @@ curl https://your-api.example.com/subtitles/movie/155
"subtitles": [
{
"label": "English",
"file": "https://your-api.example.com/api?url=https%3A%2F%2Fsubtitles.org%2Fen.vtt",
"file": "https://api.vyla.cc/api?url=https%3A%2F%2Fsubtitles.org%2Fen.vtt",
"type": "vtt",
"source": "opensubtitles"
},
{
"label": "Spanish",
"file": "https://your-api.example.com/api?url=https%3A%2F%2Fsubtitles.org%2Fes.vtt",
"file": "https://api.vyla.cc/api?url=https%3A%2F%2Fsubtitles.org%2Fes.vtt",
"type": "vtt",
"source": "opensubtitles"
},
{
"label": "French",
"file": "https://your-api.example.com/api?url=https%3A%2F%2Fsubtitles.org%2Ffr.srt",
"type": "srt",
"source": "subdl"
}
]
}
@@ -52,41 +43,37 @@ curl https://your-api.example.com/subtitles/movie/155
---
### 2. Television Episode Subtitles
### 2. Television Episode Subtitles: `GET /subtitles/tv/:id/:season/:episode`
```http
GET /subtitles/tv/:id/:season/:episode HTTP/1.1
Host: your-api.example.com
GET /subtitles/tv/1396/1/1 HTTP/1.1
Host: api.vyla.cc
```
#### Path Parameters
| Parameter | Type | Required | Description |
| :--- | :--- | :--- | :--- |
| `id` | `integer` | Required | Numeric TMDB TV Series ID (e.g. `1396`) |
| `season` | `integer` | Required | Season number (e.g. `1`) |
| `episode` | `integer` | Required | Episode number (e.g. `1`) |
**Parameters:**
- `id` *(number, required)* — TMDB Series ID (e.g. `1396`).
- `season` *(number, required)* — Season number (e.g. `1`).
- `episode` *(number, required)* — Episode number (e.g. `1`).
#### Example Request
```bash
curl https://your-api.example.com/subtitles/tv/1396/1/1
curl https://api.vyla.cc/subtitles/tv/1396/1/1
```
---
## Download Endpoints
## Direct Download Endpoints
Stream API resolves high-speed direct media download packages and torrent/debrid links. Download lookups are separated from playback streams to allow offline caching or background download managers.
Fetch direct video files and torrent/debrid download packages.
---
### 1. Movie Downloads
### 1. Movie Downloads: `GET /downloads/movie/:id`
```http
GET /downloads/movie/:id HTTP/1.1
Host: your-api.example.com
GET /downloads/movie/155 HTTP/1.1
Host: api.vyla.cc
```
#### Example Response
```json
{
"downloads": [
@@ -95,14 +82,14 @@ Host: your-api.example.com
"size": 2450882194,
"type": "mkv",
"active": true,
"url": "https://fast-cdn.example.com/download/the-dark-knight-1080p.mkv"
"url": "https://cdn.example.com/download/the-dark-knight-1080p.mkv"
},
{
"quality": "720p",
"size": 1150428190,
"type": "mkv",
"active": true,
"url": "https://fast-cdn.example.com/download/the-dark-knight-720p.mkv"
"url": "https://cdn.example.com/download/the-dark-knight-720p.mkv"
}
]
}
@@ -110,14 +97,13 @@ Host: your-api.example.com
---
### 2. Television Episode Downloads
### 2. Television Episode Downloads: `GET /downloads/tv/:id/:season/:episode`
```http
GET /downloads/tv/:id/:season/:episode HTTP/1.1
Host: your-api.example.com
GET /downloads/tv/1396/1/1 HTTP/1.1
Host: api.vyla.cc
```
#### Example Response
```json
{
"downloads": [
@@ -126,7 +112,7 @@ Host: your-api.example.com
"size": 1420882194,
"type": "mkv",
"active": true,
"url": "https://fast-cdn.example.com/download/breaking-bad-s01e01-1080p.mkv"
"url": "https://cdn.example.com/download/breaking-bad-s01e01-1080p.mkv"
}
]
}
@@ -134,8 +120,8 @@ Host: your-api.example.com
---
## Best Practices for Clients
## Client Integration Notes
1. **Do Not Block Initial Playback**: Initiate subtitle and download queries asynchronously. The video player should start playback immediately on the first candidate stream, and subtitles should be attached dynamically as they arrive.
2. **CORS Handling**: Subtitle files proxied through `/api?url=...` are served with `Access-Control-Allow-Origin: *`, ensuring HTML5 `<track>` elements load cleanly in cross-origin browser contexts.
3. **Format Support**: While most modern players prefer `.vtt`, some tracks are returned in `.srt`. If your player strictly requires WebVTT, convert SRT timestamps (`00:01:20,500` -> `00:01:20.500`) client-side before loading.
- **Non-blocking Loading**: Fetch subtitles in the background while video playback begins immediately on the first candidate stream.
- **CORS Permissive**: Subtitle tracks proxied through `/api?url=...` include `Access-Control-Allow-Origin: *`, allowing HTML5 `<track>` elements to render in any browser context.
- **Format Conversion**: If your web player requires WebVTT, convert SRT commas to dots (`00:01:20,500` -> `00:01:20.500`) client-side before loading.

View File

@@ -2,23 +2,22 @@
<p class="page-description">Monitoring, cluster diagnostics, live health probing, and testing endpoints in Stream API.</p>
## The Health Check Endpoint: `GET /health`
## Health Check: `GET /health`
Stream API provides an automated health probe endpoint that executes live responsiveness checks across all registered scraper modules.
Runs responsiveness checks across registered scraper modules.
```http
GET /health HTTP/1.1
Host: your-api.example.com
Host: api.vyla.cc
```
### Response Status Codes
- **`200 OK`**: All active provider sources passed their health probes.
- **`207 Multi-Status`**: One or more providers failed their probes (degraded state), but the API gateway remains functional.
### Status Codes
- `200 OK`: All active scrapers passed their health probes.
- `207 Multi-Status`: One or more providers failed (degraded state), but the gateway is functional.
### Example Response Payload
### Response Payload
```json
{
"note": "This is a health check endpoint. This is extremely unreliable, be sure to test sources directly.",
"status": "ok",
"timestamp": "2026-11-14T20:30:00.000Z",
"tmdb": true,
@@ -33,33 +32,31 @@ Host: your-api.example.com
}
```
#### Response Fields
| Field | Type | Description |
| :--- | :--- | :--- |
| `status` | `string` | Overall system state (`"ok"` or `"degraded"`). |
| `timestamp` | `string` | ISO 8601 timestamp of probe completion. |
| `tmdb` | `boolean` | `true` if `TMDB_API_KEY` is configured and valid in the environment. |
| `cache` | `integer` | Current number of active cached titles in the primary LRU cache. |
| `probe_id` | `integer` | The standard TMDB ID used to probe source responsiveness. |
| `sources` | `object` | Dictionary of per-source availability (`ok`) and roundtrip latency in milliseconds (`ms`). |
**Key Fields:**
- `status`: Overall state (`"ok"` or `"degraded"`).
- `timestamp`: ISO 8601 probe timestamp.
- `tmdb`: True if `TMDB_API_KEY` is configured and responding.
- `cache`: Current number of active cached titles in LRU memory.
- `probe_id`: The TMDB ID used for the probe query.
- `sources`: Dictionary of per-source availability (`ok`) and roundtrip latency in milliseconds (`ms`).
---
## Direct Source Testing: `GET /test/:source`
To test an individual scraper directly without querying all 60 providers:
Tests a single scraper directly without triggering all other providers:
```http
GET /test/vidlink?id=155 HTTP/1.1
```
For television episodes, pass season and episode query parameters:
For television episodes, include season and episode:
```http
GET /test/vixsrc?id=1396&s=1&e=1 HTTP/1.1
```
### Response
Returns the raw resolved candidate object directly from the provider scraper:
Returns the raw candidate stream resolved by that scraper:
```json
{
@@ -75,33 +72,28 @@ Returns the raw resolved candidate object directly from the provider scraper:
## Deep Debug Route: `GET /debug/:source`
When `ENABLE_DEBUG_ROUTE=true` is set in your environment variables, Stream API exposes a comprehensive diagnostic route:
Exposes diagnostic internals when `ENABLE_DEBUG_ROUTE=true` is set:
```http
GET /debug/vidlink?id=155 HTTP/1.1
```
### Diagnostic Output Includes:
1. **Raw Scraper Payload**: Unmodified JSON or HTML received from the upstream host.
2. **Extraction Timings**: Milliseconds spent parsing DOM, solving tokens, and resolving URLs.
3. **Phase 1 Verification**: Status code, response headers, and latency of the `HEAD` request.
4. **Phase 2 Verification**: First 500 bytes of the master M3U8 playlist and test chunk fetch status.
5. **Rewritten URLs**: Preview of how chunk URIs will be rewritten by the proxy.
**Output details:**
- Raw HTML or JSON received from upstream hosts.
- Time spent solving tokens and parsing DOM.
- Status code and headers from Phase 1 verification (`HEAD` check).
- First 500 bytes of the master M3U8 manifest.
- Preview of rewritten segment URLs.
:::callout-warning
**Security Note**: Keep `ENABLE_DEBUG_ROUTE=false` in public production environments to avoid leaking internal scraper tokens or upstream endpoints.
Keep `ENABLE_DEBUG_ROUTE=false` in production to prevent leaking upstream tokens or credentials.
:::
---
## Cluster & Cache Architecture
### Primary Supervisor & Worker Pool Architecture
- **Central In-Memory Cache**: Holds up to 1,500 verified title entries in a shared LRU table with a 5-minute TTL.
- **Periodic Pruner**: Runs every 30 seconds to clean expired keys and enforce memory ceilings.
- **Worker Lifecycle Supervisor**: Spawns and supervises $N$ worker processes matching CPU core count, automatically restarting terminated workers.
- **IPC Message Dispatcher**: Routes `cache:get` and `cache:set` messages between worker threads and the primary process.
1. **IPC Cache Synchronization**: When a worker resolves a title, it sends a `cache:set` message to the primary process. When another worker receives a request for the same title, it sends a `cache:get` message and receives an instant `cache:hit` without re-scraping upstream hosts.
2. **Cache Pruning**: The primary process evicts stale items older than 300,000ms (5 minutes). If total cache size exceeds 1,500 items, the oldest entries are evicted immediately.
3. **Worker Auto-Restart**: If a worker process exits unexpectedly or exceeds memory limits, the primary cluster supervisor automatically spawns a replacement worker.
- **In-Memory Cache**: Up to 1,500 verified titles stored in an LRU cache with a 5-minute TTL.
- **Cache Pruner**: Runs every 30 seconds to evict expired keys.
- **Worker Lifecycle**: The supervisor process maintains workers matching CPU core count, automatically replacing crashed processes.
- **IPC Messaging**: Workers coordinate via `cache:get` and `cache:set` messages, allowing instant cache hits across worker boundaries without an external database.

View File

@@ -1,55 +1,51 @@
# Paths & Playback Events
<p class="page-description">Detailed API reference for Stream API streaming endpoints, Server-Sent Events contracts, and proxy query parameters.</p>
<p class="page-description">API reference for Stream API endpoints, Server-Sent Events contracts, and proxy query parameters.</p>
## Progressive Playback Endpoints
Stream API uses Server-Sent Events (SSE) to deliver real-time discovery events directly to player clients. This eliminates long HTTP timeouts and allows players to begin buffering the moment the first candidate resolves.
Stream API uses Server-Sent Events (SSE) so players can start buffering as soon as the first candidate source is verified.
---
### 1. Movie Stream Endpoint: `GET /movie` (or `/api/movie`)
### 1. Movie Stream: `GET /movie`
Establishes an SSE event stream for a movie.
Establishes an SSE stream for a movie.
```http
GET /movie?id=155 HTTP/1.1
Host: your-api.example.com
Host: api.vyla.cc
Accept: text/event-stream
```
#### Query Parameters
| Parameter | Type | Required | Description | Example |
| :--- | :--- | :--- | :--- | :--- |
| `id` | `integer` | Required | Numeric TMDB Movie ID | `155` (The Dark Knight) |
**Parameters:**
- `id` *(number, required)* — Numeric TMDB Movie ID (e.g. `155` for The Dark Knight).
---
### 2. Television Stream Endpoint: `GET /tv` (or `/api/tv`)
### 2. TV Series Stream: `GET /tv`
Establishes an SSE event stream for a television series episode.
Establishes an SSE stream for a television series episode.
```http
GET /tv?id=1396&season=1&episode=1 HTTP/1.1
Host: your-api.example.com
Host: api.vyla.cc
Accept: text/event-stream
```
#### Query Parameters
| Parameter | Type | Required | Description | Example |
| :--- | :--- | :--- | :--- | :--- |
| `id` | `integer` | Required | Numeric TMDB TV Series ID | `1396` (Breaking Bad) |
| `season` | `integer` | Required | Season number | `1` |
| `episode` | `integer` | Required | Episode number | `1` |
**Parameters:**
- `id` *(number, required)* — Numeric TMDB TV Series ID (e.g. `1396` for Breaking Bad).
- `season` *(number, required)* — Season number (e.g. `1`).
- `episode` *(number, required)* — Episode number (e.g. `1`).
---
## Server-Sent Events (SSE) Contract
## SSE Event Lifecycle
Connections to `/movie` and `/tv` return `Content-Type: text/event-stream` and emit events sequentially:
Connections return `Content-Type: text/event-stream` and emit events in sequence:
### Event 1: `meta` (Emitted Immediately)
Delivers title metadata and parsed subtitle tracks while scrapers are still running.
### Event 1: `meta`
Sent immediately with title metadata and subtitle tracks while scrapers are still running.
```json
data: {
@@ -59,13 +55,13 @@ data: {
"year": 2008,
"tmdb_id": 155,
"imdb_id": "tt0468569",
"poster": "https://image.tmdb.org/t/p/w500/...",
"backdrop": "https://image.tmdb.org/t/p/original/..."
"poster": "https://image.tmdb.org/t/p/w500/sample.jpg",
"backdrop": "https://image.tmdb.org/t/p/original/sample.jpg"
},
"subtitles": [
{
"label": "English",
"file": "https://your-api.example.com/api?url=...",
"file": "https://api.vyla.cc/api?url=https%3A%2F%2Fsubtitles.org%2Fen.vtt",
"type": "vtt",
"source": "opensubtitles"
}
@@ -73,15 +69,15 @@ data: {
}
```
### Event 2..N: `source` (Emitted Progressively)
Emitted as each provider scraper successfully resolves and passes two-phase verification.
### Event 2..N: `source`
Emitted progressively as each provider scraper completes two-phase verification.
```json
data: {
"type": "source",
"source": "vidlink",
"label": "VidLink Primary",
"url": "https://your-api.example.com/api?url=https%3A%2F%2Fupstream.cdn%2Fmaster.m3u8&vl=1",
"url": "https://api.vyla.cc/api?url=https%3A%2F%2Fupstream.cdn%2Fmaster.m3u8&vl=1",
"server": "vidlink-cf",
"quality": "1080p",
"type": "hls",
@@ -92,8 +88,8 @@ data: {
}
```
### Event Final: `done` (Stream Closes)
Emitted when all enabled scrapers have finished resolving or reached their maximum timeout. The server closes the SSE connection immediately after.
### Event Final: `done`
Emitted when all enabled scrapers have finished or timed out. The server closes the SSE connection immediately after.
```json
data: {
@@ -104,34 +100,30 @@ data: {
---
## The Universal Reverse Proxy: `GET /api`
## Media Reverse Proxy: `GET /api`
The `/api` route handles media proxying, M3U8 rewriting, segment stripping, and TMDB queries.
### 1. Media Proxying: `/api?url=:encoded_url`
The `/api` route handles media proxying, M3U8 playlist rewriting, header spoofing, and byte stripping.
```http
GET /api?url=https%3A%2F%2Fcdn.example.com%2Fvideo.m3u8&proxyHeaders=%7B%22Referer%22%3A%22https%3A%2F%2Fsite.com%22%7D HTTP/1.1
GET /api?url=https%3A%2F%2Fcdn.example.com%2Fvideo.m3u8&vl=1 HTTP/1.1
```
#### Query Parameters
| Parameter | Type | Required | Description |
| :--- | :--- | :--- | :--- |
| `url` (or `proxy`) | `string` | Required | URL-encoded destination target. |
| `proxyHeaders` | `string` | Optional | URL-encoded JSON dictionary of custom headers to forward upstream. |
| `<source_flag>` | `string` | Optional | Provider flag matching `proxyParam` (e.g. `&vl=1`, `&vx=1`) to apply source-specific referer rules. |
| `tt` | `string` | Optional | Forces the 120-byte image de-masking routine on MPEG-TS video chunks. |
**Parameters:**
- `url` *(string, required)* — URL-encoded target URL to fetch.
- `proxyHeaders` *(string, optional)* — URL-encoded JSON object of custom headers to forward.
- `<source_flag>` *(string, optional)* — Source flag matching configuration (e.g. `&vl=1`, `&vx=1`) to apply source-specific referer rules.
- `tt` *(string, optional)* — Forces the 120-byte image header stripping routine on MPEG-TS video chunks.
#### Proxy Behaviors
- **M3U8 Playlists**: Rewrites all `.ts` chunk lines and AES `#EXT-X-KEY` URIs so that player requests route through the proxy with permissive CORS headers.
- **Obfuscated Chunks**: If chunks begin with PNG (`0x89`), JPEG (`0xFF`), or null (`0x00`) fake headers, Stream API automatically slices off the leading 120 bytes.
- **Range Seeking**: Forwards `Range: bytes=start-end` headers to upstream servers and returns HTTP `206 Partial Content`, enabling smooth scrubbing in MP4/MKV media.
**Proxy Features:**
- **M3U8 Rewriting**: Rewrites all `.ts` chunk URIs and `#EXT-X-KEY` AES encryption endpoints to route through the proxy with CORS headers (`Access-Control-Allow-Origin: *`).
- **Obfuscation Stripping**: Slices off the 120-byte image header if chunks start with PNG (`0x89`), JPEG (`0xFF`), or null (`0x00`) bytes.
- **Range Seeking**: Forwards `Range: bytes=start-end` headers and returns HTTP `206 Partial Content` for video seeking.
---
### 2. Active Sources Listing: `/api?sources_meta=1`
## Active Sources Listing: `GET /api?sources_meta=1`
Returns the list of active streaming sources currently loaded into the instance:
Returns all provider scrapers currently loaded into the instance:
```json
{
@@ -145,12 +137,10 @@ Returns the list of active streaming sources currently loaded into the instance:
---
## HTTP Status Codes & Error Responses
## HTTP Status Codes
| Status Code | Description | Example Payload |
| :--- | :--- | :--- |
| **`200 OK`** | Request succeeded or SSE stream opened | `text/event-stream` or JSON payload |
| **`206 Partial Content`** | Byte-range media response for video scrubbing | Binary video stream chunk |
| **`400 Bad Request`** | Missing required parameters or invalid TMDB ID | `{"error": "missing id", "route": "/movie?id=:tmdb_id"}` |
| **`404 Not Found`** | Unknown route | `{"error": "not found"}` |
| **`502 Bad Gateway`** | Upstream provider connection failed or timed out | `{"error": "upstream connection timeout"}` |
- `200 OK` — Request succeeded or SSE stream opened.
- `206 Partial Content` — Byte-range media response for video scrubbing.
- `400 Bad Request` — Missing parameters or invalid ID (`{"error": "missing id"}`).
- `404 Not Found` — Unknown route (`{"error": "not found"}`).
- `502 Bad Gateway` — Upstream provider connection failed or timed out.