huge doc rewrite

This commit is contained in:
2026-10-07 20:03:38 -07:00
parent d6ec1f93b8
commit c28308c389
47 changed files with 3185 additions and 6304 deletions

View File

@@ -0,0 +1,121 @@
# Self-host Stream API
<p class="page-description">Deploy the optional Stream API when your clients need an HTTP/SSE layer.</p>
## Overview
Stream API is a **self-hosted Node.js** application that you run on infrastructure you control. It does not provide a public hosted instance. The service runs as a Node.js process, uses in-memory caching, and can fork workers through Node's built-in `cluster` module. No database is required by the service itself.
:::callout-warning
Stream API is self-hosted only. There is no public Vyla streaming endpoint. You only need it when your architecture requires an HTTP/SSE layer; Node.js applications can use [Vyla SDK](/sdk) directly.
:::
## Requirements
- Node.js 18 or later
- A TMDB API key if you want title validation and metadata lookups
## Environment variables
Create a `.env` file in the project root:
```bash
TMDB_API_KEY=
PORT=7860
WORKER_COUNT=1
ENABLE_DEBUG_ROUTE=false
PROXY_STREAMS=false
PROXY_URL=
GA_MEASUREMENT_ID=
GA_API_SECRET=
```
<div class="card">
<div class="card-title">Variable reference</div>
<div class="card-body">
| Variable | Required | Description |
| --- | --- | --- |
| `TMDB_API_KEY` | Yes | Enables TMDB-based ID validation, metadata lookups, and anime detection. Some sources will not work without it. |
| `GA_MEASUREMENT_ID` / `GA_API_SECRET` | No | Enables Google Analytics event reporting. Leave blank to disable. |
| `PORT` | No | Port the server listens on. Defaults to `7860`. |
| `WORKER_COUNT` | No | Number of cluster workers to fork. Defaults to `1` locally. |
| `ENABLE_DEBUG_ROUTE` | No | Set to `true` to enable `/api/debug/:id`. Keep this `false` in production — it exposes upstream request/response headers and raw source output. |
| `PROXY_STREAMS` | No | Set to `true` to route stream playback through your own server's `/api` proxy endpoint instead of direct URLs. |
| `PROXY_URL` | No | An external proxy base URL to use instead of the built-in `/api` proxy. Only used if `PROXY_STREAMS` is enabled. |
</div>
</div>
## Installing and running
```bash
git clone https://gitlab.com/vyla-entertainment/stream-api
cd stream-api
npm install
node server.js
```
The server listens on:
```
http://localhost:7860
```
Visit `/health` to confirm it is running and check which sources are currently reachable from your host.
## Running with multiple workers
Setting `WORKER_COUNT` above `1` forks additional worker processes via `cluster`.
The primary process holds a shared in-memory cache (capped at 1500 entries, pruned every 30 seconds) and relays cache reads/writes between workers over IPC, allowing cached stream results to be shared rather than duplicated.
```bash
WORKER_COUNT=4
node server.js
```
Each worker independently loads source modules and handles outbound requests. Only shared cache operations and concurrency limits are coordinated through the primary process.
## Network considerations
Several sources are documented in `config.js` as blocked on datacenter IPs. This commonly affects cloud hosting providers and VPS environments.
If a source's `getStream` consistently returns nothing from your server but works from a residential connection, the cause is usually upstream blocking based on IP reputation. For the best experience, run Vyla API on a residential connection or use a residential proxy.
When running on a local desktop environment, you should have minimal issues with source availability since you're using a residential IP address.
## Reverse proxy / TLS
The server runs over plain HTTP. For public deployments, place it behind a reverse proxy such as Caddy, Nginx, or Cloudflare Tunnel to terminate TLS and forward requests to the configured `PORT`.
CORS is already configured at the application layer with:
```
Access-Control-Allow-Origin: *
```
No additional CORS configuration is required on the proxy.
## Verifying your deployment
Run these checks in order:
1. `GET /health`
Confirms the process is running and reports per-source reachability from your host.
2. `GET /api/test/155?source=vidrock`
Runs a real source resolution using a known TMDB movie ID and returns whether the source succeeded.
3. `GET /api/debug/155?source=vidrock`
Only available when `ENABLE_DEBUG_ROUTE=true`.
Returns request traces, headers, and raw candidate URLs for debugging source failures.
:::callout-warning
Leave `ENABLE_DEBUG_ROUTE` disabled on public deployments. The debug route exposes upstream request information and raw stream URLs, which can leak internal details.
:::

View File

@@ -1,199 +0,0 @@
<!doctype html>
<html lang="en" class="dark">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Subtitles and downloads — Vyla</title>
<meta name="description" content="Unified media-data paths for movies and TV episodes." />
<meta name="keywords"
content="subtitles, downloads, media data, subtitle tracks, download links, VTT, SRT, subtitle API" />
<link rel="canonical" href="https://vyla.cc/stream-api/media-data.html" />
<!-- Open Graph / Facebook -->
<meta property="og:type" content="website" />
<meta property="og:url" content="https://vyla.cc/stream-api/media-data.html" />
<meta property="og:title" content="Subtitles and downloads — Vyla" />
<meta property="og:description" content="Unified media-data paths for movies and TV episodes." />
<meta property="og:image" content="https://vyla.cc/assets/images/thumbnail.png" />
<meta property="og:site_name" content="Vyla" />
<!-- Twitter -->
<meta property="twitter:card" content="summary_large_image" />
<meta property="twitter:url" content="https://vyla.cc/stream-api/media-data.html" />
<meta property="twitter:title" content="Subtitles and downloads — Vyla" />
<meta property="twitter:description" content="Unified media-data paths for movies and TV episodes." />
<meta property="twitter:image" content="https://vyla.cc/assets/images/thumbnail.png" />
<meta property="twitter:site" content="@vylaentertainment" />
<!-- Additional SEO -->
<meta name="robots" content="index, follow" />
<meta name="author" content="Vyla" />
<link rel="icon" href="/favicon.ico" />
<link rel="stylesheet" href="/assets/base.css" />
<link rel="stylesheet" href="/assets/highlight.css" />
<link rel="stylesheet" href="/assets/mintlify-overrides.css" />
<!-- JSON-LD Structured Data -->
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "SoftwareApplication",
"name": "Subtitles and downloads",
"description": "Unified media-data paths for movies and TV episodes.",
"url": "https://vyla.cc/stream-api/media-data.html",
"applicationCategory": "MultimediaApplication",
"operatingSystem": "Windows, macOS, Linux, Android, Android TV",
"offers": {
"@type": "Offer",
"price": "0",
"priceCurrency": "USD"
}
}
</script>
</head>
<body>
<div id="banner">
<span>Vyla is self-hosted. Run Vyla Home, embed the SDK, or deploy Stream API on infrastructure you control. Rights
holders can request source removal — <a href="/misc/dmca">DMCA policy</a>.</span>
<button id="banner-close" onclick="dismissBanner()" aria-label="Dismiss">✕</button>
</div>
<header id="navbar">
<div class="nav-top">
<a class="brand" href="/"><img src="/assets/images/logo.png" alt="Vyla" id="brand-logo" loading="lazy"
fetchpriority="low"></a>
<button id="search-trigger" type="button" onclick="openSearch()" aria-label="Search"></button>
<div class="links">
<button id="theme-toggle" type="button" onclick="toggleThemeMenu(event)" aria-label="Theme"></button>
</div>
</div>
<nav class="tabs">
<a class="tab-link" href="/">Vyla</a>
<a class="tab-link" href="/sdk">SDK</a>
<a class="tab-link active" href="/stream-api/overview">Stream API</a>
<a class="tab-link" href="/guides/integrations">Resources</a>
<a class="tab-link" href="/misc/credit">Misc</a>
</nav>
</header>
<div id="layout">
<nav id="sidebar">
<div class="group">
<p class="group-title">Overview</p>
<ul>
<li><a class="" href="/stream-api/overview">Overview</a></li>
</ul>
</div>
<div class="group">
<p class="group-title">Reference</p>
<ul>
<li><a class="" href="/stream-api/paths">Paths</a></li>
<li><a class="active" href="/stream-api/media-data">Media Data</a></li>
<li><a class="" href="/stream-api/operations">Operations</a></li>
</ul>
</div>
<div class="group">
<p class="group-title">Deployment</p>
<ul>
<li><a class="" href="/self-hosting">Self Hosting</a></li>
</ul>
</div>
</nav>
<div id="content-col">
<main class="prose">
<h1>Subtitles and downloads</h1>
<p class="page-description">Unified media-data paths for movies and TV episodes.</p>
<h2 id="subtitle-paths">Subtitle paths</h2>
<table>
<thead>
<tr>
<th>Media</th>
<th>Path</th>
</tr>
</thead>
<tbody>
<tr>
<td>Movie</td>
<td><code>GET /subtitles/movie/:id</code></td>
</tr>
<tr>
<td>TV episode</td>
<td><code>GET /subtitles/tv/:id/:season/:episode</code></td>
</tr>
</tbody>
</table>
<p>The same routes are available under <code>/api/subtitles</code>. A successful response is a JSON array of
tracks; a missing result returns <code>404</code> with an error object.</p>
<pre><code class="hljs language-json"><span class="hljs-punctuation">[</span>
<span class="hljs-punctuation">{</span>
<span class="hljs-attr">"label"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"English"</span><span class="hljs-punctuation">,</span>
<span class="hljs-attr">"file"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"https://example.invalid/subtitles/en.vtt"</span><span class="hljs-punctuation">,</span>
<span class="hljs-attr">"type"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"vtt"</span><span class="hljs-punctuation">,</span>
<span class="hljs-attr">"source"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"v1"</span>
<span class="hljs-punctuation">}</span>
<span class="hljs-punctuation">]</span>
</code></pre>
<p><code>file</code> is the track URL, <code>type</code> is normally <code>vtt</code> or <code>srt</code>, and
<code>source</code> identifies the subtitle backend. Prefer the tracks included in the playback
<code>meta</code> event when you are already opening a playback stream; use these paths when subtitles are
fetched separately.
</p>
<h2 id="download-paths">Download paths</h2>
<table>
<thead>
<tr>
<th>Media</th>
<th>Path</th>
</tr>
</thead>
<tbody>
<tr>
<td>Movie</td>
<td><code>GET /downloads/movie/:id</code></td>
</tr>
<tr>
<td>TV episode</td>
<td><code>GET /downloads/tv/:id/:season/:episode</code></td>
</tr>
</tbody>
</table>
<p>The <code>/api/downloads</code> prefix is also supported. A successful response is an array; an empty array
simply means no candidates were found.</p>
<pre><code class="hljs language-json"><span class="hljs-punctuation">[</span>
<span class="hljs-punctuation">{</span>
<span class="hljs-attr">"url"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"https://example.invalid/file.mp4"</span><span class="hljs-punctuation">,</span>
<span class="hljs-attr">"quality"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"1080p"</span><span class="hljs-punctuation">,</span>
<span class="hljs-attr">"size"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"2.14 GB"</span><span class="hljs-punctuation">,</span>
<span class="hljs-attr">"format"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"MP4"</span>
<span class="hljs-punctuation">}</span>
<span class="hljs-punctuation">]</span>
</code></pre>
<p>Download URLs are provider results, not durable Vyla-hosted files. Display quality and size as informational
fields, and handle expired or unavailable links in your client.</p>
<h2 id="request-and-response-rules">Request and response rules</h2>
<ul>
<li>IDs are TMDB IDs; TV paths additionally require season and episode numbers.</li>
<li>Both families return JSON, unlike the SSE playback paths.</li>
<li>Handle <code>404</code> for missing subtitles and <code>500</code> for an upstream or resolver failure.
</li>
<li>Do not block first playback on either call. Fetch them in parallel or after the player has started.</li>
</ul>
</main>
<aside id="toc">
<p class="toc-title">On this page</p>
<ul>
<li><a href="#subtitle-paths">Subtitle paths</a></li>
<li><a href="#download-paths">Download paths</a></li>
<li><a href="#request-and-response-rules">Request and response rules</a></li>
</ul>
</aside>
</div>
</div>
<footer id="site-footer"><a href="https://discord.gg/5eqRhKeywG" target="_blank" rel="noopener">Discord</a></footer>
<script src="/assets/app.js"></script>
</body>
</html>

View File

@@ -1,167 +0,0 @@
<!doctype html>
<html lang="en" class="dark">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Health, testing, and safe operations — Vyla</title>
<meta name="description"
content="Observe SDK providers and diagnose a Stream API deployment without making debug data public." />
<meta name="keywords"
content="health check, API testing, operations, debugging, provider health, API monitoring, deployment" />
<link rel="canonical" href="https://vyla.cc/stream-api/operations.html" />
<!-- Open Graph / Facebook -->
<meta property="og:type" content="website" />
<meta property="og:url" content="https://vyla.cc/stream-api/operations.html" />
<meta property="og:title" content="Health, testing, and safe operations — Vyla" />
<meta property="og:description"
content="Observe SDK providers and diagnose a Stream API deployment without making debug data public." />
<meta property="og:image" content="https://vyla.cc/assets/images/thumbnail.png" />
<meta property="og:site_name" content="Vyla" />
<!-- Twitter -->
<meta property="twitter:card" content="summary_large_image" />
<meta property="twitter:url" content="https://vyla.cc/stream-api/operations.html" />
<meta property="twitter:title" content="Health, testing, and safe operations — Vyla" />
<meta property="twitter:description"
content="Observe SDK providers and diagnose a Stream API deployment without making debug data public." />
<meta property="twitter:image" content="https://vyla.cc/assets/images/thumbnail.png" />
<meta property="twitter:site" content="@vylaentertainment" />
<!-- Additional SEO -->
<meta name="robots" content="index, follow" />
<meta name="author" content="Vyla" />
<link rel="icon" href="/favicon.ico" />
<link rel="stylesheet" href="/assets/base.css" />
<link rel="stylesheet" href="/assets/highlight.css" />
<link rel="stylesheet" href="/assets/mintlify-overrides.css" />
<!-- JSON-LD Structured Data -->
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "SoftwareApplication",
"name": "Health, testing, and safe operations",
"description": "Observe SDK providers and diagnose a Stream API deployment without making debug data public.",
"url": "https://vyla.cc/stream-api/operations.html",
"applicationCategory": "MultimediaApplication",
"operatingSystem": "Windows, macOS, Linux, Android, Android TV",
"offers": {
"@type": "Offer",
"price": "0",
"priceCurrency": "USD"
}
}
</script>
</head>
<body>
<div id="banner">
<span>Vyla is self-hosted. Run Vyla Home, embed the SDK, or deploy Stream API on infrastructure you control. Rights
holders can request source removal — <a href="/misc/dmca">DMCA policy</a>.</span>
<button id="banner-close" onclick="dismissBanner()" aria-label="Dismiss">✕</button>
</div>
<header id="navbar">
<div class="nav-top">
<a class="brand" href="/"><img src="/assets/images/logo.png" alt="Vyla" id="brand-logo" loading="lazy"
fetchpriority="low"></a>
<button id="search-trigger" type="button" onclick="openSearch()" aria-label="Search"></button>
<div class="links">
<button id="theme-toggle" type="button" onclick="toggleThemeMenu(event)" aria-label="Theme"></button>
</div>
</div>
<nav class="tabs">
<a class="tab-link" href="/">Vyla</a>
<a class="tab-link" href="/sdk">SDK</a>
<a class="tab-link active" href="/stream-api/overview">Stream API</a>
<a class="tab-link" href="/guides/integrations">Resources</a>
<a class="tab-link" href="/misc/credit">Misc</a>
</nav>
</header>
<div id="layout">
<nav id="sidebar">
<div class="group">
<p class="group-title">Overview</p>
<ul>
<li><a class="" href="/stream-api/overview">Overview</a></li>
</ul>
</div>
<div class="group">
<p class="group-title">Reference</p>
<ul>
<li><a class="" href="/stream-api/paths">Paths</a></li>
<li><a class="" href="/stream-api/media-data">Media Data</a></li>
<li><a class="active" href="/stream-api/operations">Operations</a></li>
</ul>
</div>
<div class="group">
<p class="group-title">Deployment</p>
<ul>
<li><a class="" href="/self-hosting">Self Hosting</a></li>
</ul>
</div>
</nav>
<div id="content-col">
<main class="prose">
<h1>Health, testing, and safe operations</h1>
<p class="page-description">Observe SDK providers and diagnose a Stream API deployment without making debug data
public.</p>
<h2 id="health">Health</h2>
<p><code>GET /health</code> (or <code>/api/health</code>) probes active providers and returns a JSON summary. It
includes a status, timestamp, TMDB-key presence, cache size, and a map of provider keys to
<code>{ ok, ms }</code> results.
</p>
<p>Health is a signal, not a promise of playback. Provider behavior can differ by title, client network, and the
moment of the request. Use it for observability and source selection—not as a guarantee that a viewer will
receive a stream.</p>
<h2 id="test-one-provider">Test one provider</h2>
<p><code>GET /test/:id?source=:providerKey</code> resolves a single provider for a movie. Add
<code>season</code> and <code>episode</code> (or <code>s</code> and <code>e</code>) for TV.
</p>
<pre><code class="hljs language-bash">curl <span class="hljs-string">"http://localhost:7860/test/155?source=provider-key"</span>
curl <span class="hljs-string">"http://localhost:7860/test/1396?season=1&#x26;episode=1&#x26;source=provider-key"</span>
</code></pre>
<p>Use source keys reported by the running deployment. A test result reports whether resolution and verification
succeeded; clients should inspect the result body rather than relying only on a transport success.</p>
<h2 id="debug-route">Debug route</h2>
<p><code>GET /debug/:id?source=:providerKey</code> offers detailed candidate and request traces, but it is only
available when <code>ENABLE_DEBUG_ROUTE=true</code>.</p>
<div class="callout callout-warning">
<div class="callout-content">
<p>Do not enable the debug route on a public deployment. It can expose upstream request data, raw
candidates, and headers. Enable it temporarily in a controlled environment, then disable it again.</p>
</div>
</div>
<h2 id="production-checklist">Production checklist</h2>
<ul>
<li>Set <code>TMDB_API_KEY</code> when metadata-dependent sources are required.</li>
<li>Keep <code>ENABLE_DEBUG_ROUTE=false</code> outside controlled troubleshooting.</li>
<li>Use TLS and enforce your own access policy before making the service remotely reachable.</li>
<li>Monitor health trends and playback failures separately.</li>
<li>Run updates and provider changes through your normal deployment process.</li>
<li>Build player fallbacks instead of assuming a specific provider is always available.</li>
</ul>
</main>
<aside id="toc">
<p class="toc-title">On this page</p>
<ul>
<li><a href="#health">Health</a></li>
<li><a href="#test-one-provider">Test one provider</a></li>
<li><a href="#debug-route">Debug route</a></li>
<li><a href="#production-checklist">Production checklist</a></li>
</ul>
</aside>
</div>
</div>
<footer id="site-footer">
<a href="https://discord.gg/5eqRhKeywG" target="_blank" rel="noopener">Discord</a>
<a href="https://gitlab.com/vyla-entertainment/" target="_blank" rel="noopener">GitLab</a>
</footer>
<script src="/assets/app.js"></script>
</body>
</html>

View File

@@ -1,194 +0,0 @@
<!doctype html>
<html lang="en" class="dark">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Stream API — Vyla</title>
<meta name="description" content="An optional self-hosted HTTP and SSE layer built on top of Vyla SDK." />
<meta name="keywords"
content="Stream API, HTTP API, SSE, Server-Sent Events, streaming API, self-hosted API, Node.js API" />
<link rel="canonical" href="https://vyla.cc/stream-api/overview.html" />
<!-- Open Graph / Facebook -->
<meta property="og:type" content="website" />
<meta property="og:url" content="https://vyla.cc/stream-api/overview.html" />
<meta property="og:title" content="Stream API — Vyla" />
<meta property="og:description" content="An optional self-hosted HTTP and SSE layer built on top of Vyla SDK." />
<meta property="og:image" content="https://vyla.cc/assets/images/thumbnail.png" />
<meta property="og:site_name" content="Vyla" />
<!-- Twitter -->
<meta property="twitter:card" content="summary_large_image" />
<meta property="twitter:url" content="https://vyla.cc/stream-api/overview.html" />
<meta property="twitter:title" content="Stream API — Vyla" />
<meta property="twitter:description" content="An optional self-hosted HTTP and SSE layer built on top of Vyla SDK." />
<meta property="twitter:image" content="https://vyla.cc/assets/images/thumbnail.png" />
<meta property="twitter:site" content="@vylaentertainment" />
<!-- Additional SEO -->
<meta name="robots" content="index, follow" />
<meta name="author" content="Vyla" />
<link rel="icon" href="/favicon.ico" />
<link rel="stylesheet" href="/assets/base.css" />
<link rel="stylesheet" href="/assets/highlight.css" />
<link rel="stylesheet" href="/assets/mintlify-overrides.css" />
<!-- JSON-LD Structured Data -->
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "SoftwareApplication",
"name": "Stream API",
"description": "An optional self-hosted HTTP and SSE layer built on top of Vyla SDK.",
"url": "https://vyla.cc/stream-api/overview.html",
"applicationCategory": "MultimediaApplication",
"operatingSystem": "Windows, macOS, Linux, Android, Android TV",
"offers": {
"@type": "Offer",
"price": "0",
"priceCurrency": "USD"
}
}
</script>
</head>
<body>
<div id="banner">
<span>Vyla is self-hosted. Run Vyla Home, embed the SDK, or deploy Stream API on infrastructure you control. Rights
holders can request source removal — <a href="/misc/dmca">DMCA policy</a>.</span>
<button id="banner-close" onclick="dismissBanner()" aria-label="Dismiss">✕</button>
</div>
<header id="navbar">
<div class="nav-top">
<a class="brand" href="/"><img src="/assets/images/logo.png" alt="Vyla" id="brand-logo" loading="lazy"
fetchpriority="low"></a>
<button id="search-trigger" type="button" onclick="openSearch()" aria-label="Search"></button>
<div class="links">
<button id="theme-toggle" type="button" onclick="toggleThemeMenu(event)" aria-label="Theme"></button>
</div>
</div>
<nav class="tabs">
<a class="tab-link" href="/">Vyla</a>
<a class="tab-link" href="/sdk">SDK</a>
<a class="tab-link active" href="/stream-api/overview">Stream API</a>
<a class="tab-link" href="/guides/integrations">Resources</a>
<a class="tab-link" href="/misc/credit">Misc</a>
</nav>
</header>
<div id="layout">
<nav id="sidebar">
<div class="group">
<p class="group-title">Overview</p>
<ul>
<li><a class="active" href="/stream-api/overview">Overview</a></li>
</ul>
</div>
<div class="group">
<p class="group-title">Reference</p>
<ul>
<li><a class="" href="/stream-api/paths">Paths</a></li>
<li><a class="" href="/stream-api/media-data">Media Data</a></li>
<li><a class="" href="/stream-api/operations">Operations</a></li>
</ul>
</div>
<div class="group">
<p class="group-title">Deployment</p>
<ul>
<li><a class="" href="/self-hosting">Self Hosting</a></li>
</ul>
</div>
</nav>
<div id="content-col">
<main class="prose">
<h1>Stream API</h1>
<p class="page-description">An optional self-hosted HTTP and SSE layer built on top of Vyla SDK.</p>
<h2 id="use-this-only-when-http-is-the-right-boundary">Use this only when HTTP is the right boundary</h2>
<p>Stream API packages Vyla SDK behind a small Node.js HTTP service. It is useful when a browser, mobile app, or
another process needs progressive playback candidates over HTTP. It is not required to use Vyla, and it is not
the primary integration for a Node.js application—use <a href="/sdk">Vyla SDK</a> directly in that case.
</p>
<p>The service accepts TMDB IDs, asks configured SDK providers for candidates, verifies results, and emits
usable sources as they arrive. A player can start with the first source while retaining later sources as
fallbacks.</p>
<h2 id="what-it-provides">What it provides</h2>
<table>
<thead>
<tr>
<th>Capability</th>
<th>Endpoint family</th>
<th>Why it exists</th>
</tr>
</thead>
<tbody>
<tr>
<td>Progressive playback</td>
<td><code>/movie</code>, <code>/tv</code></td>
<td>Stream metadata and verified source candidates with SSE</td>
</tr>
<tr>
<td>Supplemental media</td>
<td><code>/subtitles</code>, <code>/downloads</code></td>
<td>Fetch tracks or downloadable candidates separately</td>
</tr>
<tr>
<td>Operations</td>
<td><code>/health</code>, <code>/test</code>, <code>/debug</code></td>
<td>Check source status and investigate a provider when permitted</td>
</tr>
</tbody>
</table>
<h2 id="start-a-local-instance">Start a local instance</h2>
<pre><code class="hljs language-bash">git <span class="hljs-built_in">clone</span> https://gitlab.com/vyla-entertainment/stream-api
<span class="hljs-built_in">cd</span> stream-api
npm install
TMDB_API_KEY=your_key
node server.js
</code></pre>
<p>The default local address is <code>http://localhost:7860</code>. For remote access, place the service behind
TLS and your own access controls. Full configuration is in <a href="/self-hosting">Self-hosting</a>.</p>
<h2 id="lifecycle-of-a-playback-request">Lifecycle of a playback request</h2>
<ol>
<li>A client opens <code>/movie?id=:tmdbId</code> or
<code>/tv?id=:tmdbId&#x26;season=:season&#x26;episode=:episode</code>.
</li>
<li>The API returns an SSE response and emits a <code>meta</code> event with title metadata and subtitle
tracks when available.</li>
<li>Active SDK providers resolve in parallel; verified results arrive as individual <code>source</code>
events.</li>
<li>The server sends <code>done</code> after all source attempts finish or time out.</li>
</ol>
<p>Start the player on the first source. Never wait for <code>done</code> before allowing playback, and always
retain a fallback queue. See <a href="/stream-api/paths">Paths and events</a> for the contract.</p>
<div class="callout callout-tip">
<div class="callout-content">
<p>If your application is already a Node.js service, call <code>VylaSDK#getStream</code>,
<code>getSubtitles</code>, and <code>getDownloads</code> directly. That removes an unnecessary network hop
and lets you define your own public API.
</p>
</div>
</div>
</main>
<aside id="toc">
<p class="toc-title">On this page</p>
<ul>
<li><a href="#use-this-only-when-http-is-the-right-boundary">Use this only when HTTP is the right boundary</a>
</li>
<li><a href="#what-it-provides">What it provides</a></li>
<li><a href="#start-a-local-instance">Start a local instance</a></li>
<li><a href="#lifecycle-of-a-playback-request">Lifecycle of a playback request</a></li>
</ul>
</aside>
</div>
</div>
<footer id="site-footer">
<a href="https://discord.gg/5eqRhKeywG" target="_blank" rel="noopener">Discord</a>
<a href="https://gitlab.com/vyla-entertainment/" target="_blank" rel="noopener">GitLab</a>
</footer>
<script src="/assets/app.js"></script>
</body>
</html>

View File

@@ -0,0 +1,36 @@
# Stream API
<p class="page-description">An optional self-hosted HTTP and SSE layer built on top of Vyla SDK.</p>
## Use this only when HTTP is the right boundary
Stream API packages Vyla SDK behind a small Node.js HTTP service. It is useful when a browser, mobile app, or another process needs progressive playback candidates over HTTP. It is not required to use Vyla, and it is not the primary integration for a Node.js application—use [Vyla SDK](/sdk) directly in that case.
The service accepts TMDB IDs, asks configured SDK providers for candidates, verifies results, and emits usable sources as they arrive. A player can start with the first source while retaining later sources as fallbacks.
## What it provides
| Capability | Endpoint family | Why it exists |
| --- | --- | --- |
| Progressive playback | `/movie`, `/tv` | Stream metadata and verified source candidates with SSE |
| Supplemental media | `/subtitles`, `/downloads` | Fetch tracks or downloadable candidates separately |
| Operations | `/health`, `/test`, `/debug` | Check source status and investigate a provider |
## Start a local instance
```bash
git clone https://gitlab.com/vyla-entertainment/stream-api
cd stream-api
npm install
TMDB_API_KEY=your_key
node server.js
```
The default local address is `http://localhost:7860`. For remote access, place the service behind TLS and your own access controls. Full configuration is in [Self-hosting](/stream-api/deployment/self-hosting).
## Lifecycle of a playback request
1. A client opens `/movie?id=:tmdbId` or `/tv?id=:tmdbId&season=:season&episode=:episode`.
2. The API returns an SSE response and emits a `meta` event with title metadata and subtitle tracks.
3. Active SDK providers resolve in parallel; verified results arrive as individual `source` events.
4. The server sends `done` after all source attempts finish or time out.

View File

@@ -1,196 +0,0 @@
<!doctype html>
<html lang="en" class="dark">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Paths and playback events — Vyla</title>
<meta name="description" content="The complete Stream API playback contract, unified for movies and TV episodes." />
<meta name="keywords"
content="Stream API paths, playback events, SSE events, API endpoints, movie API, TV API, streaming endpoints" />
<link rel="canonical" href="https://vyla.cc/stream-api/paths.html" />
<!-- Open Graph / Facebook -->
<meta property="og:type" content="website" />
<meta property="og:url" content="https://vyla.cc/stream-api/paths.html" />
<meta property="og:title" content="Paths and playback events — Vyla" />
<meta property="og:description"
content="The complete Stream API playback contract, unified for movies and TV episodes." />
<meta property="og:image" content="https://vyla.cc/assets/images/thumbnail.png" />
<meta property="og:site_name" content="Vyla" />
<!-- Twitter -->
<meta property="twitter:card" content="summary_large_image" />
<meta property="twitter:url" content="https://vyla.cc/stream-api/paths.html" />
<meta property="twitter:title" content="Paths and playback events — Vyla" />
<meta property="twitter:description"
content="The complete Stream API playback contract, unified for movies and TV episodes." />
<meta property="twitter:image" content="https://vyla.cc/assets/images/thumbnail.png" />
<meta property="twitter:site" content="@vylaentertainment" />
<!-- Additional SEO -->
<meta name="robots" content="index, follow" />
<meta name="author" content="Vyla" />
<link rel="icon" href="/favicon.ico" />
<link rel="stylesheet" href="/assets/base.css" />
<link rel="stylesheet" href="/assets/highlight.css" />
<link rel="stylesheet" href="/assets/mintlify-overrides.css" />
<!-- JSON-LD Structured Data -->
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "SoftwareApplication",
"name": "Paths and playback events",
"description": "The complete Stream API playback contract, unified for movies and TV episodes.",
"url": "https://vyla.cc/stream-api/paths.html",
"applicationCategory": "MultimediaApplication",
"operatingSystem": "Windows, macOS, Linux, Android, Android TV",
"offers": {
"@type": "Offer",
"price": "0",
"priceCurrency": "USD"
}
}
</script>
</head>
<body>
<div id="banner">
<span>Vyla is self-hosted. Run Vyla Home, embed the SDK, or deploy Stream API on infrastructure you control. Rights
holders can request source removal — <a href="/misc/dmca">DMCA policy</a>.</span>
<button id="banner-close" onclick="dismissBanner()" aria-label="Dismiss">✕</button>
</div>
<header id="navbar">
<div class="nav-top">
<a class="brand" href="/"><img src="/assets/images/logo.png" alt="Vyla" id="brand-logo" loading="lazy"
fetchpriority="low"></a>
<button id="search-trigger" type="button" onclick="openSearch()" aria-label="Search"></button>
<div class="links">
<button id="theme-toggle" type="button" onclick="toggleThemeMenu(event)" aria-label="Theme"></button>
</div>
</div>
<nav class="tabs">
<a class="tab-link" href="/">Vyla</a>
<a class="tab-link" href="/sdk">SDK</a>
<a class="tab-link active" href="/stream-api/overview">Stream API</a>
<a class="tab-link" href="/guides/integrations">Resources</a>
<a class="tab-link" href="/misc/credit">Misc</a>
</nav>
</header>
<div id="layout">
<nav id="sidebar">
<div class="group">
<p class="group-title">Overview</p>
<ul>
<li><a class="" href="/stream-api/overview">Overview</a></li>
</ul>
</div>
<div class="group">
<p class="group-title">Reference</p>
<ul>
<li><a class="active" href="/stream-api/paths">Paths</a></li>
<li><a class="" href="/stream-api/media-data">Media Data</a></li>
<li><a class="" href="/stream-api/operations">Operations</a></li>
</ul>
</div>
<div class="group">
<p class="group-title">Deployment</p>
<ul>
<li><a class="" href="/self-hosting">Self Hosting</a></li>
</ul>
</div>
</nav>
<div id="content-col">
<main class="prose">
<h1>Paths and playback events</h1>
<p class="page-description">The complete Stream API playback contract, unified for movies and TV episodes.</p>
<h2 id="playback-paths">Playback paths</h2>
<p>The API supports both the short paths below and <code>/api</code>-prefixed equivalents. A movie needs only
<code>id</code>; a TV episode also needs <code>season</code> and <code>episode</code>.
</p>
<table>
<thead>
<tr>
<th>Media</th>
<th>Path</th>
<th>Response</th>
</tr>
</thead>
<tbody>
<tr>
<td>Movie</td>
<td><code>GET /movie?id=:id</code></td>
<td>Server-Sent Events</td>
</tr>
<tr>
<td>TV episode</td>
<td><code>GET /tv?id=:id&#x26;season=:season&#x26;episode=:episode</code></td>
<td>Server-Sent Events</td>
</tr>
</tbody>
</table>
<p><code>id</code> is a TMDB ID. For example:</p>
<pre><code class="hljs language-bash">curl -N <span class="hljs-string">"http://localhost:7860/movie?id=155"</span>
curl -N <span class="hljs-string">"http://localhost:7860/tv?id=1396&#x26;season=1&#x26;episode=1"</span>
</code></pre>
<h2 id="event-contract">Event contract</h2>
<p>Playback routes use <code>text/event-stream</code>, not a single JSON document. Read each <code>data: </code>
line as JSON.</p>
<h3 id="meta"><code>meta</code></h3>
<p>Sent before source resolution completes. It can include TMDB metadata and an array of subtitle tracks.</p>
<pre><code class="hljs language-json"><span class="hljs-punctuation">{</span>
<span class="hljs-attr">"type"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"meta"</span><span class="hljs-punctuation">,</span>
<span class="hljs-attr">"meta"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span> <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">155</span><span class="hljs-punctuation">,</span> <span class="hljs-attr">"title"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"The Dark Knight"</span> <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span>
<span class="hljs-attr">"subtitles"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span><span class="hljs-punctuation">{</span> <span class="hljs-attr">"label"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"English"</span><span class="hljs-punctuation">,</span> <span class="hljs-attr">"file"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"https://…"</span><span class="hljs-punctuation">,</span> <span class="hljs-attr">"type"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"vtt"</span><span class="hljs-punctuation">,</span> <span class="hljs-attr">"source"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"v1"</span> <span class="hljs-punctuation">}</span><span class="hljs-punctuation">]</span>
<span class="hljs-punctuation">}</span>
</code></pre>
<h3 id="source"><code>source</code></h3>
<p>One event for each candidate that passes the service's verification pipeline.</p>
<pre><code class="hljs language-json"><span class="hljs-punctuation">{</span>
<span class="hljs-attr">"type"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"source"</span><span class="hljs-punctuation">,</span>
<span class="hljs-attr">"source"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span>
<span class="hljs-attr">"source"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"provider-key"</span><span class="hljs-punctuation">,</span>
<span class="hljs-attr">"label"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Provider name"</span><span class="hljs-punctuation">,</span>
<span class="hljs-attr">"url"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"http://localhost:7860/api?url=…"</span>
<span class="hljs-punctuation">}</span>
<span class="hljs-punctuation">}</span>
</code></pre>
<p>The <code>url</code> is ready for a player. HLS sources may be wrapped by Vyla's proxy so manifest segments
and keys can be fetched with the appropriate upstream headers. Some configured sources bypass proxying. Do not
assume a filename extension alone determines playback behavior.</p>
<h3 id="done"><code>done</code></h3>
<p>Sent after the configured attempts are complete.</p>
<pre><code class="hljs language-json"><span class="hljs-punctuation">{</span> <span class="hljs-attr">"type"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"done"</span><span class="hljs-punctuation">,</span> <span class="hljs-attr">"total"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">3</span> <span class="hljs-punctuation">}</span>
</code></pre>
<p><code>total: 0</code> means no playable sources were produced for that request. The SSE connection itself can
still be successful.</p>
<h2 id="client-rules">Client rules</h2>
<ol>
<li>Start playback as soon as the first <code>source</code> arrives.</li>
<li>Queue every later source for manual switching or automatic fallback.</li>
<li>Attach subtitle tracks from <code>meta</code> without blocking playback.</li>
<li>Treat an empty result as a normal product state, not a malformed response.</li>
<li>Abort the request when the viewer leaves the page or chooses another title.</li>
</ol>
<p>The <a href="/guides/integrations">integration guide</a> contains a practical player pattern; <a
href="/guides/error-handling">error handling</a> covers fallback behavior.</p>
</main>
<aside id="toc">
<p class="toc-title">On this page</p>
<ul>
<li><a href="#playback-paths">Playback paths</a></li>
<li><a href="#event-contract">Event contract</a></li>
<li><a href="#client-rules">Client rules</a></li>
</ul>
</aside>
</div>
</div>
<footer id="site-footer"><a href="https://discord.gg/5eqRhKeywG" target="_blank" rel="noopener">Discord</a></footer>
<script src="/assets/app.js"></script>
</body>
</html>

View File

@@ -0,0 +1,54 @@
# Subtitles and downloads
<p class="page-description">Unified media-data paths for movies and TV episodes.</p>
## Subtitle paths
| Media | Path |
| --- | --- |
| Movie | `GET /subtitles/movie/:id` |
| TV episode | `GET /subtitles/tv/:id/:season/:episode` |
The same routes are available under `/api/subtitles`. A successful response is a JSON array of tracks; a missing result returns `404` with an error object.
```json
[
{
"label": "English",
"file": "https://example.invalid/subtitles/en.vtt",
"type": "vtt",
"source": "v1"
}
]
```
`file` is the track URL, `type` is normally `vtt` or `srt`, and `source` identifies the subtitle backend. Prefer the tracks included in the playback `meta` event when you are already opening a playback stream; use these paths when subtitles are fetched separately.
## Download paths
| Media | Path |
| --- | --- |
| Movie | `GET /downloads/movie/:id` |
| TV episode | `GET /downloads/tv/:id/:season/:episode` |
The `/api/downloads` prefix is also supported. A successful response is an array; an empty array simply means no candidates were found.
```json
[
{
"url": "https://example.invalid/file.mp4",
"quality": "1080p",
"size": "2.14 GB",
"format": "MP4"
}
]
```
Download URLs are provider results, not durable Vyla-hosted files. Display quality and size as informational fields, and handle expired or unavailable links in your client.
## Request and response rules
- IDs are TMDB IDs; TV paths additionally require season and episode numbers.
- Both families return JSON, unlike the SSE playback paths.
- Handle `404` for missing subtitles and `500` for an upstream or resolver failure.
- Do not block first playback on either call. Fetch them in parallel or after the player has started.

View File

@@ -0,0 +1,37 @@
# Health, testing, and safe operations
<p class="page-description">Observe SDK providers and diagnose a Stream API deployment without making debug data public.</p>
## Health
`GET /health` (or `/api/health`) probes active providers and returns a JSON summary. It includes a status, timestamp, TMDB-key presence, cache size, and a map of provider keys to `{ ok, ms }` results.
Health is a signal, not a promise of playback. Provider behavior can differ by title, client network, and the moment of the request. Use it for observability and source selection—not as a guarantee that a viewer will receive a stream.
## Test one provider
`GET /test/:id?source=:providerKey` resolves a single provider for a movie. Add `season` and `episode` (or `s` and `e`) for TV.
```bash
curl "http://localhost:7860/test/155?source=provider-key"
curl "http://localhost:7860/test/1396?season=1&episode=1&source=provider-key"
```
Use source keys reported by the running deployment. A test result reports whether resolution and verification succeeded; clients should inspect the result body rather than relying only on a transport success.
## Debug route
`GET /debug/:id?source=:providerKey` offers detailed candidate and request traces, but it is only available when `ENABLE_DEBUG_ROUTE=true`.
:::callout-warning
Do not enable the debug route on a public deployment. It can expose upstream request data, raw candidates, and headers. Enable it temporarily in a controlled environment, then disable it again.
:::
## Production checklist
- Set `TMDB_API_KEY` when metadata-dependent sources are required.
- Keep `ENABLE_DEBUG_ROUTE=false` outside controlled troubleshooting.
- Use TLS and enforce your own access policy before making the service remotely reachable.
- Monitor health trends and playback failures separately.
- Run updates and provider changes through your normal deployment process.
- Build player fallbacks instead of assuming a specific provider is always available.

View File

@@ -0,0 +1,72 @@
# Paths and playback events
<p class="page-description">The complete Stream API playback contract, unified for movies and TV episodes.</p>
## Playback paths
The API supports both the short paths below and `/api`-prefixed equivalents. A movie needs only `id`; a TV episode also needs `season` and `episode`.
| Media | Path | Response |
| --- | --- | --- |
| Movie | `GET /movie?id=:id` | Server-Sent Events |
| TV episode | `GET /tv?id=:id&season=:season&episode=:episode` | Server-Sent Events |
`id` is a TMDB ID. For example:
```bash
curl -N "http://localhost:7860/movie?id=155"
curl -N "http://localhost:7860/tv?id=1396&season=1&episode=1"
```
## Event contract
Playback routes use `text/event-stream`, not a single JSON document. Read each `data: ` line as JSON.
### `meta`
Sent before source resolution completes. It can include TMDB metadata and an array of subtitle tracks.
```json
{
"type": "meta",
"meta": { "id": 155, "title": "The Dark Knight" },
"subtitles": [{ "label": "English", "file": "https://…", "type": "vtt", "source": "v1" }]
}
```
### `source`
One event for each candidate that passes the service's verification pipeline.
```json
{
"type": "source",
"source": {
"source": "provider-key",
"label": "Provider name",
"url": "http://localhost:7860/api?url=…"
}
}
```
The `url` is ready for a player. HLS sources may be wrapped by Vyla's proxy so manifest segments and keys can be fetched with the appropriate upstream headers. Some configured sources bypass proxying. Do not assume a filename extension alone determines playback behavior.
### `done`
Sent after the configured attempts are complete.
```json
{ "type": "done", "total": 3 }
```
`total: 0` means no playable sources were produced for that request. The SSE connection itself can still be successful.
## Client rules
1. Start playback as soon as the first `source` arrives.
2. Queue every later source for manual switching or automatic fallback.
3. Attach subtitle tracks from `meta` without blocking playback.
4. Treat an empty result as a normal product state, not a malformed response.
5. Abort the request when the viewer leaves the page or chooses another title.
The [integration guide](/guides/integrations) contains a practical player pattern; [error handling](/guides/error-handling) covers fallback behavior.