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

@@ -1,177 +1,113 @@
{
"name": "Vyla Documentation",
"description": "Comprehensive documentation for Vyla - a community-built, self-hosted media project focused on freedom, privacy, and user control. Includes SDK reference, Stream API documentation, and guides for building custom media players.",
"url": "https://vyla.cc",
"type": "documentation",
"category": "software",
"tags": [
"media streaming",
"self-hosted",
"privacy",
"open source",
"SDK",
"API",
"Node.js",
"TypeScript",
"Android",
"Android TV",
"desktop",
"streaming",
"video player",
"HLS",
"SSE"
],
"language": "en",
"version": "1.0.0",
"lastUpdated": "2026-10-06",
"maintainer": {
"name": "Vyla Entertainment",
"url": "https://gitlab.com/vyla-entertainment/"
"$schema": "https://json-schema.org/draft/2020-12/schema",
"name": "Vyla Entertainment",
"identifier": "vyla-entertainment",
"description": "A community-built media streaming and scraper ecosystem providing native applications, a Node.js resolver SDK, and a self-hosted Stream API.",
"homepage": "https://vyla.cc",
"documentation": "https://vyla.cc",
"llms_context": "https://vyla.cc/llms.txt",
"repository": "https://gitlab.com/vyla-entertainment",
"community": "https://discord.gg/5eqRhKeywG",
"license": "Open Source",
"ecosystem": {
"clients": [
{
"name": "Vyla Home",
"platform": [
"Windows",
"macOS",
"Linux"
],
"description": "Desktop control center and self-contained runtime.",
"documentation": "https://vyla.cc/products/home"
},
{
"name": "Vyla Go",
"platform": [
"Android"
],
"description": "Mobile application running local resolver services.",
"documentation": "https://vyla.cc/products/go"
},
{
"name": "Vyla TV",
"platform": [
"Android TV"
],
"description": "Television interface built with Jetpack Compose and ExoPlayer.",
"documentation": "https://vyla.cc/products/tv"
}
],
"libraries": [
{
"package": "@vyla-entertainment/sdk",
"language": "Node.js (>=18)",
"description": "Server-side resolver library for stream links, subtitle extraction, and download links.",
"documentation": "https://vyla.cc/sdk"
}
],
"services": [
{
"name": "Stream API",
"type": "Self-hosted HTTP/SSE Proxy",
"description": "REST and Server-Sent Events service wrapping Vyla SDK for remote clients.",
"default_port": 7860,
"protocols": [
"HTTP",
"SSE"
],
"documentation": "https://vyla.cc/stream-api/overview",
"endpoints": [
{
"path": "/movie",
"method": "GET",
"query_parameters": [
"id"
],
"response_type": "text/event-stream"
},
{
"path": "/tv",
"method": "GET",
"query_parameters": [
"id",
"season",
"episode"
],
"response_type": "text/event-stream"
},
{
"path": "/subtitles/movie/:id",
"method": "GET",
"response_type": "application/json"
},
{
"path": "/subtitles/tv/:id/:season/:episode",
"method": "GET",
"response_type": "application/json"
},
{
"path": "/downloads/movie/:id",
"method": "GET",
"response_type": "application/json"
},
{
"path": "/downloads/tv/:id/:season/:episode",
"method": "GET",
"response_type": "application/json"
},
{
"path": "/health",
"method": "GET",
"response_type": "application/json"
}
]
}
]
},
"documentation": {
"main": "https://vyla.cc",
"llms": "https://vyla.cc/llms.txt",
"repository": "https://gitlab.com/vyla-entertainment/"
},
"topics": [
{
"name": "Getting Started",
"pages": [
{
"title": "Introduction",
"url": "/"
},
{
"title": "Architecture",
"url": "/architecture"
}
]
},
{
"name": "Products",
"pages": [
{
"title": "Vyla Home",
"url": "/home"
},
{
"title": "Vyla Go",
"url": "/go"
},
{
"title": "Vyla TV",
"url": "/tv"
},
{
"title": "Build a Player",
"url": "/player"
}
]
},
{
"name": "SDK",
"pages": [
{
"title": "SDK Reference",
"url": "/sdk"
},
{
"title": "SDK Quickstart",
"url": "/sdk-quickstart"
}
]
},
{
"name": "Stream API",
"pages": [
{
"title": "Overview",
"url": "/stream-api/overview"
},
{
"title": "Paths and Events",
"url": "/stream-api/paths"
},
{
"title": "Subtitles and Downloads",
"url": "/stream-api/media-data"
},
{
"title": "Health and Operations",
"url": "/stream-api/operations"
}
]
},
{
"name": "Deployment",
"pages": [
{
"title": "Self-hosting",
"url": "/self-hosting"
}
]
},
{
"name": "Concepts",
"pages": [
{
"title": "Providers",
"url": "/concepts/providers"
}
]
},
{
"name": "Guides",
"pages": [
{
"title": "Client Integration",
"url": "/guides/integrations"
},
{
"title": "Error Handling",
"url": "/guides/error-handling"
}
]
},
{
"name": "Miscellaneous",
"pages": [
{
"title": "Credits",
"url": "/misc/credit"
},
{
"title": "DMCA Policy",
"url": "/misc/dmca"
}
]
}
],
"apis": [
{
"name": "Vyla SDK",
"type": "npm",
"package": "@vyla-entertainment/sdk",
"description": "Node.js/TypeScript SDK for direct provider resolution",
"language": "TypeScript"
},
{
"name": "Stream API",
"type": "http",
"description": "Self-hosted HTTP/SSE API for browser and mobile clients",
"protocol": "SSE"
}
],
"platforms": [
"Windows",
"macOS",
"Linux",
"Android",
"Android TV"
],
"license": "ISC",
"community": {
"discord": "https://discord.gg/5eqRhKeywG",
"gitlab": "https://gitlab.com/vyla-entertainment/"
"compliance": {
"hosting_policy": "Non-hosting scraper proxy; all video segments originate from third-party hosts.",
"dmca_contact": "alwaysmesmerizingyou@gmail.com",
"dmca_policy": "https://vyla.cc/misc/project/dmca"
}
}

View File

@@ -1,190 +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>Our Architecture — Vyla</title>
<meta name="description"
content="How Vyla Home, SDK, and Stream API relate, and where each responsibility belongs." />
<meta name="keywords"
content="Vyla architecture, Vyla Home, Vyla SDK, Stream API, system design, media stack, deployment patterns, self-hosted media" />
<link rel="canonical" href="https://vyla.cc/architecture.html" />
<!-- Open Graph / Facebook -->
<meta property="og:type" content="website" />
<meta property="og:url" content="https://vyla.cc/architecture.html" />
<meta property="og:title" content="Our Architecture — Vyla" />
<meta property="og:description"
content="How Vyla Home, SDK, and Stream API relate, and where each responsibility belongs." />
<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/architecture.html" />
<meta property="twitter:title" content="Our Architecture — Vyla" />
<meta property="twitter:description"
content="How Vyla Home, SDK, and Stream API relate, and where each responsibility belongs." />
<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": "Our Architecture",
"description": "How Vyla Home, SDK, and Stream API relate, and where each responsibility belongs.",
"url": "https://vyla.cc/architecture.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">Start here</p>
<ul>
<li><a class="active" href="/">Introduction</a></li>
<li><a class="" href="/architecture">Architecture</a></li>
</ul>
</div>
<div class="group">
<p class="group-title">Products</p>
<ul>
<li><a class="" href="/home">Home</a></li>
<li><a class="" href="/go">Go</a></li>
<li><a class="" href="/tv">Tv</a></li>
<li><a class="" href="/player">Player</a></li>
</ul>
</div>
<div class="group">
<p class="group-title">Concepts</p>
<ul>
<li><a class="" href="/concepts/providers">Providers</a></li>
</ul>
</div>
</nav>
<div id="content-col">
<main class="prose">
<h1>Our Architecture</h1>
<p class="page-description">How Vyla Home, SDK, and Stream API relate, and where each responsibility belongs.
</p>
<h2 id="the-vyla-stack">The Vyla stack</h2>
<p>Vyla separates local application management, provider resolution, and client-facing delivery. That separation
lets each layer be used on its own while keeping the integration points clear.</p>
<h3 id="vyla-home">Vyla Home</h3>
<p>Home is responsible for a desktop session: local configuration, encrypted local credentials, update
coordination, managed processes, and the embedded interface. It is the right layer for launching and
supervising a local experience.</p>
<h3 id="vyla-sdk">Vyla SDK</h3>
<p>The SDK contains the provider-facing work: source configuration, stream resolution, subtitle retrieval,
download lookup, and health probes. It is the right layer when your Node.js application needs to decide how
requests are authorized, cached, selected, and returned.</p>
<h3 id="stream-api">Stream API</h3>
<p>Stream API wraps the SDK in a self-hosted HTTP service. Its movie and TV endpoints use Server-Sent Events so
metadata can arrive first and usable sources can be delivered progressively. It also provides subtitle,
download, health, test, and optional debug routes.</p>
<h2 id="common-deployment-patterns">Common deployment patterns</h2>
<table>
<thead>
<tr>
<th>Pattern</th>
<th>Best for</th>
<th>Key decision</th>
</tr>
</thead>
<tbody>
<tr>
<td>Home only</td>
<td>a managed local desktop experience</td>
<td>Home owns the lifecycle of bundled local services</td>
</tr>
<tr>
<td>App + SDK</td>
<td>a Node.js backend with a custom product API</td>
<td>your app owns authentication, policy, and response design</td>
</tr>
<tr>
<td>Player + Stream API</td>
<td>browser or mobile clients that need a stable HTTP contract</td>
<td>deploy Stream API and place it behind TLS when exposed remotely</td>
</tr>
<tr>
<td>Home + Stream API</td>
<td>a local player backed by a local service</td>
<td>keep the service bound and access-controlled for the local environment</td>
</tr>
</tbody>
</table>
<h2 id="data-flow-for-playback">Data flow for playback</h2>
<ol>
<li>A client provides a TMDB media ID (plus season and episode for TV).</li>
<li>Stream API, or your own SDK-based backend, asks active providers for candidates.</li>
<li>Candidates are checked and successful sources are returned progressively.</li>
<li>The player begins with the first compatible candidate and keeps later candidates as fallbacks.</li>
<li>Subtitle and download lookups are separate operations; neither should block first playback.</li>
</ol>
<p>Provider availability varies by source and network environment. Build the player experience around fallbacks
and empty-result states, as described in <a href="/guides/error-handling">Error handling</a>.</p>
</main>
<aside id="toc">
<p class="toc-title">On this page</p>
<ul>
<li><a href="#the-vyla-stack">The Vyla stack</a></li>
<li><a href="#common-deployment-patterns">Common deployment patterns</a></li>
<li><a href="#data-flow-for-playback">Data flow for playback</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>

File diff suppressed because it is too large Load Diff

View File

@@ -1,107 +0,0 @@
pre code.hljs {
display: block;
overflow-x: auto;
padding: 1em;
scrollbar-width: none;
-ms-overflow-style: none;
::-webkit-scrollbar {
width: 0;
}
}
code.hljs {
padding: 3px 5px;
}
.hljs {
color: #5e5e5e;
background: #0d1117
}
.hljs-doctag,
.hljs-keyword,
.hljs-meta .hljs-keyword,
.hljs-template-tag,
.hljs-template-variable,
.hljs-type,
.hljs-variable.language_ {
color: #ff7b72
}
.hljs-title,
.hljs-title.class_,
.hljs-title.class_.inherited__,
.hljs-title.function_ {
color: #d2a8ff
}
.hljs-attr,
.hljs-attribute,
.hljs-literal,
.hljs-meta,
.hljs-number,
.hljs-operator,
.hljs-variable,
.hljs-selector-attr,
.hljs-selector-class,
.hljs-selector-id {
color: #79c0ff
}
.hljs-regexp,
.hljs-string,
.hljs-meta .hljs-string {
color: #a5d6ff
}
.hljs-built_in,
.hljs-symbol {
color: #ffa657
}
.hljs-comment,
.hljs-code,
.hljs-formula {
color: #8b949e
}
.hljs-name,
.hljs-quote,
.hljs-selector-tag,
.hljs-selector-pseudo {
color: #7ee787
}
.hljs-subst {
color: #5e5e5e
}
.hljs-section {
color: #1f6feb;
font-weight: bold
}
.hljs-bullet {
color: #f2cc60
}
.hljs-emphasis {
color: #5e5e5e;
font-style: italic
}
.hljs-strong {
color: #5e5e5e;
font-weight: bold
}
.hljs-addition {
color: #aff5b4;
background-color: #033a16
}
.hljs-deletion {
color: #ffdcd7;
background-color: #67060c
}

View File

@@ -1,121 +0,0 @@
:root {
--table-header-text: #000000;
--vyla-black: #000000;
--vyla-white: #ffffff;
--vyla-border-light: rgba(0, 0, 0, 0.12);
--vyla-border-dark: rgba(255, 255, 255, 0.16);
--banner-bg: #ffffff;
--banner-text: #000000;
}
.dark {
--table-header-text: #ffffff;
--banner-bg: #000000;
--banner-text: #ffffff;
}
table th {
color: var(--table-header-text) !important;
}
#banner,
#banner *,
#banner p,
#banner a {
color: var(--banner-text) !important;
}
.data-\[is-opaque\=true\]\:bg-background-light[data-is-opaque=true] {
background-color: rgba(255, 255, 255, 0.2) !important;
}
.data-\[is-opaque\=true\]\:dark\:bg-background-dark\/75:is(.dark *)[data-is-opaque=true] {
background-color: rgba(0, 0, 0, 0.2) !important;
}
#banner a {
text-decoration: underline !important;
text-underline-offset: 3px;
}
#banner button {
color: var(--banner-text) !important;
}
#navbar a[href*="gitlab"],
#navbar a[href*="discord"],
#navbar a[href="/"] {
border-radius: 999px !important;
}
#navbar a[href="/"] {
padding-left: 16px !important;
padding-right: 16px !important;
}
.dark\:border-gray-300\/\[0\.06\]:is(.dark *) {
border-color: transparent !important;
}
.dark\:ring-0:is(.dark *),
.dark\:ring-1:is(.dark *),
.ring-1,
.ring-2 {
box-shadow: none !important;
}
.dark\:hover\:\!border-primary-light:hover:is(.dark *) {
background-color: transparent !important;
border-color: transparent !important;
}
.hover\:\!border-primary:hover {
background-color: transparent !important;
border-color: transparent !important;
}
.dark\:border-white\/10:is(.dark *) {
border-color: transparent !important;
}
.dark\:bg-white\/10:is(.dark *) {
background-color: rgba(25, 25, 25, 0.1) !important;
}
html.dark .code-block-background {
background-color: rgba(5, 5, 5) !important;
border-color: transparent !important;
}
html .code-block-background {
background-color: rgba(250, 250, 250) !important;
border-color: transparent !important;
}
[data-fade-overlay] {
background-color: transparent !important;
}
.border {
border-color: transparent !important;
}
.h-\[1\.5px\] {
height: 0px !important;
}
.prose :where(img):not(:where([class~=not-prose], [class~=not-prose] *)) {
border-radius: 30px;
}
html.dark [data-fade-overlay] {
background: transparent !important;
}
.dark\:text-gray-500:is(.dark *) {
display: none !important;
}
.px-6 {
padding-left: 0px !important;
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,140 @@
html.dark pre[class*="language-"],
html.dark code[class*="language-"] {
color: #abb2bf;
}
.token.comment,
.token.prolog,
.token.doctype,
.token.cdata {
color: #6a737d;
font-style: italic;
}
html.dark .token.comment,
html.dark .token.prolog,
html.dark .token.doctype,
html.dark .token.cdata {
color: #5c6370;
font-style: italic;
}
.token.punctuation {
color: #24292e;
}
html.dark .token.punctuation {
color: #abb2bf;
}
.token.function,
.token.function-name {
color: #6f42c1;
font-weight: 500;
}
html.dark .token.function,
html.dark .token.function-name {
color: #61afef;
font-weight: 500;
}
.token.keyword,
.token.selector {
color: #d73a49;
font-weight: 600;
}
html.dark .token.keyword,
html.dark .token.selector {
color: #c678dd;
font-weight: 600;
}
.token.string,
.token.url {
color: #22863a;
}
html.dark .token.string,
html.dark .token.url {
color: #98c379;
}
.token.variable,
.token.attr-name,
.token.property {
color: #e36209;
}
html.dark .token.variable,
html.dark .token.attr-name,
html.dark .token.property {
color: #e06c75;
}
.token.operator,
.token.entity {
color: #d73a49;
}
html.dark .token.operator,
html.dark .token.entity {
color: #56b6c2;
}
.token.number,
.token.boolean,
.token.constant {
color: #005cc5;
}
html.dark .token.number,
html.dark .token.boolean,
html.dark .token.constant {
color: #d19a66;
}
.token.parameter {
color: #b08800;
}
html.dark .token.parameter {
color: #d19a66;
}
.token.class-name {
color: #6f42c1;
}
html.dark .token.class-name {
color: #e5c07b;
}
.token.tag {
color: #22863a;
}
html.dark .token.tag {
color: #e06c75;
}
.token.deleted {
color: #b31d28;
background-color: #ffeef0;
}
html.dark .token.deleted {
color: #e06c75;
background-color: rgba(224, 108, 117, 0.15);
}
.token.inserted {
color: #22863a;
background-color: #f0fff4;
}
html.dark .token.inserted {
color: #98c379;
background-color: rgba(152, 195, 121, 0.15);
}

3
assets/styling/main.css Normal file
View File

@@ -0,0 +1,3 @@
@import "roots.css";
@import "base.css";
@import "highlight.css";

60
assets/styling/roots.css Normal file
View File

@@ -0,0 +1,60 @@
@import url("https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap");
:root {
--primary: #000000;
--bg-light: #ffffff;
--bg-dark: #000000;
--text-light: #0b0b0c;
--text-dark: #f3f3f3;
--muted-light: #6b7280;
--muted-dark: #a3a3a3;
--sidebar-width: 256px;
--toc-width: 240px;
--header-height: 148px;
--font: Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
--mono: ui-monospace, SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace;
--border-light: #e5e7eb;
--border-dark: #2b2b2b98;
}
body.banner-dismissed {
--header-height: 109px;
}
* {
box-sizing: border-box;
}
html {
height: 100%;
}
body {
min-height: 100%;
margin: 0;
font-family: var(--font);
background: var(--bg-light);
color: var(--text-light);
line-height: 1.65;
overflow-x: hidden;
}
html.dark body {
background: var(--bg-dark);
color: var(--text-dark);
}
.video-bg {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
object-fit: cover;
z-index: -1;
}
a {
color: inherit;
text-decoration: none;
}

87
concepts/providers.md Normal file
View File

@@ -0,0 +1,87 @@
# Providers
<p class="page-description">How Vyla SDK sources are configured, selected, and verified.</p>
Vyla SDK aggregates multiple independent providers. Each has different internal mechanisms, reliability profiles, and timeout characteristics. This page explains the source layer that powers both direct SDK use and the optional Stream API.
:::callout-note
Provider availability and performance can vary based on your network environment. When self-hosting, providers may work differently than on residential connections due to IP-based blocking from upstream sources.
:::
---
## How Providers Are Used
When your app calls `getStream()`—directly or through Stream API:
1. All active (non-disabled) providers are queried **simultaneously** with per-source random jitter to stagger upstream requests
2. Each provider has its own **timeout**, **retry count**, and **backoff delay**
3. Every returned URL is **verified** — first via a HEAD request to the raw upstream URL, then via a proxied HLS playability check
4. Only URLs that pass both verification steps appear in the response as `source` events
Because the endpoint is SSE, **verified sources are emitted immediately as each provider resolves** — your player receives them progressively. The stream closes once every provider has either resolved or timed out.
:::callout-info
Call `sdk.getSources(true)` to inspect enabled providers and `sdk.probeAllSources()` for a live availability signal. If you use Stream API, its `/health` endpoint exposes probes over HTTP.
:::
---
## Provider Configuration
Each provider is defined with the following properties:
| Field | Description |
| --- | --- |
| `key` | Internal identifier used in API responses and the `source` field |
| `label` | Human-readable name shown in the `label` field |
| `proxyParam` | Short query parameter used to route proxied requests |
| `timeout` | Maximum time (ms) to wait for a stream URL |
| `sourcesTimeout` | Optional secondary timeout (ms) for an internal source-listing step |
| `jitter` | Max random delay (ms) before the request fires |
| `retries` | Number of retry attempts on failure |
| `skipProxy` | If `true`, stream URLs are returned as-is without proxy wrapping |
| `disabled` | If `true`, the provider is excluded from all requests |
---
## Response Time
The first `source` event typically arrives within **3–8 seconds**. Subsequent sources stream in as each provider resolves. The stream closes once the **slowest configured provider** times out.
**Best practice:** always show a loading state, begin playback on the first `source` event, and implement a client-side timeout for the overall stream. See the [Error Handling](/guides/error-handling) guide.
---
## Caching
Results are cached in-memory with a **5-minute TTL**. The per-source stream cache is keyed by `source_key + tmdb_id + season + episode`.
```
CACHE_TTL = 5 minutes
```
---
## The HLS Proxy
All provider stream URLs are wrapped in the server proxy (unless `skipProxy: true`):
```
http://localhost:7860/api?url=<encoded_stream_url>&<proxyParam>=1
```
When your player fetches this path from your self-hosted instance, the server:
1. Fetches the upstream M3U8 with the correct `Referer`, `Origin`, and any provider-specific headers
2. Rewrites all segment URLs and encryption key URIs in the playlist to route through the same proxy
3. Returns the rewritten playlist with `Access-Control-Allow-Origin: *`
---
## Stream Verification
Before a URL is emitted as a `source` event, it passes two verification steps:
1. **`verifyStream`** — a HEAD request to the raw upstream URL. Must return HTTP `< 400`.
2. **`verifyPlayable`** — fetches the proxied URL, validates `#EXTM3U`, checks for `#EXTINF` or `#EXT-X-STREAM-INF`, then fetches the first segment.

View File

@@ -1,293 +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>Providers — Vyla</title>
<meta name="description" content="How Vyla SDK sources are configured, selected, and verified." />
<meta name="keywords"
content="providers, source configuration, stream sources, provider verification, SDK providers, source selection" />
<link rel="canonical" href="https://vyla.cc/concepts/providers.html" />
<!-- Open Graph / Facebook -->
<meta property="og:type" content="website" />
<meta property="og:url" content="https://vyla.cc/concepts/providers.html" />
<meta property="og:title" content="Providers — Vyla" />
<meta property="og:description" content="How Vyla SDK sources are configured, selected, and verified." />
<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/concepts/providers.html" />
<meta property="twitter:title" content="Providers — Vyla" />
<meta property="twitter:description" content="How Vyla SDK sources are configured, selected, and verified." />
<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": "Providers",
"description": "How Vyla SDK sources are configured, selected, and verified.",
"url": "https://vyla.cc/concepts/providers.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">Start here</p>
<ul>
<li><a class="active" href="/">Introduction</a></li>
</li>
<li><a class="" href="/architecture">Architecture</a></li>
</ul>
</div>
<div class="group">
<p class="group-title">Products</p>
<ul>
<li><a class="" href="/home">Home</a></li>
<li><a class="" href="/go">Go</a></li>
<li><a class="" href="/tv">Tv</a></li>
<li><a class="" href="/player">Player</a></li>
</ul>
</div>
<div class="group">
<p class="group-title">Concepts</p>
<ul>
<li><a class="active" href="/concepts/providers">Providers</a></li>
</ul>
</div>
</nav>
<div id="content-col">
<main class="prose">
<h1>Providers</h1>
<p class="page-description">How Vyla SDK sources are configured, selected, and verified.</p>
<p>Vyla SDK aggregates multiple independent providers. Each has different internal mechanisms, reliability
profiles, and timeout characteristics. This page explains the source layer that powers both direct SDK use and
the optional Stream API.</p>
<div class="callout callout-note">
<div class="callout-content">
<p>Provider availability and performance can vary based on your network environment. When self-hosting,
providers may work differently than on residential connections due to IP-based blocking from upstream
sources.</p>
</div>
</div>
<hr>
<h2 id="how-providers-are-used">How Providers Are Used</h2>
<p>When your app calls <code>getStream()</code>—directly or through Stream API:</p>
<ol>
<li>All active (non-disabled) providers are queried <strong>simultaneously</strong> with per-source random
jitter to stagger upstream requests</li>
<li>Each provider has its own <strong>timeout</strong>, <strong>retry count</strong>, and <strong>backoff
delay</strong></li>
<li>Every returned URL is <strong>verified</strong> — first via a HEAD request to the raw upstream URL, then
via a proxied HLS playability check</li>
<li>Only URLs that pass both verification steps appear in the response as <code>source</code> events</li>
</ol>
<p>Because the endpoint is SSE, <strong>verified sources are emitted immediately as each provider
resolves</strong> — your player receives them progressively. The stream closes once every provider has
either resolved or timed out.</p>
<div class="callout callout-tip">
<div class="callout-content">
<p>Call <code>sdk.getSources(true)</code> to inspect enabled providers and
<code>sdk.probeAllSources()</code> for a live availability signal. If you use Stream API, its <a
href="/stream-api/operations"><code>/health</code></a> endpoint exposes probes over HTTP.
</p>
</div>
</div>
<hr>
<h2 id="provider-configuration">Provider Configuration</h2>
<p>Each provider is defined with the following properties:</p>
<table>
<thead>
<tr>
<th>Field</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>key</code></td>
<td>Internal identifier used in API responses and the <code>source</code> field</td>
</tr>
<tr>
<td><code>label</code></td>
<td>Human-readable name shown in the <code>label</code> field</td>
</tr>
<tr>
<td><code>proxyParam</code></td>
<td>Short query parameter used to route proxied requests</td>
</tr>
<tr>
<td><code>timeout</code></td>
<td>Maximum time (ms) to wait for a stream URL</td>
</tr>
<tr>
<td><code>sourcesTimeout</code></td>
<td>Optional secondary timeout (ms) for an internal source-listing step, used by some providers</td>
</tr>
<tr>
<td><code>jitter</code></td>
<td>Max random delay (ms) before the request fires, to stagger upstream hits</td>
</tr>
<tr>
<td><code>retries</code></td>
<td>Number of retry attempts on failure</td>
</tr>
<tr>
<td><code>skipProxy</code></td>
<td>If <code>true</code>, stream URLs are returned as-is without proxy wrapping</td>
</tr>
<tr>
<td><code>disabled</code></td>
<td>If <code>true</code>, the provider is excluded from all requests</td>
</tr>
</tbody>
</table>
<p>Providers with <code>disabled: true</code> are completely skipped — they won't appear in health checks or
fanout responses.</p>
<hr>
<h2 id="response-time">Response Time</h2>
<p>The first <code>source</code> event typically arrives within <strong>3–8 seconds</strong>. Subsequent sources
stream in as each provider resolves. The stream closes once the <strong>slowest configured provider</strong>
times out — in the worst case this can be 60+ seconds (e.g. <code>cinezo</code> has a 60 s timeout), though
most sources emit well before then.</p>
<p><strong>Best practice:</strong> always show a loading state, begin playback on the first <code>source</code>
event, and implement a client-side timeout for the overall stream. See the <a
href="/guides/error-handling">Error Handling</a> guide.</p>
<hr>
<h2 id="caching">Caching</h2>
<p>Results are cached in-memory with a <strong>5-minute TTL</strong>. The per-source stream cache is keyed by
<code>source_key + tmdb_id + season + episode</code>; the aggregate source-results cache is keyed by
<code>tmdb_id + season + episode + base</code>. Cache hits skip the provider fetch entirely and return
near-instantly.
</p>
<pre><code>CACHE_TTL = 5 minutes
</code></pre>
<p>The cache resets on server restart. Cache size is reported in the <code>/api/health</code> response under the
<code>cache</code> field.
</p>
<hr>
<h2 id="the-hls-proxy">The HLS Proxy</h2>
<p>All provider stream URLs are wrapped in the server proxy (unless <code>skipProxy: true</code>):</p>
<pre><code>http://localhost:7860/api?url=&#x3C;encoded_stream_url>&#x26;&#x3C;proxyParam>=1
</code></pre>
<p>When your player fetches this path from your self-hosted instance, the server:</p>
<ol>
<li>Fetches the upstream M3U8 with the correct <code>Referer</code>, <code>Origin</code>, and any
provider-specific headers</li>
<li>Rewrites all segment URLs and encryption key URIs in the playlist to route through the same proxy</li>
<li>Returns the rewritten playlist with <code>Access-Control-Allow-Origin: *</code></li>
</ol>
<p>This means <strong>your player never talks to the provider directly</strong> — all HLS traffic flows through
the proxy, keeping CORS and rate-limiting issues fully contained.</p>
<hr>
<h2 id="stream-verification">Stream Verification</h2>
<p>Before a URL is emitted as a <code>source</code> event, it passes two verification steps:</p>
<ol>
<li><strong><code>verifyStream</code></strong> — a HEAD request to the raw upstream URL (skipped for
<code>SKIP_VERIFY</code> sources). Must return HTTP <code>&#x3C; 400</code>.
</li>
<li><strong><code>verifyPlayable</code></strong> — fetches the proxied URL, validates <code>#EXTM3U</code>,
checks for <code>#EXTINF</code> or <code>#EXT-X-STREAM-INF</code>, then fetches the first segment with a
range request.</li>
</ol>
<p>Sources that fail either step — HTTP errors, timeouts, non-HLS/non-MP4 content — are silently dropped. MP4
sources skip the <code>#EXTM3U</code> check and are verified via HEAD + content-type inspection instead. HTTP
429 responses and timeout/abort errors are treated as <strong>passing</strong> (stream exists but is
rate-limited).</p>
<div class="callout callout-note">
<div class="callout-content">
<p><code>/test</code> runs the same two-step verification pipeline as <code>/movie</code> and
<code>/tv</code>. Check the <code>ok</code> field in the response body — HTTP status is always
<code>200</code> regardless of result.
</p>
</div>
</div>
<hr>
<h2 id="disabled-providers">Disabled Providers</h2>
<p>Providers marked <code>disabled: true</code> in config are completely excluded from:</p>
<ul>
<li><code>/api/movie</code> and <code>/api/tv</code> fanout</li>
<li><code>/api/health</code> probes</li>
<li><code>GET /api?sources_meta</code> listings</li>
</ul>
<p>If a provider you expect to see is missing from responses, check whether it's been disabled.</p>
<hr>
<h2 id="multi-url-sources">Multi-URL Sources</h2>
<p>Some providers return multiple candidate stream URLs (via <code>allUrls</code>). For these, verification runs
through each candidate in order and returns the first one that passes. If none pass, the source is dropped.
</p>
<p>For providers with <code>SKIP_VERIFY</code>, all candidate URLs are returned as separate source events
without verification. These are not distinguished by numbered labels in the response.</p>
</main>
<aside id="toc">
<p class="toc-title">On this page</p>
<ul>
<li><a href="#how-providers-are-used">How Providers Are Used</a></li>
<li><a href="#provider-configuration">Provider Configuration</a></li>
<li><a href="#response-time">Response Time</a></li>
<li><a href="#caching">Caching</a></li>
<li><a href="#the-hls-proxy">The HLS Proxy</a></li>
<li><a href="#stream-verification">Stream Verification</a></li>
<li><a href="#disabled-providers">Disabled Providers</a></li>
<li><a href="#multi-url-sources">Multi-URL Sources</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,152 +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>Vyla Go — Vyla</title>
<meta name="description" content="The Android control center for running the Vyla media stack locally." />
<meta name="keywords"
content="Vyla Go, Android media player, mobile streaming, self-hosted media, Android app, media streaming" />
<link rel="canonical" href="https://vyla.cc/go.html" />
<!-- Open Graph / Facebook -->
<meta property="og:type" content="website" />
<meta property="og:url" content="https://vyla.cc/go.html" />
<meta property="og:title" content="Vyla Go — Vyla" />
<meta property="og:description" content="The Android control center for running the Vyla media stack locally." />
<meta property="og:image" content="https://vyla.cc/assets/images/android-preview.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/go.html" />
<meta property="twitter:title" content="Vyla Go — Vyla" />
<meta property="twitter:description" content="The Android control center for running the Vyla media stack locally." />
<meta property="twitter:image" content="https://vyla.cc/assets/images/android-preview.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": "Vyla Go",
"description": "The Android control center for running the Vyla media stack locally.",
"url": "https://vyla.cc/go.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">Start here</p>
<ul>
<li><a class="active" href="/">Introduction</a></li>
</li>
<li><a class="" href="/architecture">Architecture</a></li>
</ul>
</div>
<div class="group">
<p class="group-title">Products</p>
<ul>
<li><a class="" href="/home">Home</a></li>
<li><a class="active" href="/go">Go</a></li>
<li><a class="" href="/tv">Tv</a></li>
<li><a class="" href="/player">Player</a></li>
</ul>
</div>
<div class="group">
<p class="group-title">Concepts</p>
<ul>
<li><a class="" href="/concepts/providers">Providers</a></li>
</ul>
</div>
</nav>
<div id="content-col">
<main class="prose">
<h1>Vyla Go</h1>
<p class="page-description">The Android control center for running the Vyla media stack locally.</p>
<img class="vyla-banner" src="/assets/images/android/site_preview.png"
alt="A collage of films and shows available through a Vyla experience" loading="lazy" fetchpriority="low">
<p><strong>Vyla Go</strong> is the Android application in the Vyla ecosystem. It prepares and runs the local
services that power your media experience, then opens the interface in a secure embedded window. It is
designed for people who want Vyla on their Android device without managing a separate server process or
service lifecycle by hand.</p>
<h2 id="downloads">Downloads</h2>
<p>Vyla Go is available for Android. Get the latest version below:</p>
<table>
<thead>
<tr>
<th>Platform</th>
<th>Download</th>
</tr>
</thead>
<tbody>
<tr>
<td>Android</td>
<td><a href="https://gitlab.com/vyla-entertainment/go/-/releases">Download .apk</a></td>
</tr>
</tbody>
</table>
<p>Inside the releases page, you can find the latest version of Vyla Go for Android, find the <code>.apk</code>
file, and install it on your device.</p>
</main>
<aside id="toc">
<p class="toc-title">On this page</p>
<ul>
<li><a href="#downloads">Downloads</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,432 +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 error handling — Vyla</title>
<meta name="description"
content="Handle SSE failures, source fallbacks, and edge cases in an optional Stream API client." />
<meta name="keywords"
content="error handling, fallbacks, SSE errors, streaming errors, retry logic, error boundary, source fallback" />
<link rel="canonical" href="https://vyla.cc/guides/error-handling.html" />
<!-- Open Graph / Facebook -->
<meta property="og:type" content="website" />
<meta property="og:url" content="https://vyla.cc/guides/error-handling.html" />
<meta property="og:title" content="Stream API error handling — Vyla" />
<meta property="og:description"
content="Handle SSE failures, source fallbacks, and edge cases in an optional Stream API client." />
<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/guides/error-handling.html" />
<meta property="twitter:title" content="Stream API error handling — Vyla" />
<meta property="twitter:description"
content="Handle SSE failures, source fallbacks, and edge cases in an optional Stream API client." />
<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 error handling",
"description": "Handle SSE failures, source fallbacks, and edge cases in an optional Stream API client.",
"url": "https://vyla.cc/guides/error-handling.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">Guides</p>
<ul>
<li><a class="" href="/guides/integrations">Integrations</a></li>
<li><a class="active" href="/guides/error-handling">Error Handling</a></li>
</ul>
</div>
</nav>
<div id="content-col">
<main class="prose">
<h1>Stream API error handling</h1>
<p class="page-description">Handle SSE failures, source fallbacks, and edge cases in an optional Stream API
client.</p>
<p>The <code>/movie</code> and <code>/tv</code> endpoints stream via SSE — error handling looks different from a
standard JSON API. This page covers every failure mode and the recommended pattern for each.</p>
<div class="callout callout-note">
<div class="callout-content">
<p>These patterns apply when you choose the optional Stream API boundary. SDK integrations should apply the
same fallback principles inside their own server-side request flow.</p>
</div>
</div>
<hr>
<h2 id="error-response-shape">Error Response Shape</h2>
<p>Non-streaming endpoints (<code>/health</code>, <code>/subtitles</code>, <code>/downloads</code>,
<code>/test</code>) return standard JSON errors:
</p>
<pre><code class="hljs language-json"><span class="hljs-punctuation">{</span> <span class="hljs-attr">"error"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"description of what went wrong"</span> <span class="hljs-punctuation">}</span>
</code></pre>
<table>
<thead>
<tr>
<th>HTTP Status</th>
<th>When</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>400</code></td>
<td>Missing or invalid request parameters</td>
</tr>
<tr>
<td><code>404</code></td>
<td>No data found (subtitles, downloads)</td>
</tr>
<tr>
<td><code>500</code></td>
<td>Unexpected server error</td>
</tr>
</tbody>
</table>
<p>For <code>/movie</code> and <code>/tv</code>, HTTP <code>200</code> is returned immediately when the SSE
stream opens. Failures surface as <strong>zero <code>source</code> events</strong> followed by a
<code>done</code> event with <code>total: 0</code> — not as HTTP error codes.
</p>
<hr>
<h2 id="handling-no-sources">Handling No Sources</h2>
<p>The most common failure — all providers failed to return a playable stream. Detect it by checking
<code>total</code> in the <code>done</code> event.
</p>
<pre><code class="hljs language-typescript"><span class="hljs-keyword">const</span> <span class="hljs-variable constant_">BASE</span> = <span class="hljs-string">'http://localhost:7860'</span>;
<span class="hljs-keyword">const</span> res = <span class="hljs-keyword">await</span> <span class="hljs-title function_">fetch</span>(<span class="hljs-string">`<span class="hljs-subst">${BASE}</span>/movie?id=550`</span>);
<span class="hljs-keyword">const</span> reader = res.<span class="hljs-property">body</span>!.<span class="hljs-title function_">getReader</span>();
<span class="hljs-keyword">const</span> decoder = <span class="hljs-keyword">new</span> <span class="hljs-title class_">TextDecoder</span>();
<span class="hljs-keyword">let</span> buffer = <span class="hljs-string">''</span>;
<span class="hljs-keyword">let</span> started = <span class="hljs-literal">false</span>;
<span class="hljs-keyword">while</span> (<span class="hljs-literal">true</span>) {
<span class="hljs-keyword">const</span> { done, value } = <span class="hljs-keyword">await</span> reader.<span class="hljs-title function_">read</span>();
<span class="hljs-keyword">if</span> (done) <span class="hljs-keyword">break</span>;
buffer += decoder.<span class="hljs-title function_">decode</span>(value, { <span class="hljs-attr">stream</span>: <span class="hljs-literal">true</span> });
<span class="hljs-keyword">const</span> lines = buffer.<span class="hljs-title function_">split</span>(<span class="hljs-string">'\n'</span>);
buffer = lines.<span class="hljs-title function_">pop</span>()!;
<span class="hljs-keyword">for</span> (<span class="hljs-keyword">const</span> line <span class="hljs-keyword">of</span> lines) {
<span class="hljs-keyword">if</span> (!line.<span class="hljs-title function_">startsWith</span>(<span class="hljs-string">'data: '</span>)) <span class="hljs-keyword">continue</span>;
<span class="hljs-keyword">const</span> event = <span class="hljs-title class_">JSON</span>.<span class="hljs-title function_">parse</span>(line.<span class="hljs-title function_">slice</span>(<span class="hljs-number">6</span>));
<span class="hljs-keyword">if</span> (event.<span class="hljs-property">type</span> === <span class="hljs-string">'source'</span>) { started = <span class="hljs-literal">true</span>; <span class="hljs-title function_">playSource</span>(event.<span class="hljs-property">source</span>.<span class="hljs-property">url</span>); }
<span class="hljs-keyword">if</span> (event.<span class="hljs-property">type</span> === <span class="hljs-string">'done'</span> &#x26;&#x26; !started) {
<span class="hljs-title function_">showError</span>(<span class="hljs-string">'This title is currently unavailable. Try again later.'</span>);
}
}
}
</code></pre>
<hr>
<h2 id="source-fallback-queue">Source Fallback Queue</h2>
<p>Sources stream in as providers resolve. Queue every <code>source</code> event URL and fall back to the next
one if playback fails mid-stream:</p>
<pre><code class="hljs language-typescript"><span class="hljs-keyword">import</span> <span class="hljs-title class_">Hls</span> <span class="hljs-keyword">from</span> <span class="hljs-string">'hls.js'</span>;
<span class="hljs-keyword">const</span> <span class="hljs-variable constant_">BASE</span> = <span class="hljs-string">'http://localhost:7860'</span>;
<span class="hljs-keyword">async</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">playWithFallback</span>(<span class="hljs-params">
<span class="hljs-attr">videoEl</span>: <span class="hljs-title class_">HTMLVideoElement</span>,
<span class="hljs-attr">tmdbId</span>: <span class="hljs-built_in">number</span>,
<span class="hljs-attr">season</span>?: <span class="hljs-built_in">number</span>,
<span class="hljs-attr">episode</span>?: <span class="hljs-built_in">number</span>,
<span class="hljs-attr">onSourceChange</span>?: (label: <span class="hljs-built_in">string</span>) => <span class="hljs-built_in">void</span>
</span>) {
<span class="hljs-keyword">const</span> endpoint = season &#x26;&#x26; episode
? <span class="hljs-string">`<span class="hljs-subst">${BASE}</span>/tv?id=<span class="hljs-subst">${tmdbId}</span>&#x26;season=<span class="hljs-subst">${season}</span>&#x26;episode=<span class="hljs-subst">${episode}</span>`</span>
: <span class="hljs-string">`<span class="hljs-subst">${BASE}</span>/movie?id=<span class="hljs-subst">${tmdbId}</span>`</span>;
<span class="hljs-keyword">const</span> <span class="hljs-attr">queue</span>: { <span class="hljs-attr">url</span>: <span class="hljs-built_in">string</span>; <span class="hljs-attr">label</span>: <span class="hljs-built_in">string</span> }[] = [];
<span class="hljs-keyword">let</span> started = <span class="hljs-literal">false</span>;
<span class="hljs-keyword">let</span> <span class="hljs-attr">hls</span>: <span class="hljs-title class_">Hls</span> | <span class="hljs-literal">null</span> = <span class="hljs-literal">null</span>;
<span class="hljs-keyword">function</span> <span class="hljs-title function_">tryNext</span>(<span class="hljs-params"></span>) {
<span class="hljs-keyword">if</span> (!queue.<span class="hljs-property">length</span>) {
<span class="hljs-variable language_">console</span>.<span class="hljs-title function_">error</span>(<span class="hljs-string">'All sources exhausted'</span>);
<span class="hljs-keyword">return</span>;
}
<span class="hljs-keyword">const</span> { url, label } = queue.<span class="hljs-title function_">shift</span>()!;
onSourceChange?.(label);
hls?.<span class="hljs-title function_">destroy</span>();
hls = <span class="hljs-keyword">new</span> <span class="hljs-title class_">Hls</span>({ <span class="hljs-attr">maxBufferLength</span>: <span class="hljs-number">30</span>, <span class="hljs-attr">maxMaxBufferLength</span>: <span class="hljs-number">60</span> });
hls.<span class="hljs-title function_">on</span>(<span class="hljs-title class_">Hls</span>.<span class="hljs-property">Events</span>.<span class="hljs-property">ERROR</span>, <span class="hljs-function">(<span class="hljs-params">_, err</span>) =></span> {
<span class="hljs-keyword">if</span> (err.<span class="hljs-property">fatal</span>) {
<span class="hljs-variable language_">console</span>.<span class="hljs-title function_">warn</span>(<span class="hljs-string">`Source "<span class="hljs-subst">${label}</span>" failed:`</span>, err.<span class="hljs-property">type</span>);
<span class="hljs-title function_">tryNext</span>();
}
});
hls.<span class="hljs-title function_">loadSource</span>(url);
hls.<span class="hljs-title function_">attachMedia</span>(videoEl);
hls.<span class="hljs-title function_">on</span>(<span class="hljs-title class_">Hls</span>.<span class="hljs-property">Events</span>.<span class="hljs-property">MANIFEST_PARSED</span>, <span class="hljs-function">() =></span> videoEl.<span class="hljs-title function_">play</span>());
}
<span class="hljs-keyword">const</span> res = <span class="hljs-keyword">await</span> <span class="hljs-title function_">fetch</span>(endpoint);
<span class="hljs-keyword">const</span> reader = res.<span class="hljs-property">body</span>!.<span class="hljs-title function_">getReader</span>();
<span class="hljs-keyword">const</span> decoder = <span class="hljs-keyword">new</span> <span class="hljs-title class_">TextDecoder</span>();
<span class="hljs-keyword">let</span> buffer = <span class="hljs-string">''</span>;
<span class="hljs-keyword">while</span> (<span class="hljs-literal">true</span>) {
<span class="hljs-keyword">const</span> { done, value } = <span class="hljs-keyword">await</span> reader.<span class="hljs-title function_">read</span>();
<span class="hljs-keyword">if</span> (done) <span class="hljs-keyword">break</span>;
buffer += decoder.<span class="hljs-title function_">decode</span>(value, { <span class="hljs-attr">stream</span>: <span class="hljs-literal">true</span> });
<span class="hljs-keyword">const</span> lines = buffer.<span class="hljs-title function_">split</span>(<span class="hljs-string">'\n'</span>);
buffer = lines.<span class="hljs-title function_">pop</span>()!;
<span class="hljs-keyword">for</span> (<span class="hljs-keyword">const</span> line <span class="hljs-keyword">of</span> lines) {
<span class="hljs-keyword">if</span> (!line.<span class="hljs-title function_">startsWith</span>(<span class="hljs-string">'data: '</span>)) <span class="hljs-keyword">continue</span>;
<span class="hljs-keyword">const</span> event = <span class="hljs-title class_">JSON</span>.<span class="hljs-title function_">parse</span>(line.<span class="hljs-title function_">slice</span>(<span class="hljs-number">6</span>));
<span class="hljs-keyword">if</span> (event.<span class="hljs-property">type</span> === <span class="hljs-string">'source'</span>) {
queue.<span class="hljs-title function_">push</span>({ <span class="hljs-attr">url</span>: event.<span class="hljs-property">source</span>.<span class="hljs-property">url</span>, <span class="hljs-attr">label</span>: event.<span class="hljs-property">source</span>.<span class="hljs-property">label</span> });
<span class="hljs-keyword">if</span> (!started) { started = <span class="hljs-literal">true</span>; <span class="hljs-title function_">tryNext</span>(); }
}
}
}
<span class="hljs-keyword">return</span> <span class="hljs-function">() =></span> hls?.<span class="hljs-title function_">destroy</span>();
}
</code></pre>
<hr>
<h2 id="network-timeouts">Network Timeouts</h2>
<p>The SSE stream can stay open 20+ seconds while slow providers resolve. Set a client-side timeout using
<code>AbortController</code>:
</p>
<pre><code class="hljs language-typescript"><span class="hljs-keyword">async</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">fetchStreamWithTimeout</span>(<span class="hljs-params">
<span class="hljs-attr">url</span>: <span class="hljs-built_in">string</span>,
timeoutMs = <span class="hljs-number">20000</span>
</span>): <span class="hljs-title class_">Promise</span>&#x3C;<span class="hljs-title class_">ReadableStreamDefaultReader</span>&#x3C;<span class="hljs-title class_">Uint8Array</span>>> {
<span class="hljs-keyword">const</span> controller = <span class="hljs-keyword">new</span> <span class="hljs-title class_">AbortController</span>();
<span class="hljs-keyword">const</span> timer = <span class="hljs-built_in">setTimeout</span>(<span class="hljs-function">() =></span> controller.<span class="hljs-title function_">abort</span>(), timeoutMs);
<span class="hljs-keyword">try</span> {
<span class="hljs-keyword">const</span> res = <span class="hljs-keyword">await</span> <span class="hljs-title function_">fetch</span>(url, {
<span class="hljs-attr">signal</span>: controller.<span class="hljs-property">signal</span>
});
<span class="hljs-built_in">clearTimeout</span>(timer);
<span class="hljs-keyword">return</span> res.<span class="hljs-property">body</span>!.<span class="hljs-title function_">getReader</span>();
} <span class="hljs-keyword">catch</span> (<span class="hljs-attr">err</span>: <span class="hljs-built_in">any</span>) {
<span class="hljs-built_in">clearTimeout</span>(timer);
<span class="hljs-keyword">if</span> (err.<span class="hljs-property">name</span> === <span class="hljs-string">'AbortError'</span>) <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Error</span>(<span class="hljs-string">'Stream timed out'</span>);
<span class="hljs-keyword">throw</span> err;
}
}
</code></pre>
<hr>
<h2 id="handling-missing-subtitles">Handling Missing Subtitles</h2>
<p>Subtitles arrive in the <code>meta</code> event. Always guard against an empty array:</p>
<pre><code class="hljs language-typescript"><span class="hljs-keyword">if</span> (event.<span class="hljs-property">type</span> === <span class="hljs-string">'meta'</span>) {
<span class="hljs-keyword">if</span> (!event.<span class="hljs-property">subtitles</span>.<span class="hljs-property">length</span>) {
subtitleButton.<span class="hljs-property">style</span>.<span class="hljs-property">display</span> = <span class="hljs-string">'none'</span>;
} <span class="hljs-keyword">else</span> {
<span class="hljs-title function_">attachSubtitles</span>(videoEl, event.<span class="hljs-property">subtitles</span>);
}
}
</code></pre>
<hr>
<h2 id="retry-logic-for-stream-failures">Retry Logic for Stream Failures</h2>
<p>Add a top-level retry for complete stream failures (network errors, server 5xx before the stream opens):</p>
<pre><code class="hljs language-typescript"><span class="hljs-keyword">async</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">fetchStreamWithRetry</span>(<span class="hljs-params">
<span class="hljs-attr">url</span>: <span class="hljs-built_in">string</span>,
attempts = <span class="hljs-number">3</span>,
delayMs = <span class="hljs-number">2000</span>
</span>): <span class="hljs-title class_">Promise</span>&#x3C;<span class="hljs-title class_">Response</span>> {
<span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> i = <span class="hljs-number">0</span>; i &#x3C; attempts; i++) {
<span class="hljs-keyword">try</span> {
<span class="hljs-keyword">const</span> res = <span class="hljs-keyword">await</span> <span class="hljs-title function_">fetch</span>(url);
<span class="hljs-keyword">if</span> (res.<span class="hljs-property">ok</span>) <span class="hljs-keyword">return</span> res;
<span class="hljs-keyword">if</span> (i === attempts - <span class="hljs-number">1</span>) <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Error</span>(<span class="hljs-string">`HTTP <span class="hljs-subst">${res.status}</span>`</span>);
} <span class="hljs-keyword">catch</span> (err) {
<span class="hljs-keyword">if</span> (i === attempts - <span class="hljs-number">1</span>) <span class="hljs-keyword">throw</span> err;
<span class="hljs-keyword">await</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Promise</span>(<span class="hljs-function"><span class="hljs-params">r</span> =></span> <span class="hljs-built_in">setTimeout</span>(r, delayMs * (i + <span class="hljs-number">1</span>)));
}
}
<span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Error</span>(<span class="hljs-string">'Max retries exceeded'</span>);
}
</code></pre>
<hr>
<h2 id="checking-health-before-requests">Checking Health Before Requests</h2>
<p>If you're building a dashboard, check provider health before opening a stream to give users an early warning:
</p>
<pre><code class="hljs language-typescript"><span class="hljs-keyword">async</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">checkAndStream</span>(<span class="hljs-params"><span class="hljs-attr">tmdbId</span>: <span class="hljs-built_in">number</span></span>) {
<span class="hljs-keyword">const</span> health = <span class="hljs-keyword">await</span> <span class="hljs-title function_">fetch</span>(
<span class="hljs-string">'http://localhost:7860/api/health'</span>
).<span class="hljs-title function_">then</span>(<span class="hljs-function"><span class="hljs-params">r</span> =></span> r.<span class="hljs-title function_">json</span>());
<span class="hljs-keyword">if</span> (health.<span class="hljs-property">status</span> === <span class="hljs-string">'degraded'</span>) {
<span class="hljs-keyword">const</span> down = <span class="hljs-title class_">Object</span>.<span class="hljs-title function_">entries</span>(health.<span class="hljs-property">sources</span>)
.<span class="hljs-title function_">filter</span>(<span class="hljs-function">(<span class="hljs-params">[, v]: <span class="hljs-built_in">any</span></span>) =></span> !v.<span class="hljs-property">ok</span>)
.<span class="hljs-title function_">map</span>(<span class="hljs-function">(<span class="hljs-params">[k]</span>) =></span> k);
<span class="hljs-variable language_">console</span>.<span class="hljs-title function_">warn</span>(<span class="hljs-string">`Degraded providers: <span class="hljs-subst">${down.length}</span>`</span>);
}
<span class="hljs-keyword">return</span> <span class="hljs-title function_">fetch</span>(
<span class="hljs-string">`http://localhost:7860/movie?id=<span class="hljs-subst">${tmdbId}</span>`</span>
);
}
</code></pre>
<hr>
<h2 id="full-error-boundary-react">Full Error Boundary (React)</h2>
<pre><code class="hljs language-tsx"><span class="hljs-keyword">import</span> { useState, useCallback, useRef } <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-title class_">Hls</span> <span class="hljs-keyword">from</span> <span class="hljs-string">'hls.js'</span>;
<span class="hljs-keyword">const</span> <span class="hljs-variable constant_">BASE</span> = <span class="hljs-string">'http://localhost:7860'</span>;
<span class="hljs-keyword">type</span> <span class="hljs-title class_">PlayerState</span> =
| { <span class="hljs-attr">status</span>: <span class="hljs-string">'idle'</span> }
| { <span class="hljs-attr">status</span>: <span class="hljs-string">'loading'</span> }
| { <span class="hljs-attr">status</span>: <span class="hljs-string">'playing'</span>; <span class="hljs-attr">sourceLabel</span>: <span class="hljs-built_in">string</span> }
| { <span class="hljs-attr">status</span>: <span class="hljs-string">'error'</span>; <span class="hljs-attr">message</span>: <span class="hljs-built_in">string</span> };
<span class="hljs-keyword">export</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">useStreamPlayer</span>(<span class="hljs-params"><span class="hljs-attr">tmdbId</span>: <span class="hljs-built_in">number</span></span>) {
<span class="hljs-keyword">const</span> [state, setState] = useState&#x3C;<span class="hljs-title class_">PlayerState</span>>({ <span class="hljs-attr">status</span>: <span class="hljs-string">'idle'</span> });
<span class="hljs-keyword">const</span> hlsRef = useRef&#x3C;<span class="hljs-title class_">Hls</span> | <span class="hljs-literal">null</span>>(<span class="hljs-literal">null</span>);
<span class="hljs-keyword">const</span> load = <span class="hljs-title function_">useCallback</span>(<span class="hljs-title function_">async</span> (<span class="hljs-attr">videoEl</span>: <span class="hljs-title class_">HTMLVideoElement</span>) => {
<span class="hljs-title function_">setState</span>({ <span class="hljs-attr">status</span>: <span class="hljs-string">'loading'</span> });
<span class="hljs-keyword">const</span> <span class="hljs-attr">queue</span>: { <span class="hljs-attr">url</span>: <span class="hljs-built_in">string</span>; <span class="hljs-attr">label</span>: <span class="hljs-built_in">string</span> }[] = [];
<span class="hljs-keyword">let</span> started = <span class="hljs-literal">false</span>;
<span class="hljs-keyword">function</span> <span class="hljs-title function_">tryNext</span>(<span class="hljs-params"></span>) {
<span class="hljs-keyword">if</span> (!queue.<span class="hljs-property">length</span>) {
<span class="hljs-title function_">setState</span>({ <span class="hljs-attr">status</span>: <span class="hljs-string">'error'</span>, <span class="hljs-attr">message</span>: <span class="hljs-string">'All sources failed.'</span> });
<span class="hljs-keyword">return</span>;
}
<span class="hljs-keyword">const</span> { url, label } = queue.<span class="hljs-title function_">shift</span>()!;
hlsRef.<span class="hljs-property">current</span>?.<span class="hljs-title function_">destroy</span>();
<span class="hljs-keyword">const</span> hls = <span class="hljs-keyword">new</span> <span class="hljs-title class_">Hls</span>();
hlsRef.<span class="hljs-property">current</span> = hls;
hls.<span class="hljs-title function_">on</span>(<span class="hljs-title class_">Hls</span>.<span class="hljs-property">Events</span>.<span class="hljs-property">MANIFEST_PARSED</span>, <span class="hljs-function">() =></span> {
<span class="hljs-title function_">setState</span>({ <span class="hljs-attr">status</span>: <span class="hljs-string">'playing'</span>, <span class="hljs-attr">sourceLabel</span>: label });
videoEl.<span class="hljs-title function_">play</span>();
});
hls.<span class="hljs-title function_">on</span>(<span class="hljs-title class_">Hls</span>.<span class="hljs-property">Events</span>.<span class="hljs-property">ERROR</span>, <span class="hljs-function">(<span class="hljs-params">_, err</span>) =></span> {
<span class="hljs-keyword">if</span> (err.<span class="hljs-property">fatal</span>) { hls.<span class="hljs-title function_">destroy</span>(); <span class="hljs-title function_">tryNext</span>(); }
});
hls.<span class="hljs-title function_">loadSource</span>(url);
hls.<span class="hljs-title function_">attachMedia</span>(videoEl);
}
<span class="hljs-keyword">try</span> {
<span class="hljs-keyword">const</span> res = <span class="hljs-keyword">await</span> <span class="hljs-title function_">fetch</span>(<span class="hljs-string">`<span class="hljs-subst">${BASE}</span>/movie?id=<span class="hljs-subst">${tmdbId}</span>`</span>);
<span class="hljs-keyword">const</span> reader = res.<span class="hljs-property">body</span>!.<span class="hljs-title function_">getReader</span>();
<span class="hljs-keyword">const</span> decoder = <span class="hljs-keyword">new</span> <span class="hljs-title class_">TextDecoder</span>();
<span class="hljs-keyword">let</span> buffer = <span class="hljs-string">''</span>;
<span class="hljs-keyword">while</span> (<span class="hljs-literal">true</span>) {
<span class="hljs-keyword">const</span> { done, value } = <span class="hljs-keyword">await</span> reader.<span class="hljs-title function_">read</span>();
<span class="hljs-keyword">if</span> (done) <span class="hljs-keyword">break</span>;
buffer += decoder.<span class="hljs-title function_">decode</span>(value, { <span class="hljs-attr">stream</span>: <span class="hljs-literal">true</span> });
<span class="hljs-keyword">const</span> lines = buffer.<span class="hljs-title function_">split</span>(<span class="hljs-string">'\n'</span>);
buffer = lines.<span class="hljs-title function_">pop</span>()!;
<span class="hljs-keyword">for</span> (<span class="hljs-keyword">const</span> line <span class="hljs-keyword">of</span> lines) {
<span class="hljs-keyword">if</span> (!line.<span class="hljs-title function_">startsWith</span>(<span class="hljs-string">'data: '</span>)) <span class="hljs-keyword">continue</span>;
<span class="hljs-keyword">const</span> event = <span class="hljs-title class_">JSON</span>.<span class="hljs-title function_">parse</span>(line.<span class="hljs-title function_">slice</span>(<span class="hljs-number">6</span>));
<span class="hljs-keyword">if</span> (event.<span class="hljs-property">type</span> === <span class="hljs-string">'source'</span>) {
queue.<span class="hljs-title function_">push</span>({ <span class="hljs-attr">url</span>: event.<span class="hljs-property">source</span>.<span class="hljs-property">url</span>, <span class="hljs-attr">label</span>: event.<span class="hljs-property">source</span>.<span class="hljs-property">label</span> });
<span class="hljs-keyword">if</span> (!started) { started = <span class="hljs-literal">true</span>; <span class="hljs-title function_">tryNext</span>(); }
}
<span class="hljs-keyword">if</span> (event.<span class="hljs-property">type</span> === <span class="hljs-string">'done'</span> &#x26;&#x26; !started) {
<span class="hljs-title function_">setState</span>({ <span class="hljs-attr">status</span>: <span class="hljs-string">'error'</span>, <span class="hljs-attr">message</span>: <span class="hljs-string">'No sources available for this title.'</span> });
}
}
}
} <span class="hljs-keyword">catch</span> {
<span class="hljs-title function_">setState</span>({ <span class="hljs-attr">status</span>: <span class="hljs-string">'error'</span>, <span class="hljs-attr">message</span>: <span class="hljs-string">'Network error. Check your connection.'</span> });
}
}, [tmdbId]);
<span class="hljs-keyword">return</span> { state, load };
}
</code></pre>
</main>
<aside id="toc">
<p class="toc-title">On this page</p>
<ul>
<li><a href="#error-response-shape">Error Response Shape</a></li>
<li><a href="#handling-no-sources">Handling No Sources</a></li>
<li><a href="#source-fallback-queue">Source Fallback Queue</a></li>
<li><a href="#network-timeouts">Network Timeouts</a></li>
<li><a href="#handling-missing-subtitles">Handling Missing Subtitles</a></li>
<li><a href="#retry-logic-for-stream-failures">Retry Logic for Stream Failures</a></li>
<li><a href="#checking-health-before-requests">Checking Health Before Requests</a></li>
<li><a href="#full-error-boundary-react">Full Error Boundary (React)</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>

File diff suppressed because it is too large Load Diff

View File

@@ -1,202 +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>Vyla Home — Vyla</title>
<meta name="description" content="The desktop control center for running the Vyla media stack locally." />
<meta name="keywords"
content="Vyla Home, desktop media player, Windows, macOS, Linux, self-hosted media, local streaming" />
<link rel="canonical" href="https://vyla.cc/home.html" />
<!-- Open Graph / Facebook -->
<meta property="og:type" content="website" />
<meta property="og:url" content="https://vyla.cc/home.html" />
<meta property="og:title" content="Vyla Home — Vyla" />
<meta property="og:description" content="The desktop control center for running the Vyla media stack locally." />
<meta property="og:image" content="https://vyla.cc/assets/images/desktop-preview.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/home.html" />
<meta property="twitter:title" content="Vyla Home — Vyla" />
<meta property="twitter:description"
content="The desktop control center for running the Vyla media stack locally." />
<meta property="twitter:image" content="https://vyla.cc/assets/images/desktop-preview.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": "Vyla Home",
"description": "The desktop control center for running the Vyla media stack locally.",
"url": "https://vyla.cc/home.html",
"applicationCategory": "MultimediaApplication",
"operatingSystem": "Windows, macOS, Linux",
"offers": {
"@type": "Offer",
"price": "0",
"priceCurrency": "USD"
}
}
</script>
<script>
(function () {
var saved = localStorage.getItem('vyla-theme');
var isDark = saved ? saved === 'dark' : true;
document.documentElement.classList.toggle('dark', isDark);
})();
</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"></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">Start here</p>
<ul>
<li><a class="active" href="/">Introduction</a></li>
</li>
<li><a class="" href="/architecture">Architecture</a></li>
</ul>
</div>
<div class="group">
<p class="group-title">Products</p>
<ul>
<li><a class="active" href="/home">Home</a></li>
<li><a class="" href="/go">Go</a></li>
<li><a class="" href="/tv">Tv</a></li>
<li><a class="" href="/player">Player</a></li>
</ul>
</div>
<div class="group">
<p class="group-title">Concepts</p>
<ul>
<li><a class="" href="/concepts/providers">Providers</a></li>
</ul>
</div>
</nav>
<div id="content-col">
<main class="prose">
<h1>Vyla Home</h1>
<p class="page-description">The desktop control center for running the Vyla media stack locally.</p>
<img class="vyla-banner" src="/assets/images/desktop/site_preview.png"
alt="Vyla Home desktop application preview" loading="lazy" fetchpriority="low">
<h2 id="vyla-home">Vyla Home</h2>
<p><strong>Vyla Home</strong> is the desktop application in the Vyla ecosystem. It prepares and runs the
local services that power your media experience, then opens the interface in a secure embedded
window. It is designed for people who want Vyla without managing a separate server process, reverse
proxy, or service lifecycle by hand.</p>
<h2 id="downloads">Downloads</h2>
<p>Vyla Home is available for Windows, macOS, and Linux. Get the latest version below:</p>
<table>
<thead>
<tr>
<th>Platform</th>
<th>Download</th>
</tr>
</thead>
<tbody>
<tr>
<td>Windows</td>
<td><a href="https://gitlab.com/vyla-entertainment/home/-/releases">Download .exe</a></td>
</tr>
<tr>
<td>macOS</td>
<td><a href="https://gitlab.com/vyla-entertainment/home/-/releases">Download .dmg</a></td>
</tr>
<tr>
<td>Linux</td>
<td><a href="#linux-setup">Build from source</a></td>
</tr>
</tbody>
</table>
<p>Inside the releases page, you can find the latest version of Vyla Home for each platform, find the
<code>.exe</code> for windows, and <code>.dmg</code> for macOS.
</p>
<h2 id="linux-setup">Linux Setup</h2>
<p>For Linux, you'll need to build Vyla Home from source. Follow these steps to install on Fedora:</p>
<p><strong>Step 1: Install dependencies</strong></p>
<pre><code class="hljs language-bash"><span class="hljs-built_in">sudo</span> dnf install webkit2gtk4.1-devel curl wget openssl-devel libappindicator-gtk3-devel librsvg2-devel
<span class="hljs-built_in">sudo</span> dnf install gcc gcc-c++ make automake autoconf
</code></pre>
<p><strong>Step 2: Install Node.js and NPM</strong></p>
<pre><code class="hljs language-bash"><span class="hljs-built_in">sudo</span> dnf install nodejs npm
</code></pre>
<p><strong>Step 3: Install Rust</strong></p>
<pre><code class="hljs language-bash">curl --proto <span class="hljs-string">'=https'</span> --tlsv1.2 -sSf https://sh.rustup.rs | sh
<span class="hljs-built_in">source</span> <span class="hljs-string">"<span class="hljs-variable">$HOME</span>/.cargo/env"</span>
</code></pre>
<p><strong>Step 4: Clone the Git repository</strong></p>
<pre><code class="hljs language-bash">git <span class="hljs-built_in">clone</span> https://gitlab.com/vyla-entertainment/home.git vyla-home
<span class="hljs-built_in">cd</span> vyla-home
</code></pre>
<p><strong>Step 5: Install more dependencies</strong></p>
<pre><code class="hljs language-bash">npm install
</code></pre>
<p><strong>Step 6: Build the Fedora package (.rpm)</strong></p>
<pre><code class="hljs language-bash">cargo update --manifest-path src-tauri/Cargo.toml
npx tauri build --bundles rpm,appimage
</code></pre>
<p><strong>Step 7: Final installation</strong></p>
<pre><code class="hljs language-bash"><span class="hljs-built_in">sudo</span> dnf install src-tauri/target/release/bundle/rpm/*.rpm
</code></pre>
<p><em>Thank you @krishi279 for supplying the Linux setup instructions on Discord ❤️.</em></p>
</main>
<aside id="toc">
<p class="toc-title">On this page</p>
<ul>
<li><a href="#vyla-home">Vyla Home</a></li>
<li><a href="#downloads">Downloads</a></li>
<li><a href="#linux-setup">Linux Setup</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

@@ -9,53 +9,12 @@
content="A community-built media project focused on freedom, privacy, and giving people control over their own experience." />
<meta name="keywords"
content="Vyla, media streaming, self-hosted, privacy, open source, media player, streaming API, SDK, Android TV, desktop media" />
<link rel="canonical" href="https://vyla.cc/" />
<!-- Open Graph / Facebook -->
<meta property="og:type" content="website" />
<meta property="og:url" content="https://vyla.cc/" />
<meta property="og:title" content="Vyla Entertainment — Vyla" />
<meta property="og:description"
content="A community-built media project focused on freedom, privacy, and giving people control over their own experience." />
<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/" />
<meta property="twitter:title" content="Vyla Entertainment — Vyla" />
<meta property="twitter:description"
content="A community-built media project focused on freedom, privacy, and giving people control over their own experience." />
<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": "Vyla",
"description": "A community-built media project focused on freedom, privacy, and giving people control over their own experience.",
"url": "https://vyla.cc/",
"applicationCategory": "MultimediaApplication",
"operatingSystem": "Windows, macOS, Linux, Android, Android TV",
"offers": {
"@type": "Offer",
"price": "0",
"priceCurrency": "USD"
}
}
</script>
<link rel="stylesheet" href="/assets/styling/main.css" />
<link rel="manifest" href="/manifest.webmanifest" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-core.min.js"></script>
<script
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');
@@ -66,176 +25,78 @@
</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"></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>
<video class="video-bg" src="" autoplay muted loop></video>
<div id="header-container">
<div id="banner">
<span>
Rights holders can request source removal — <a href="/misc/project/dmca">DMCA policy</a>.
</span>
<button id="banner-close" onclick="dismissBanner()" aria-label="Dismiss">✕</button>
</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">Start here</p>
<ul>
<li><a class="active" href="/">Introduction</a></li>
<li><a class="" href="/architecture">Architecture</a></li>
</ul>
</div>
<div class="group">
<p class="group-title">Products</p>
<ul>
<li><a class="" href="/home">Home</a></li>
<li><a class="" href="/go">Go</a></li>
<li><a class="" href="/tv">Tv</a></li>
<li><a class="" href="/player">Player</a></li>
</ul>
</div>
<div class="group">
<p class="group-title">Concepts</p>
<ul>
<li><a class="" href="/concepts/providers">Providers</a></li>
</ul>
</div>
</nav>
<div id="content-col">
<main class="prose">
<h1>Vyla Entertainment</h1>
<p class="page-description">A community-built media project focused on freedom, privacy, and giving
people
control over their own experience.</p>
<img class="vyla-banner" src="/assets/images/devices.png"
alt="A collage of films and shows available through a Vyla experience" loading="lazy"
fetchpriority="low">
<h2 id="about-vyla">About Vyla</h2>
<p>Vyla is a community-built media project created around a simple idea: <strong>people should have
control over
how they experience their media.</strong></p>
<p>We started Vyla because we wanted something clean, private, and easy to use without relying on a
single
company or centralized service. What began as a small project has grown into a collection of
applications and
tools for desktop, Android, Android TV, and more.</p>
<p>Vyla is not a streaming service, and we do not host or provide the media itself. Instead, we build
the
software that brings the experience together while leaving control in the hands of the person using
it.</p>
<h2 id="where-we-come-from">Where we come from</h2>
<p>Vyla started as a small web project focused on creating a better way to discover and watch movies and
shows.
Over time, it grew into a broader project with applications for different platforms alongside the
tools that
power them.</p>
<p>We care about keeping Vyla open, accessible, and community-driven. Our project has also been shared
by
communities such as <strong>FMHY</strong> and <strong>r/Piracy</strong>, where people interested in
alternative and self-hosted media solutions have discovered Vyla.</p>
<p>We are proud to be part of a wider community of people who build, share, and explore alternatives to
traditional media platforms.</p>
<h2 id="what-we-make">What we make</h2>
<p>Vyla is made up of several projects designed for different ways of using the platform.</p>
<table>
<thead>
<tr>
<th>Platform</th>
<th>Description</th>
<th>Link</th>
</tr>
</thead>
<tbody>
<tr>
<td>Vyla Home</td>
<td>Desktop app</td>
<td><a href="/home">Learn more</a></td>
</tr>
<tr>
<td>Vyla Go</td>
<td>Android app</td>
<td><a href="/go">Learn more</a></td>
</tr>
<tr>
<td>Vyla TV</td>
<td>Android TV app</td>
<td><a href="/tv">Learn more</a></td>
</tr>
<tr>
<td>Vyla SDK</td>
<td>Developer SDK</td>
<td><a href="/sdk">Learn more</a></td>
</tr>
<tr>
<td>Stream API</td>
<td>Self-hosted API</td>
<td><a href="/stream-api/overview">Learn more</a></td>
</tr>
</tbody>
</table>
<h2 id="made-for-people-not-platforms">Made for people, not platforms</h2>
<p>We believe software should give people more control, not less.</p>
<p>That means keeping Vyla local and self-hostable, avoiding unnecessary centralized dependencies, and
allowing
developers and users to decide how they want to use the project.</p>
<p>Vyla can be as simple as a TV or mobile app, a desktop application, or the foundation for a
completely custom
media experience. <strong>You decide how much of Vyla you use and where it runs.</strong></p>
<h2 id="start-exploring">Start exploring</h2>
<ul>
<li><strong>On desktop?</strong> See <a href="/home">Vyla Home</a>.</li>
<li><strong>On Android?</strong> See <a href="/go">Vyla Go</a>.</li>
<li><strong>Using Android TV?</strong> See <a href="/tv">Vyla TV</a>.</li>
<li><strong>Building something with Vyla?</strong> Start with the <a href="/sdk-quickstart">SDK
quickstart</a>.</li>
<li><strong>Building your own client?</strong> See the <a href="/stream-api/overview">Stream
API</a>.
</li>
<li><strong>Want to understand how everything fits together?</strong> Visit <a
href="/architecture">Our
Architecture</a>.</li>
</ul>
<div class="callout callout-info">
<div class="callout-content">
<p>Vyla is self-hosted software and does not host or distribute media. Please respect applicable
laws,
upstream terms, and rights-holder requests. See the <a href="/misc/dmca">DMCA policy</a> for
removal
information.</p>
</div>
<header id="navbar">
<div class="nav-top">
<a class="brand" href="/"><img src="/assets/images/logo.png" alt="Vyla" id="brand-logo" width="28"
height="28" decoding="async" /></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>
</main>
<aside id="toc">
<p class="toc-title">On this page</p>
<ul>
<li><a href="#about-vyla">About Vyla</a></li>
<li><a href="#where-we-come-from">Where we come from</a></li>
<li><a href="#what-we-make">What we make</a></li>
<li><a href="#made-for-people-not-platforms">Made for people, not platforms</a></li>
<li><a href="#start-exploring">Start exploring</a></li>
</ul>
</aside>
</div>
</div>
<nav class="tabs" id="nav-tabs"></nav>
</header>
</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>
<div id="layout">
<nav id="sidebar"></nav>
<div id="content-wrapper">
<div id="content-col">
<div id="main-wrap">
<main class="prose" id="main-content"></main>
<footer id="site-footer">
<div class="footer-nav">
<a id="footer-prev" class="footer-link prev" href="#"></a>
<a id="footer-next" class="footer-link next" href="#"></a>
</div>
<div class="footer-bottom">
<div class="footer-socials">
<a href="https://discord.gg/5eqRhKeywG" target="_blank" rel="noopener"
aria-label="Discord">
<svg class="footer-icon" viewBox="0 0 24 24" fill="currentColor">
<path
d="M20.317 4.37a19.791 19.791 0 0 0-4.885-1.515.074.074 0 0 0-.079.037c-.21.375-.444.864-.608 1.25a18.27 18.27 0 0 0-5.487 0 12.64 12.64 0 0 0-.617-1.25.077.077 0 0 0-.079-.037A19.736 19.736 0 0 0 3.677 4.37a.07.07 0 0 0-.032.027C.533 9.046-.32 13.58.099 18.057a.082.082 0 0 0 .031.057 19.9 19.9 0 0 0 5.993 3.03.078.078 0 0 0 .084-.028c.462-.63.874-1.295 1.226-1.994.021-.041.001-.09-.041-.106a13.107 13.107 0 0 1-1.872-.892.077.077 0 0 1-.008-.128 10.2 10.2 0 0 0 .372-.292.074.074 0 0 1 .077-.01c3.929 1.793 8.18 1.793 12.061 0a.074.074 0 0 1 .078.01c.12.098.246.198.373.292a.077.077 0 0 1-.006.127 12.299 12.299 0 0 1-1.873.892.077.077 0 0 0-.041.107c.36.698.772 1.362 1.225 1.993a.076.076 0 0 0 .084.028 19.839 19.839 0 0 0 6.002-3.03.077.077 0 0 0 .032-.054c.5-5.177-.838-9.674-3.549-13.66a.061.061 0 0 0-.031-.028zM8.02 15.33c-1.183 0-2.157-1.085-2.157-2.419 0-1.333.956-2.419 2.157-2.419 1.21 0 2.176 1.096 2.157 2.42 0 1.333-.956 2.418-2.157 2.418zm7.975 0c-1.183 0-2.157-1.085-2.157-2.419 0-1.333.955-2.419 2.157-2.419 1.21 0 2.176 1.096 2.157 2.42 0 1.333-.946 2.418-2.157 2.418z" />
</svg>
</a>
<a href="https://gitlab.com/vyla-entertainment/" target="_blank" rel="noopener"
aria-label="GitLab">
<svg class="footer-icon" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"
fill="currentColor">
<g id="SVGRepo_bgCarrier" stroke-width="0"></g>
<g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round">
</g>
<g id="SVGRepo_iconCarrier">
<path fill="currentColor"
d="M14.975 8.904L14.19 6.55l-1.552-4.67a.268.268 0 00-.255-.18.268.268 0 00-.254.18l-1.552 4.667H5.422L3.87 1.879a.267.267 0 00-.254-.179.267.267 0 00-.254.18l-1.55 4.667-.784 2.357a.515.515 0 00.193.583l6.78 4.812 6.778-4.812a.516.516 0 00.196-.583z">
</path>
</g>
</svg>
</a>
<a href="https://reddit.com" target="_blank" rel="noopener" aria-label="Reddit">
<svg class="footer-icon" viewBox="0 0 24 24" fill="currentColor">
<path
d="M12 0A12 12 0 0 0 0 12a12 12 0 0 0 12 12 12 12 0 0 0 12-12A12 12 0 0 0 12 0zm5.01 4.744c.688 0 1.25.561 1.25 1.249a1.25 1.25 0 0 1-2.498.056l-2.597-.547-.8 3.747c1.824.07 3.48.632 4.674 1.488.308-.309.73-.491 1.207-.491.968 0 1.754.786 1.754 1.754 0 .716-.435 1.333-1.01 1.614a3.111 3.111 0 0 1 .042.52c0 2.694-3.13 4.87-7.004 4.87-3.874 0-7.004-2.176-7.004-4.87 0-.183.015-.366.043-.534A1.748 1.748 0 0 1 4.028 12c0-.968.786-1.754 1.754-1.754.463 0 .898.196 1.207.49 1.207-.883 2.878-1.43 4.744-1.487l.885-4.182a.342.342 0 0 1 .14-.197.35.35 0 0 1 .238-.042l2.906.617a1.214 1.214 0 0 1 1.108-.702zM9.25 12C8.561 12 8 12.562 8 13.25c0 .687.561 1.248 1.25 1.248.687 0 1.248-.561 1.248-1.249 0-.688-.561-1.249-1.249-1.249zm5.5 0c-.687 0-1.248.561-1.248 1.25 0 .687.561 1.248 1.249 1.248.688 0 1.249-.561 1.249-1.249 0-.687-.562-1.249-1.25-1.249zm-5.466 3.99a.327.327 0 0 0-.231.094.33.33 0 0 0 0 .463c.842.842 2.484.913 2.961.913.477 0 2.105-.056 2.961-.913a.361.361 0 0 0 .029-.463.33.33 0 0 0-.464 0c-.547.533-1.684.73-2.512.73-.828 0-1.979-.197-2.512-.73a.326.326 0 0 0-.232-.095z" />
</svg>
</a>
</div>
</div>
</footer>
</div>
<aside id="toc">
<p class="toc-title">On this page</p>
<ul id="toc-list"></ul>
</aside>
</div>
</div>
<script src="/assets/app.js"></script>
</body>
</html>

View File

@@ -1,77 +1,43 @@
# Vyla Documentation
--- START OF FILE llms.txt ---
# Vyla Entertainment
Vyla is a community-built media project focused on freedom, privacy, and giving people control over their own experience.
> Vyla is a community-built, self-hosted media platform and developer ecosystem focused on user freedom, privacy, and decentralized control. It provides native client applications (Home, Go, TV), a Node.js scraper SDK, and an optional HTTP/SSE Stream API proxy.
## Project Overview
## Overview & Architecture
Vyla is not a streaming service and does not host or provide media. Instead, Vyla builds software that brings the media experience together while leaving control in the hands of the person using it. The project consists of:
- [Introduction](https://vyla.cc/): Community ethos, mission, platforms, and getting started overview.
- [Our Architecture](https://vyla.cc/start-here/architecture): System decoupling between local runtimes, provider resolution, and playback delivery.
- **Vyla Home**: Desktop application for Windows, macOS, and Linux
- **Vyla Go**: Android mobile application
- **Vyla TV**: Android TV application
- **Vyla SDK**: Node.js/TypeScript SDK for developers
- **Stream API**: Self-hosted HTTP/SSE API for browser and mobile clients
## Products
## Key Features
- [Vyla Home](https://vyla.cc/products/home): Desktop control center and application runtime for Windows, macOS, and Linux.
- [Vyla Go](https://vyla.cc/products/go): Android mobile client managing local service lifecycles and embedded playback.
- [Vyla TV](https://vyla.cc/products/tv): Android TV Leanback streaming client built with Jetpack Compose and ExoPlayer.
- [Build a Player](https://vyla.cc/products/player): Architectural guidelines for building resilient client media players.
- Self-hosted and privacy-focused
- Open source community project
- Provider-based streaming resolution
- Multi-platform support (desktop, Android, Android TV)
- Developer-friendly SDK and API
- Support for HLS and MP4 streaming
- Built-in health monitoring and fallback systems
## Core Concepts
## Documentation Structure
- [Providers](https://vyla.cc/concepts/providers): Source configurations, jitter, timeout policies, HLS proxy mechanics, and verification pipelines.
- `/` - Introduction to Vyla
- `/architecture` - System architecture and component relationships
- `/home` - Vyla Home desktop application
- `/go` - Vyla Go Android application
- `/tv` - Vyla TV Android TV application
- `/player` - Building custom media players
- `/sdk` - SDK reference documentation
- `/sdk-quickstart` - Quick start guide for SDK
- `/stream-api/overview` - Stream API overview
- `/stream-api/paths` - API paths and SSE events
- `/stream-api/media-data` - Subtitles and downloads API
- `/stream-api/operations` - Health, testing, and operations
- `/self-hosting` - Self-hosting deployment guide
- `/concepts/providers` - Provider configuration and verification
- `/guides/integrations` - Client integration guide
- `/guides/error-handling` - Error handling and fallbacks
- `/misc/credit` - Credits and acknowledgments
- `/misc/dmca` - DMCA and copyright policy
## Developer SDK
## Technical Stack
- [SDK Reference](https://vyla.cc/sdk): Node.js package (@vyla-entertainment/sdk) for direct stream resolution, subtitles, downloads, and health probing.
- [SDK Quickstart](https://vyla.cc/sdk/vyla-sdk/quickstart): Rapid installation, provider selection, and server-side integration guide.
- Node.js 18+ for SDK and Stream API
- TypeScript for SDK
- Server-Sent Events (SSE) for streaming
- HLS.js for web playback
- TMDB API for metadata
- Jetpack Compose for Android TV
## Stream API
## Integration Options
- [Stream API Overview](https://vyla.cc/stream-api/overview): Self-hosted HTTP and SSE progressive playback service.
- [Paths & Playback Events](https://vyla.cc/stream-api/reference/paths): SSE event contract for /movie and /tv routes emitting meta, source, and done events.
- [Subtitles & Downloads](https://vyla.cc/stream-api/reference/media-data): REST endpoints for subtitle track extraction and direct media downloads.
- [Operations & Health](https://vyla.cc/stream-api/reference/operations): /health metrics, /test probes, and safe production troubleshooting.
- [Self-Hosting Guide](https://vyla.cc/stream-api/deployment/self-hosting): Node.js deployment, cluster scaling, environment variables, and reverse proxy setup.
1. **Direct SDK**: For Node.js applications - embed provider resolution directly
2. **Stream API**: For browser/mobile clients - HTTP/SSE interface
3. **Vyla Home**: Managed desktop experience with bundled services
## Guides & Integrations
## Important Notes
- [Client Integration](https://vyla.cc/guides/integrations): Frontend implementation patterns using HLS.js, React, Vue, ExoPlayer, and AVPlayer.
- [Error Handling](https://vyla.cc/guides/error-handling): Handling SSE timeouts, fallback queues, subtitle absence, and retry strategies.
- Vyla is self-hosted only - no public hosted instance
- Requires TMDB API key for metadata-dependent sources
- Provider availability varies by network environment
- Supports fallback strategies for resilient playback
- CORS-enabled by default
- No database required for Stream API
## Policies & Attribution
## Community
- GitLab: https://gitlab.com/vyla-entertainment/
- Discord: https://discord.gg/5eqRhKeywG
## Legal
Vyla does not host or distribute media. Rights holders can request source removal via the DMCA policy. The project is intended for personal, educational, and development use. Users are responsible for complying with applicable laws.
- [Credit](https://vyla.cc/misc/project/credit): Acknowledgments for upstream open-source inspiration including P-Stream, CinePro, and Movieslay.
- [DMCA Policy](https://vyla.cc/misc/project/dmca): DMCA takedown procedures, upstream content aggregation disclosures, and contact points.

30
manifest.webmanifest Normal file
View File

@@ -0,0 +1,30 @@
{
"name": "Vyla Entertainment",
"short_name": "Vyla",
"description": "A community-built media project focused on freedom, privacy, and control.",
"start_url": "/",
"scope": "/",
"display": "standalone",
"background_color": "#000000",
"theme_color": "#000000",
"orientation": "any",
"categories": [
"entertainment",
"multimedia",
"utilities"
],
"icons": [
{
"src": "/assets/images/logo.png",
"sizes": "192x192",
"type": "image/png",
"purpose": "any maskable"
},
{
"src": "/assets/images/logo.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "any maskable"
}
]
}

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>Credit — Vyla</title>
<meta name="description" content="Credits and inspiration behind the Vyla API ecosystem." />
<meta name="keywords"
content="credits, attribution, open source, inspiration, P-Stream, CinePro, Movieslay, acknowledgments" />
<link rel="canonical" href="https://vyla.cc/misc/credit.html" />
<!-- Open Graph / Facebook -->
<meta property="og:type" content="website" />
<meta property="og:url" content="https://vyla.cc/misc/credit.html" />
<meta property="og:title" content="Credit — Vyla" />
<meta property="og:description" content="Credits and inspiration behind the Vyla API ecosystem." />
<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/misc/credit.html" />
<meta property="twitter:title" content="Credit — Vyla" />
<meta property="twitter:description" content="Credits and inspiration behind the Vyla API ecosystem." />
<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": "Credit",
"description": "Credits and inspiration behind the Vyla API ecosystem.",
"url": "https://vyla.cc/misc/credit.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">Project</p>
<ul>
<li><a class="active" href="/misc/credit">Credit</a></li>
<li><a class="" href="/misc/dmca">Dmca</a></li>
</ul>
</div>
</nav>
<div id="content-col">
<main class="prose">
<h1>Credit</h1>
<p class="page-description">Credits and inspiration behind the Vyla API ecosystem.</p>
<p>This page highlights the open-source projects and developers that heavily inspired the Vyla API, player
ecosystem, and overall UI direction.</p>
<hr>
<h2 id="player--ui-inspiration">Player &#x26; UI Inspiration</h2>
<p>The overall player experience, streaming layout, and much of the UI direction for Vyla was heavily inspired
by P-Stream.</p>
<ul>
<li>Repository: <a href="https://github.com/p-stream">https://github.com/p-stream</a></li>
<li>Owner: <a href="https://github.com/Pasithea0">https://github.com/Pasithea0</a></li>
</ul>
<p>Their work served as a major reference point for the design language and viewing experience used throughout
Vyla.</p>
<hr>
<h2 id="stream-api-inspiration">Stream API Inspiration</h2>
<p>The architecture and implementation ideas behind the Vyla streaming API were heavily inspired by CinePro.</p>
<h3 id="resources">Resources</h3>
<ul>
<li>Organization: <a href="https://github.com/cinepro-org/">https://github.com/cinepro-org/</a></li>
<li>Main Website: <a href="https://cinepro.cc/">https://cinepro.cc/</a></li>
<li>Documentation: <a href="https://docs.cinepro.cc/">https://docs.cinepro.cc/</a>
</li>
<li>UI Components: <a href="https://ui.cinepro.cc/">https://ui.cinepro.cc/</a></li>
<li>Owner: <a href="https://github.com/An0n-00">https://github.com/An0n-00</a></li>
</ul>
<p>CinePro's ecosystem played a large role in shaping how Vyla handles providers, source structures, streaming
workflows, and API organization.</p>
<hr>
<h2 id="source-implementation--fixes">Source Implementation &#x26; Fixes</h2>
<p>The owner of Movieslay helped fix and implement new sources across the Vyla API.</p>
<ul>
<li>Repository: <a
href="https://gitlab.com/gavingogaming/movieslay-emerald-temp">gitlab.com/gavingogaming/movieslay-emerald-temp</a>
</li>
<li>Discord: <a href="https://discord.gg/rD75qxGBVG">discord.gg/rD75qxGBVG</a></li>
<li>Owner: <a href="https://gitlab.com/gavingogaming">gitlab.com/gavingogaming</a></li>
</ul>
<p>Movieslay Emerald is a successor to the original Movieslay webapp. gavingogaming's direct contributions were
instrumental in expanding and stabilizing Vyla's source coverage.</p>
<hr>
<h2 id="open-source-appreciation">Open Source Appreciation</h2>
<p>Vyla would not exist in its current form without the incredible open-source work created by these developers
and communities.</p>
<p>Massive thanks to:</p>
<ul>
<li>Pasithea0</li>
<li>An0n-00</li>
<li>gavingogaming (Movieslay)</li>
<li>The P-Stream contributors</li>
<li>The CinePro contributors</li>
<li>The Movieslay contributors</li>
</ul>
<p>for building and sharing their projects publicly.</p>
</main>
<aside id="toc">
<p class="toc-title">On this page</p>
<ul>
<li><a href="#player--ui-inspiration">Player & UI Inspiration</a></li>
<li><a href="#stream-api-inspiration">Stream API Inspiration</a></li>
<li><a href="#source-implementation--fixes">Source Implementation & Fixes</a></li>
<li><a href="#open-source-appreciation">Open Source Appreciation</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,182 +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>DMCA & Copyright — Vyla</title>
<meta name="description" content="Content sources used by Vyla API and how rights holders can request removal." />
<meta name="keywords"
content="DMCA, copyright, takedown, legal, content sources, rights holders, copyright policy, takedown request" />
<link rel="canonical" href="https://vyla.cc/misc/dmca.html" />
<!-- Open Graph / Facebook -->
<meta property="og:type" content="website" />
<meta property="og:url" content="https://vyla.cc/misc/dmca.html" />
<meta property="og:title" content="DMCA & Copyright — Vyla" />
<meta property="og:description"
content="Content sources used by Vyla API and how rights holders can request removal." />
<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/misc/dmca.html" />
<meta property="twitter:title" content="DMCA & Copyright — Vyla" />
<meta property="twitter:description"
content="Content sources used by Vyla API and how rights holders can request removal." />
<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": "DMCA & Copyright",
"description": "Content sources used by Vyla API and how rights holders can request removal.",
"url": "https://vyla.cc/misc/dmca.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">Project</p>
<ul>
<li><a class="" href="/misc/credit">Credit</a></li>
<li><a class="active" href="/misc/dmca">Dmca</a></li>
</ul>
</div>
</nav>
<div id="content-col">
<main class="prose">
<h1>DMCA & Copyright</h1>
<p class="page-description">Content sources used by Vyla API and how rights holders can request removal.</p>
<p>Vyla API is a self-hosted streaming proxy that aggregates publicly accessible stream URLs from third-party
providers. Vyla does not host, store, upload, or distribute any video content. All media is served directly
from its original source.</p>
<hr>
<h2 id="how-it-works">How It Works</h2>
<p>Vyla acts as a <strong>reverse proxy and aggregator</strong>. When a stream is requested:</p>
<ol>
<li>Vyla queries third-party providers for a stream URL.</li>
<li>That URL is proxied through the Vyla server to handle CORS headers.</li>
<li>The underlying video segments are fetched from their original CDN and passed through to the client.</li>
</ol>
<p>At no point does Vyla store, transcode, or re-host any media file.</p>
<hr>
<h2 id="content-sources">Content Sources</h2>
<p>The following third-party sources are currently scraped or proxied by Vyla API. These are the upstream
providers — Vyla does not control, operate, or own any of them.</p>
<p>You can find the full list of sources in the <a
href="https://gitlab.com/vyla-entertainment/sdk/-/tree/main/src/sources">stream-api sources directory</a>.
</p>
<div class="callout callout-note">
<div class="callout-content">
<p>This list reflects active sources. These sources are active or in the works.</p>
</div>
</div>
<hr>
<h2 id="dmca--takedown-requests">DMCA / Takedown Requests</h2>
<p>If you are a rights holder and believe that content accessible through Vyla API infringes your copyright, you
may submit a takedown request. Because Vyla does not host the content itself, the most effective path is to
contact the upstream source directly.</p>
<p>However, Vyla can disable specific providers or sources upon valid request.</p>
<p><strong>To submit a takedown request, contact:</strong></p>
<div class="card">
<div class="card-title">DMCA Contact</div>
<div class="card-body">
<p>Open an issue on the GitLab repository or reach out via Discord or email.</p>
<ul>
<li><strong>GitLab Issues:</strong> <a
href="https://gitlab.com/vyla-entertainment/stream-api/issues">https://gitlab.com/vyla-entertainment/stream-api/issues</a>
</li>
<li><strong>Discord:</strong> <a href="https://discord.gg/5eqRhKeywG">https://discord.gg/5eqRhKeywG</a>
</li>
<li><strong>Email:</strong> <a
href="mailto:alwaysmesmerizingyou@gmail.com">alwaysmesmerizingyou@gmail.com</a></li>
</ul>
<p>Please include the following in your request:</p>
<ul>
<li>Your name and organization</li>
<li>The specific content or provider you are requesting removal of</li>
<li>Evidence of your rights to the content</li>
<li>Your contact information</li>
</ul>
</div>
</div>
<hr>
<h2 id="good-faith-compliance">Good Faith Compliance</h2>
<p>Vyla API is operated in good faith. Upon receiving a valid and complete takedown request, the relevant source
will be reviewed and disabled within a reasonable timeframe if the claim is substantiated.</p>
<p>Rights holders such as Sony Pictures, Universal, Warner Bros., Disney, Netflix, or any other studio or
distributor are welcome to contact via the channels above. We will respond promptly.</p>
<hr>
<h2 id="disclaimer">Disclaimer</h2>
<p>Vyla API is intended for personal, educational, and development use only. The operator of this API does not
endorse or encourage copyright infringement. Users of this API are responsible for complying with the laws of
their jurisdiction.</p>
</main>
<aside id="toc">
<p class="toc-title">On this page</p>
<ul>
<li><a href="#how-it-works">How It Works</a></li>
<li><a href="#content-sources">Content Sources</a></li>
<li><a href="#dmca--takedown-requests">DMCA / Takedown Requests</a></li>
<li><a href="#good-faith-compliance">Good Faith Compliance</a></li>
<li><a href="#disclaimer">Disclaimer</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>

59
misc/project/DMCA.md Normal file
View File

@@ -0,0 +1,59 @@
# DMCA & Copyright
<p class="page-description">Content sources used by Vyla API and how rights holders can request removal.</p>
Vyla API is a self-hosted streaming proxy that aggregates publicly accessible stream URLs from third-party providers. Vyla does not host, store, upload, or distribute any video content. All media is served directly from its original source.
---
## How It Works
Vyla acts as a **reverse proxy and aggregator**. When a stream is requested:
1. Vyla queries third-party providers for a stream URL.
2. That URL is proxied through the Vyla server to handle CORS headers.
3. The underlying video segments are fetched from their original CDN and passed through to the client.
At no point does Vyla store, transcode, or re-host any media file.
---
## Content Sources
The following third-party sources are scraped or proxied by Vyla API. These are upstream providers — Vyla does not control, operate, or own any of them.
You can find the full list of sources in the [stream-api sources directory](https://gitlab.com/vyla-entertainment/sdk/-/tree/main/src/sources).
---
## DMCA / Takedown Requests
If you are a rights holder and believe that content accessible through Vyla API infringes your copyright, you may submit a takedown request. Because Vyla does not host the content itself, the most effective path is to contact the upstream source directly.
However, Vyla can disable specific providers or sources upon valid request.
**To submit a takedown request, contact:**
<div class="card">
<div class="card-title">DMCA Contact</div>
<div class="card-body">
<p>Open an issue on the GitLab repository or reach out via Discord or email.</p>
<ul>
<li><strong>GitLab Issues:</strong> <a href="https://gitlab.com/vyla-entertainment/stream-api/issues">https://gitlab.com/vyla-entertainment/stream-api/issues</a></li>
<li><strong>Discord:</strong> <a href="https://discord.gg/5eqRhKeywG">https://discord.gg/5eqRhKeywG</a></li>
<li><strong>Email:</strong> <a href="mailto:alwaysmesmerizingyou@gmail.com">alwaysmesmerizingyou@gmail.com</a></li>
</ul>
</div>
</div>
---
## Good Faith Compliance
Vyla API is operated in good faith. Upon receiving a valid and complete takedown request, the relevant source will be reviewed and disabled within a reasonable timeframe if the claim is substantiated.
---
## Disclaimer
Vyla API is intended for personal, educational, and development use only. The operator of this API does not endorse or encourage copyright infringement. Users of this API are responsible for complying with the laws of their jurisdiction.

55
misc/project/credit.md Normal file
View File

@@ -0,0 +1,55 @@
# Credit
<p class="page-description">Credits and inspiration behind the Vyla API ecosystem.</p>
This page highlights the open-source projects and developers that heavily inspired the Vyla API, player ecosystem, and overall UI direction.
---
## Player & UI Inspiration
The overall player experience, streaming layout, and much of the UI direction for Vyla was heavily inspired by P-Stream.
- Repository: [https://github.com/p-stream](https://github.com/p-stream)
- Owner: [https://github.com/Pasithea0](https://github.com/Pasithea0)
Their work served as a major reference point for the design language and viewing experience used throughout Vyla.
---
## Stream API Inspiration
The architecture and implementation ideas behind the Vyla streaming API were heavily inspired by CinePro.
### Resources
- Organization: [https://github.com/cinepro-org/](https://github.com/cinepro-org/)
- Main Website: [https://cinepro.cc/](https://cinepro.cc/)
- Documentation: [https://docs.cinepro.cc/](https://docs.cinepro.cc/)
- UI Components: [https://ui.cinepro.cc/](https://ui.cinepro.cc/)
- Owner: [https://github.com/An0n-00](https://github.com/An0n-00)
---
## Source Implementation & Fixes
The owner of Movieslay helped fix and implement new sources across the Vyla API.
- Repository: [gitlab.com/gavingogaming/movieslay-emerald-temp](https://gitlab.com/gavingogaming/movieslay-emerald-temp)
- Discord: [https://discord.gg/rD75qxGBVG](https://discord.gg/rD75qxGBVG)
- Owner: [https://gitlab.com/gavingogaming](https://gitlab.com/gavingogaming)
---
## Open Source Appreciation
Vyla would not exist in its current form without the incredible open-source work created by these developers and communities.
Massive thanks to:
- Pasithea0
- An0n-00
- gavingogaming (Movieslay)
- The P-Stream contributors
- The CinePro contributors
- The Movieslay contributors

View File

@@ -1,184 +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>Build a player — Vyla</title>
<meta name="description"
content="Design a resilient Vyla playback experience around your own product and infrastructure." />
<meta name="keywords"
content="Vyla player, media player, custom player, playback experience, HLS player, MP4 player, streaming player" />
<link rel="canonical" href="https://vyla.cc/player.html" />
<!-- Open Graph / Facebook -->
<meta property="og:type" content="website" />
<meta property="og:url" content="https://vyla.cc/player.html" />
<meta property="og:title" content="Build a player — Vyla" />
<meta property="og:description"
content="Design a resilient Vyla playback experience around your own product and infrastructure." />
<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/player.html" />
<meta property="twitter:title" content="Build a player — Vyla" />
<meta property="twitter:description"
content="Design a resilient Vyla playback experience around your own product and infrastructure." />
<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": "Build a player",
"description": "Design a resilient Vyla playback experience around your own product and infrastructure.",
"url": "https://vyla.cc/player.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">Start here</p>
<ul>
<li><a class="active" href="/">Introduction</a></li>
</li>
<li><a class="" href="/architecture">Architecture</a></li>
</ul>
</div>
<div class="group">
<p class="group-title">Products</p>
<ul>
<li><a class="" href="/home">Home</a></li>
<li><a class="" href="/go">Go</a></li>
<li><a class="" href="/tv">Tv</a></li>
<li><a class="active" href="/player">Player</a></li>
</ul>
</div>
<div class="group">
<p class="group-title">Concepts</p>
<ul>
<li><a class="" href="/concepts/providers">Providers</a></li>
</ul>
</div>
</nav>
<div id="content-col">
<main class="prose">
<h1>Build a player</h1>
<p class="page-description">Design a resilient Vyla playback experience around your own product and
infrastructure.</p>
<h2 id="your-player-your-rules">Your player, your rules</h2>
<p>Vyla supplies the integration layer; your application owns the player experience. That includes design,
authentication, title discovery, parental controls, analytics, playback policy, and fallback behavior.</p>
<p>For a local managed experience, Vyla Home starts its bundled player service. For a custom product, choose one
of two boundaries:</p>
<table>
<thead>
<tr>
<th>Integration</th>
<th>Best for</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>SDK directly</strong></td>
<td>Node.js products that already have a backend and want complete control</td>
</tr>
<tr>
<td><strong>Stream API</strong></td>
<td>browser or mobile clients that need an HTTP/SSE source feed</td>
</tr>
</tbody>
</table>
<h2 id="build-for-changing-conditions">Build for changing conditions</h2>
<p>Provider results vary by title and network. A dependable player should:</p>
<ol>
<li>Begin playback as soon as it has a usable candidate.</li>
<li>Keep later candidates as fallbacks.</li>
<li>Keep subtitles independent from first playback.</li>
<li>Report a helpful unavailable state when no candidates resolve.</li>
<li>Destroy playback and abort in-flight work when the viewer changes titles.</li>
</ol>
<p>The SDK returns provider results directly; Stream API emits <code>meta</code>, <code>source</code>, and
<code>done</code> events. Both models benefit from the same fallback strategy.
</p>
<h2 id="playback-formats">Playback formats</h2>
<p>Candidates may be HLS manifests or direct media files. Let the result metadata and your player capabilities
guide how a candidate is loaded; do not assume every URL has the same format or availability. HLS-capable web
players normally use HLS.js where native HLS is unavailable, while direct media can be assigned to a video
element.</p>
<h2 id="continue-building">Continue building</h2>
<ul>
<li>Embed provider resolution with <a href="/sdk-quickstart">SDK quickstart</a>.</li>
<li>Learn the optional HTTP event contract in <a href="/stream-api/paths">Paths and playback events</a>.
</li>
<li>Use the <a href="/guides/integrations">client integration guide</a> for a browser-oriented example.
</li>
<li>Review <a href="/guides/error-handling">error handling</a> before shipping a player.</li>
</ul>
</main>
<aside id="toc">
<p class="toc-title">On this page</p>
<ul>
<li><a href="#your-player-your-rules">Your player, your rules</a></li>
<li><a href="#build-for-changing-conditions">Build for changing conditions</a></li>
<li><a href="#playback-formats">Playback formats</a></li>
<li><a href="#continue-building">Continue building</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>

17
products/go.md Normal file
View File

@@ -0,0 +1,17 @@
# Vyla Go
<p class="page-description">The Android control center for running the Vyla media stack locally.</p>
<img class="vyla-banner" src="/assets/images/android/site_preview.png" alt="A collage of films and shows available through a Vyla experience" loading="lazy" fetchpriority="low" />
**Vyla Go** is the Android application in the Vyla ecosystem. It prepares and runs the local services that power your media experience, then opens the interface in a secure embedded window. It is designed for people who want Vyla on their Android device without managing a separate server process or service lifecycle by hand.
## Downloads
Vyla Go is available for Android. Get the latest version below:
| Platform | Download |
| --- | --- |
| Android | [Download .apk](https://gitlab.com/vyla-entertainment/go/-/releases) |
Inside the releases page, you can find the latest version of Vyla Go for Android, find the `.apk` file, and install it on your device.

73
products/home.md Normal file
View File

@@ -0,0 +1,73 @@
# Vyla Home
<p class="page-description">The desktop control center for running the Vyla media stack locally.</p>
<img class="vyla-banner" src="/assets/images/desktop/site_preview.png" alt="Vyla Home desktop application preview" loading="lazy" fetchpriority="low" />
## Vyla Home
**Vyla Home** is the desktop application in the Vyla ecosystem. It prepares and runs the local services that power your media experience, then opens the interface in a secure embedded window. It is designed for people who want Vyla without managing a separate server process, reverse proxy, or service lifecycle by hand.
## Downloads
Vyla Home is available for Windows, macOS, and Linux. Get the latest version below:
| Platform | Download |
| --- | --- |
| Windows | [Download .exe](https://gitlab.com/vyla-entertainment/home/-/releases) |
| macOS | [Download .dmg](https://gitlab.com/vyla-entertainment/home/-/releases) |
| Linux | [Build from source](#linux-setup) |
Inside the releases page, you can find the latest version of Vyla Home for each platform, find the `.exe` for windows, and `.dmg` for macOS.
## Linux Setup
For Linux, you'll need to build Vyla Home from source. Follow these steps to install on Fedora:
**Step 1: Install dependencies**
```bash
sudo dnf install webkit2gtk4.1-devel curl wget openssl-devel libappindicator-gtk3-devel librsvg2-devel
sudo dnf install gcc gcc-c++ make automake autoconf
```
**Step 2: Install Node.js and NPM**
```bash
sudo dnf install nodejs npm
```
**Step 3: Install Rust**
```bash
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
source "$HOME/.cargo/env"
```
**Step 4: Clone the Git repository**
```bash
git clone https://gitlab.com/vyla-entertainment/home.git vyla-home
cd vyla-home
```
**Step 5: Install more dependencies**
```bash
npm install
```
**Step 6: Build the Fedora package (.rpm)**
```bash
cargo update --manifest-path src-tauri/Cargo.toml
npx tauri build --bundles rpm,appimage
```
**Step 7: Final installation**
```bash
sudo dnf install src-tauri/target/release/bundle/rpm/*.rpm
```
*Thank you @krishi279 for supplying the Linux setup instructions on Discord ❤️.*

37
products/player.md Normal file
View File

@@ -0,0 +1,37 @@
# Build a player
<p class="page-description">Design a resilient Vyla playback experience around your own product and infrastructure.</p>
## Your player, your rules
Vyla supplies the integration layer; your application owns the player experience. That includes design, authentication, title discovery, parental controls, analytics, playback policy, and fallback behavior.
For a local managed experience, Vyla Home starts its bundled player service. For a custom product, choose one of two boundaries:
| Integration | Best for |
| --- | --- |
| **SDK directly** | Node.js products that already have a backend and want complete control |
| **Stream API** | browser or mobile clients that need an HTTP/SSE source feed |
## Build for changing conditions
Provider results vary by title and network. A dependable player should:
1. Begin playback as soon as it has a usable candidate.
2. Keep later candidates as fallbacks.
3. Keep subtitles independent from first playback.
4. Report a helpful unavailable state when no candidates resolve.
5. Destroy playback and abort in-flight work when the viewer changes titles.
The SDK returns provider results directly; Stream API emits `meta`, `source`, and `done` events. Both models benefit from the same fallback strategy.
## Playback formats
Candidates may be HLS manifests or direct media files. Let the result metadata and your player capabilities guide how a candidate is loaded; do not assume every URL has the same format or availability. HLS-capable web players normally use HLS.js where native HLS is unavailable, while direct media can be assigned to a video element.
## Continue building
- Embed provider resolution with [SDK quickstart](/sdk/vyla-sdk/quickstart).
- Learn the optional HTTP event contract in [Paths and playback events](/stream-api/reference/paths).
- Use the [client integration guide](/guides/integrations) for a browser-oriented example.
- Review [error handling](/guides/error-handling) before shipping a player.

19
products/tv.md Normal file
View File

@@ -0,0 +1,19 @@
# Vyla TV
<p class="page-description">The Android TV application for running the Vyla media experience on your television.</p>
<img class="vyla-banner" src="/assets/images/android-tv/site-preview.png" alt="A collage of films and shows available through a Vyla experience" loading="lazy" fetchpriority="low" />
## Vyla TV
**Vyla TV** is an Android TV streaming app built with Jetpack Compose and ExoPlayer that lets you browse and watch movies and TV shows from TMDB.
## Downloads
Vyla TV is available for Android TV. Get the latest version below:
| Platform | Download |
| --- | --- |
| Android TV | [Download .apk](https://gitlab.com/vyla-entertainment/TV/-/releases) |
Inside the releases page, you can find the latest version of Vyla TV for Android TV, find the `.apk` file, and install it on your device.

View File

@@ -1,5 +1,102 @@
User-agent: *
Allow: /
Disallow: /assets/
Sitemap: https://vyla.cc/sitemap.xml
--- END OF FILE robots.txt ---
--- START OF FILE ai-catalog.json ---
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"name": "Vyla Entertainment",
"identifier": "vyla-entertainment",
"description": "A community-built media streaming and scraper ecosystem providing native applications, a Node.js resolver SDK, and a self-hosted Stream API.",
"homepage": "https://vyla.cc",
"documentation": "https://vyla.cc",
"llms_context": "https://vyla.cc/llms.txt",
"repository": "https://gitlab.com/vyla-entertainment",
"community": "https://discord.gg/5eqRhKeywG",
"license": "Open Source",
"ecosystem": {
"clients": [
{
"name": "Vyla Home",
"platform": ["Windows", "macOS", "Linux"],
"description": "Desktop control center and self-contained runtime.",
"documentation": "https://vyla.cc/products/home"
},
{
"name": "Vyla Go",
"platform": ["Android"],
"description": "Mobile application running local resolver services.",
"documentation": "https://vyla.cc/products/go"
},
{
"name": "Vyla TV",
"platform": ["Android TV"],
"description": "Television interface built with Jetpack Compose and ExoPlayer.",
"documentation": "https://vyla.cc/products/tv"
}
],
"libraries": [
{
"package": "@vyla-entertainment/sdk",
"language": "Node.js (>=18)",
"description": "Server-side resolver library for stream links, subtitle extraction, and download links.",
"documentation": "https://vyla.cc/sdk"
}
],
"services": [
{
"name": "Stream API",
"type": "Self-hosted HTTP/SSE Proxy",
"description": "REST and Server-Sent Events service wrapping Vyla SDK for remote clients.",
"default_port": 7860,
"protocols": ["HTTP", "SSE"],
"documentation": "https://vyla.cc/stream-api/overview",
"endpoints": [
{
"path": "/movie",
"method": "GET",
"query_parameters": ["id"],
"response_type": "text/event-stream"
},
{
"path": "/tv",
"method": "GET",
"query_parameters": ["id", "season", "episode"],
"response_type": "text/event-stream"
},
{
"path": "/subtitles/movie/:id",
"method": "GET",
"response_type": "application/json"
},
{
"path": "/subtitles/tv/:id/:season/:episode",
"method": "GET",
"response_type": "application/json"
},
{
"path": "/downloads/movie/:id",
"method": "GET",
"response_type": "application/json"
},
{
"path": "/downloads/tv/:id/:season/:episode",
"method": "GET",
"response_type": "application/json"
},
{
"path": "/health",
"method": "GET",
"response_type": "application/json"
}
]
}
]
},
"compliance": {
"hosting_policy": "Non-hosting scraper proxy; all video segments originate from third-party hosts.",
"dmca_contact": "alwaysmesmerizingyou@gmail.com",
"dmca_policy": "https://vyla.cc/misc/project/dmca"
}
}

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>SDK quickstart — Vyla</title>
<meta name="description" content="Install Vyla SDK, resolve a source, and build a safe server-side integration." />
<meta name="keywords"
content="Vyla SDK, SDK quickstart, Node.js SDK, server-side integration, stream resolution, API integration" />
<link rel="canonical" href="https://vyla.cc/sdk-quickstart.html" />
<!-- Open Graph / Facebook -->
<meta property="og:type" content="website" />
<meta property="og:url" content="https://vyla.cc/sdk-quickstart.html" />
<meta property="og:title" content="SDK quickstart — Vyla" />
<meta property="og:description"
content="Install Vyla SDK, resolve a source, and build a safe server-side integration." />
<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/sdk-quickstart.html" />
<meta property="twitter:title" content="SDK quickstart — Vyla" />
<meta property="twitter:description"
content="Install Vyla SDK, resolve a source, and build a safe server-side integration." />
<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": "SDK quickstart",
"description": "Install Vyla SDK, resolve a source, and build a safe server-side integration.",
"url": "https://vyla.cc/sdk-quickstart.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">Vyla SDK</p>
<ul>
<li><a class="" href="/sdk">Sdk</a></li>
<li><a class="active" href="/sdk-quickstart">Sdk Quickstart</a></li>
</ul>
</div>
</nav>
<div id="content-col">
<main class="prose">
<h1>SDK quickstart</h1>
<p class="page-description">Install Vyla SDK, resolve a source, and build a safe server-side integration.</p>
<h2 id="before-you-start">Before you start</h2>
<p>Vyla SDK is for <strong>server-side Node.js</strong>. It contains provider integrations and makes outbound
requests from the process where you install it. Do not import it into a browser bundle or expose provider
results, credentials, or internal source keys directly to untrusted clients.</p>
<p>You need Node.js 18 or later and, for sources that resolve title metadata, a TMDB API key.</p>
<h2 id="install-and-initialize">Install and initialize</h2>
<div class="code-group">
<div class="code-group-tabs"><button class="code-group-tab active" data-tab-index="0" type="button"
onclick="cgSwitch(this)">npm</button><button class="code-group-tab" data-tab-index="1" type="button"
onclick="cgSwitch(this)">pnpm</button><button class="code-group-tab" data-tab-index="2" type="button"
onclick="cgSwitch(this)">yarn</button></div>
<div class="code-group-panels">
<div class="code-group-panel active">
<pre><code class="hljs language-bash">npm <span class="hljs-built_in">install</span> <span class="hljs-string">@vyla-entertainment/sdk</span></code></pre>
</div>
<div class="code-group-panel">
<pre><code class="hljs language-bash">pnpm <span class="hljs-built_in">install</span> <span class="hljs-string">@vyla-entertainment/sdk</span></code></pre>
</div>
<div class="code-group-panel">
<pre><code class="hljs language-bash">yarn <span class="hljs-built_in">add</span> <span class="hljs-string">@vyla-entertainment/sdk</span></code></pre>
</div>
</div>
</div>
<pre><code class="hljs language-js"><span class="hljs-keyword">import</span> <span class="hljs-title class_">VylaSDK</span> <span class="hljs-keyword">from</span> <span class="hljs-string">"@vyla-entertainment/sdk"</span>;
<span class="hljs-keyword">const</span> vyla = <span class="hljs-keyword">new</span> <span class="hljs-title class_">VylaSDK</span>({
<span class="hljs-attr">tmdbApiKey</span>: process.<span class="hljs-property">env</span>.<span class="hljs-property">TMDB_API_KEY</span>,
<span class="hljs-attr">penguManifest</span>: process.<span class="hljs-property">env</span>.<span class="hljs-property">PENGU_MANIFEST</span>
});
</code></pre>
<div class="callout callout-warning">
<div class="callout-content">
<p>Keep <code>TMDB_API_KEY</code> and any deployment configuration in server-side environment variables.
Never commit them or send them to a client.</p>
</div>
</div>
<h2 id="resolve-a-movie">Resolve a movie</h2>
<p>First inspect active providers. Provider availability changes over time, so select a key from the running
configuration instead of hard-coding a placeholder.</p>
<pre><code class="hljs language-js"><span class="hljs-keyword">const</span> providers = vyla.<span class="hljs-title function_">getSources</span>(<span class="hljs-literal">true</span>);
<span class="hljs-variable language_">console</span>.<span class="hljs-title function_">table</span>(providers.<span class="hljs-title function_">map</span>(<span class="hljs-function">(<span class="hljs-params">{ key, label }</span>) =></span> ({ key, label })));
<span class="hljs-keyword">const</span> provider = providers[<span class="hljs-number">0</span>];
<span class="hljs-keyword">const</span> result = <span class="hljs-keyword">await</span> vyla.<span class="hljs-title function_">getStream</span>(provider.<span class="hljs-property">key</span>, <span class="hljs-number">155</span>);
<span class="hljs-keyword">const</span> candidates = result.<span class="hljs-property">allUrls</span> ?? [result];
<span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(candidates.<span class="hljs-title function_">map</span>(<span class="hljs-function">(<span class="hljs-params">{ url, type, quality }</span>) =></span> ({ url, type, quality })));
</code></pre>
<p><code>155</code> is a TMDB movie ID. For an episode, include season and episode:</p>
<pre><code class="hljs language-js"><span class="hljs-keyword">const</span> result = <span class="hljs-keyword">await</span> vyla.<span class="hljs-title function_">getStream</span>(provider.<span class="hljs-property">key</span>, <span class="hljs-number">1396</span>, <span class="hljs-number">1</span>, <span class="hljs-number">1</span>);
</code></pre>
<h2 id="make-it-resilient">Make it resilient</h2>
<p>Provider results are inherently variable: a source can fail, time out, return more than one candidate, or be
temporarily disabled. In production:</p>
<ol>
<li>Probe sources or maintain a recent availability signal before choosing one.</li>
<li>Give every provider call a request deadline in your application.</li>
<li>Treat <code>allUrls</code> as fallback candidates, not a guarantee that every URL will play.</li>
<li>Return only the fields your client needs and validate requests at your own API boundary.</li>
<li>Cache and rate-limit at your boundary to avoid repeatedly triggering upstream work.</li>
</ol>
<pre><code class="hljs language-js"><span class="hljs-keyword">const</span> health = <span class="hljs-keyword">await</span> vyla.<span class="hljs-title function_">probeAllSources</span>();
<span class="hljs-keyword">const</span> usableKey = <span class="hljs-title class_">Object</span>.<span class="hljs-title function_">entries</span>(health).<span class="hljs-title function_">find</span>(<span class="hljs-function">(<span class="hljs-params">[, result]</span>) =></span> result.<span class="hljs-property">ok</span>)?.[<span class="hljs-number">0</span>];
<span class="hljs-keyword">if</span> (!usableKey) <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Error</span>(<span class="hljs-string">"No providers are currently available"</span>);
<span class="hljs-keyword">const</span> stream = <span class="hljs-keyword">await</span> vyla.<span class="hljs-title function_">getStream</span>(usableKey, <span class="hljs-number">155</span>);
</code></pre>
<h2 id="next-steps">Next steps</h2>
<ul>
<li>Read the complete <a href="/sdk">SDK reference</a> for methods and return shapes.</li>
<li>Learn provider flags, timeout behavior, and source keys in <a href="/concepts/providers">Providers</a>.
</li>
<li>Choose <a href="/stream-api/overview">Stream API</a> only when an SSE HTTP interface better fits your
client architecture.</li>
</ul>
</main>
<aside id="toc">
<p class="toc-title">On this page</p>
<ul>
<li><a href="#before-you-start">Before you start</a></li>
<li><a href="#install-and-initialize">Install and initialize</a></li>
<li><a href="#resolve-a-movie">Resolve a movie</a></li>
<li><a href="#make-it-resilient">Make it resilient</a></li>
<li><a href="#next-steps">Next steps</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,588 +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>SDK Reference — Vyla</title>
<meta name="description"
content="Scrape video streams, fetch subtitles, retrieve download links, and monitor provider health directly from your Node.js application." />
<meta name="keywords"
content="Vyla SDK, Node.js SDK, TypeScript SDK, video streaming, subtitle API, download API, health monitoring, server-side streaming" />
<link rel="canonical" href="https://vyla.cc/sdk.html" />
<!-- Open Graph / Facebook -->
<meta property="og:type" content="website" />
<meta property="og:url" content="https://vyla.cc/sdk.html" />
<meta property="og:title" content="SDK Reference — Vyla" />
<meta property="og:description"
content="Scrape video streams, fetch subtitles, retrieve download links, and monitor provider health directly from your Node.js application." />
<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/sdk.html" />
<meta property="twitter:title" content="SDK Reference — Vyla" />
<meta property="twitter:description"
content="Scrape video streams, fetch subtitles, retrieve download links, and monitor provider health directly from your Node.js application." />
<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": "Vyla SDK",
"description": "Scrape video streams, fetch subtitles, retrieve download links, and monitor provider health directly from your Node.js application.",
"url": "https://vyla.cc/sdk.html",
"applicationCategory": "DeveloperApplication",
"operatingSystem": "Node.js",
"offers": {
"@type": "Offer",
"price": "0",
"priceCurrency": "USD"
}
}
</script>
<script>
(function () {
var saved = localStorage.getItem('vyla-theme');
var isDark = saved ? saved === 'dark' : true;
document.documentElement.classList.toggle('dark', isDark);
})();
</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"></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">Vyla SDK</p>
<ul>
<li><a class="active" href="/sdk">Sdk</a></li>
<li><a class="" href="/sdk-quickstart">Sdk Quickstart</a></li>
</ul>
</div>
</nav>
<div id="content-col">
<main class="prose">
<h1>SDK Reference</h1>
<p class="page-description">Scrape video streams, fetch subtitles, retrieve download links, and monitor provider
health directly from your Node.js application.</p>
<p>The <strong>Vyla SDK</strong> (<code>@vyla-entertainment/sdk</code>) is a server-side JavaScript/TypeScript
package that provides direct access to Vyla's stream resolvers, health probes, download resolvers, and
subtitle providers.</p>
<p>By embedding the SDK directly into your backend application, provider resolution executes in your own
environment. It is an alternative to operating the Stream API as a separate process: your application owns its
request boundary, caching, authorization, timeouts, and response shape.</p>
<div class="callout callout-note">
<div class="callout-content">
<p>The SDK can be used independently of the self-hosted Stream API. If you prefer to embed Vyla capabilities
in an existing Node.js application rather than run a separate HTTP service, begin with the <a
href="/sdk-quickstart">SDK quickstart</a>.</p>
</div>
</div>
<hr>
<h2 id="features">Features</h2>
<ul>
<li><strong>Direct-to-Client Playback</strong>: Scraped streams link directly to source CDNs. No proxy server
overhead required unless specified by provider headers.</li>
<li><strong>Built-In Health Probing</strong>: Monitor latency and uptime per provider with built-in health
check utilities.</li>
<li><strong>Automated Anime Sub/Dub Routing</strong>: Automatically configures audio track requirements
(<code>sub</code> vs <code>dub</code>) based on provider naming conventions.</li>
<li><strong>Multi-Source Subtitle Aggregation</strong>: Merges subtitle tracks from multiple redundant
subtitle mirrors into a unified VTT/SRT format.</li>
<li><strong>Metadata Fallbacks</strong>: Automatically maps TMDB IDs to external IDs (such as IMDB and
AniList) for smooth stream lookups.</li>
<li><strong>Self-Contained</strong>: No need to deploy a separate API server — all streaming functionality
runs within your existing Node.js application.</li>
</ul>
<hr>
<h2 id="installation">Installation</h2>
<p>Install the package into your Node.js backend using your package manager of choice:</p>
<div class="code-group">
<div class="code-group-tabs"><button class="code-group-tab active" data-tab-index="0" type="button"
onclick="cgSwitch(this)">npm</button><button class="code-group-tab" data-tab-index="1" type="button"
onclick="cgSwitch(this)">pnpm</button><button class="code-group-tab" data-tab-index="2" type="button"
onclick="cgSwitch(this)">yarn</button></div>
<div class="code-group-panels">
<div class="code-group-panel active">
<pre><code class="hljs language-bash">npm <span class="hljs-built_in">install</span> <span class="hljs-string">@vyla-entertainment/sdk</span></code></pre>
</div>
<div class="code-group-panel">
<pre><code class="hljs language-bash">pnpm <span class="hljs-built_in">install</span> <span class="hljs-string">@vyla-entertainment/sdk</span></code></pre>
</div>
<div class="code-group-panel">
<pre><code class="hljs language-bash">yarn <span class="hljs-built_in">add</span> <span class="hljs-string">@vyla-entertainment/sdk</span></code></pre>
</div>
</div>
</div>
<blockquote>
<p><strong>Note</strong>: Node.js <strong>v18+</strong> is required due to modern standard <code>fetch</code>
and <code>AbortSignal.timeout</code> usage.</p>
</blockquote>
<hr>
<h2 id="initialization">Initialization</h2>
<p>Initialize the <code>VylaSDK</code> client by supplying your <strong>TMDB API Key</strong>. The key resolves
media metadata, validates movie/TV show IDs, and translates TMDB IDs into AniList IDs for anime sources.</p>
<p>You may optionally provide a pengu.uk manifest URL to enable the Pengu source.</p>
<pre><code class="hljs language-javascript"><span class="hljs-keyword">import</span> <span class="hljs-title class_">VylaSDK</span> <span class="hljs-keyword">from</span> <span class="hljs-string">"@vyla-entertainment/sdk"</span>;
<span class="hljs-keyword">const</span> sdk = <span class="hljs-keyword">new</span> <span class="hljs-title class_">VylaSDK</span>({
<span class="hljs-attr">tmdbApiKey</span>: process.<span class="hljs-property">env</span>.<span class="hljs-property">TMDB_API_KEY</span>,
<span class="hljs-attr">penguManifest</span>: <span class="hljs-string">""</span> <span class="hljs-comment">// Optional, get from pengu.uk</span>
});
</code></pre>
<hr>
<h2 id="core-methods">Core Methods</h2>
<h3 id="getstreamkey-id-s-e-clientip"><code>getStream(key, id, s?, e?, clientIP?)</code></h3>
<p>Scrapes video stream URLs and playability headers for a specified provider key.</p>
<pre><code class="hljs language-javascript"><span class="hljs-comment">// Fetch a movie stream (TMDB ID: 155 - The Dark Knight)</span>
<span class="hljs-keyword">const</span> movieStream = <span class="hljs-keyword">await</span> sdk.<span class="hljs-title function_">getStream</span>(<span class="hljs-string">"sourcename"</span>, <span class="hljs-string">"155"</span>);
<span class="hljs-comment">// Fetch a TV show episode (TMDB ID: 1399, Season 1, Episode 1)</span>
<span class="hljs-keyword">const</span> tvStream = <span class="hljs-keyword">await</span> sdk.<span class="hljs-title function_">getStream</span>(<span class="hljs-string">"sourcename"</span>, <span class="hljs-string">"1399"</span>, <span class="hljs-number">1</span>, <span class="hljs-number">1</span>);
<span class="hljs-comment">// Pass client IP for providers that use geo-verification</span>
<span class="hljs-keyword">const</span> geoStream = <span class="hljs-keyword">await</span> sdk.<span class="hljs-title function_">getStream</span>(<span class="hljs-string">"sourcename"</span>, <span class="hljs-string">"155"</span>, <span class="hljs-literal">null</span>, <span class="hljs-literal">null</span>, <span class="hljs-string">"203.0.113.195"</span>);
</code></pre>
<h4 id="parameters">Parameters</h4>
<table>
<thead>
<tr>
<th align="left">Parameter</th>
<th align="left">Type</th>
<th align="left">Required</th>
<th align="left">Default</th>
<th align="left">Description</th>
</tr>
</thead>
<tbody>
<tr>
<td align="left"><code>key</code></td>
<td align="left"><code>string</code></td>
<td align="left"><strong>Yes</strong></td>
<td align="left">—</td>
<td align="left">Unique key identifier for the provider (e.g. <code>"sourcename"</code>,
<code>"sourcename"</code>, <code>"sourcename"</code>).
</td>
</tr>
<tr>
<td align="left"><code>id</code></td>
<td align="left"><code>string</code></td>
<td align="left"><strong>Yes</strong></td>
<td align="left">—</td>
<td align="left">TMDB media ID (e.g., <code>"155"</code>).</td>
</tr>
<tr>
<td align="left"><code>s</code></td>
<td align="left"><code>string | number</code></td>
<td align="left"><strong>No</strong></td>
<td align="left"><code>null</code></td>
<td align="left">Season number (required for TV shows).</td>
</tr>
<tr>
<td align="left"><code>e</code></td>
<td align="left"><code>string | number</code></td>
<td align="left"><strong>No</strong></td>
<td align="left"><code>null</code></td>
<td align="left">Episode number (required for TV shows).</td>
</tr>
<tr>
<td align="left"><code>clientIP</code></td>
<td align="left"><code>string</code></td>
<td align="left"><strong>No</strong></td>
<td align="left"><code>null</code></td>
<td align="left">End-user IP address forwarded for geo-restricted scrapers.</td>
</tr>
</tbody>
</table>
<h4 id="example-response-format">Example Response Format</h4>
<pre><code class="hljs language-json"><span class="hljs-punctuation">{</span>
<span class="hljs-attr">"url"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"https://example-cdn.com/hls/manifest.m3u8"</span><span class="hljs-punctuation">,</span>
<span class="hljs-attr">"quality"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"auto"</span><span class="hljs-punctuation">,</span>
<span class="hljs-attr">"isM3U8"</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">true</span></span><span class="hljs-punctuation">,</span>
<span class="hljs-attr">"headers"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span>
<span class="hljs-attr">"Referer"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"https://example.com/"</span><span class="hljs-punctuation">,</span>
<span class="hljs-attr">"Origin"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"https://example.com"</span>
<span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span>
<span class="hljs-attr">"allUrls"</span><span class="hljs-punctuation">:</span> <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-cdn.com/hls/manifest.m3u8"</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">"isM3U8"</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">true</span></span>
<span class="hljs-punctuation">}</span>
<span class="hljs-punctuation">]</span>
<span class="hljs-punctuation">}</span>
</code></pre>
<hr>
<h3 id="getsubtitlesid-s-e"><code>getSubtitles(id, s?, e?)</code></h3>
<p>Fetches subtitle files aggregated across multiple subtitle backend mirrors (<code>v1</code>, <code>v2</code>,
<code>febbox</code>). Returns clean VTT and SRT tracks.
</p>
<pre><code class="hljs language-javascript"><span class="hljs-comment">// Fetch movie subtitles</span>
<span class="hljs-keyword">const</span> movieSubs = <span class="hljs-keyword">await</span> sdk.<span class="hljs-title function_">getSubtitles</span>(<span class="hljs-string">"155"</span>);
<span class="hljs-comment">// Fetch TV show episode subtitles</span>
<span class="hljs-keyword">const</span> tvSubs = <span class="hljs-keyword">await</span> sdk.<span class="hljs-title function_">getSubtitles</span>(<span class="hljs-string">"1399"</span>, <span class="hljs-number">1</span>, <span class="hljs-number">1</span>);
</code></pre>
<h4 id="parameters-1">Parameters</h4>
<table>
<thead>
<tr>
<th align="left">Parameter</th>
<th align="left">Type</th>
<th align="left">Required</th>
<th align="left">Default</th>
<th align="left">Description</th>
</tr>
</thead>
<tbody>
<tr>
<td align="left"><code>id</code></td>
<td align="left"><code>string</code></td>
<td align="left"><strong>Yes</strong></td>
<td align="left">—</td>
<td align="left">TMDB media ID.</td>
</tr>
<tr>
<td align="left"><code>s</code></td>
<td align="left"><code>string | number</code></td>
<td align="left"><strong>No</strong></td>
<td align="left"><code>null</code></td>
<td align="left">Season number (required for TV shows).</td>
</tr>
<tr>
<td align="left"><code>e</code></td>
<td align="left"><code>string | number</code></td>
<td align="left"><strong>No</strong></td>
<td align="left"><code>null</code></td>
<td align="left">Episode number (required for TV shows).</td>
</tr>
</tbody>
</table>
<h4 id="example-response-format-1">Example Response Format</h4>
<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://sub.vdrk.site/v1/vtt/movie/155/English.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>
<span class="hljs-punctuation">{</span>
<span class="hljs-attr">"label"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Spanish"</span><span class="hljs-punctuation">,</span>
<span class="hljs-attr">"file"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"https://fed-subs.pstream.mov/subtitles/spanish.srt"</span><span class="hljs-punctuation">,</span>
<span class="hljs-attr">"type"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"srt"</span><span class="hljs-punctuation">,</span>
<span class="hljs-attr">"source"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"febbox"</span>
<span class="hljs-punctuation">}</span>
<span class="hljs-punctuation">]</span>
</code></pre>
<hr>
<h3 id="getdownloadsid-s-e"><code>getDownloads(id, s?, e?)</code></h3>
<p>Fetches direct downloadable media files (MP4/MKV) paired with resolution and file size descriptors.</p>
<pre><code class="hljs language-javascript"><span class="hljs-comment">// Movie downloads</span>
<span class="hljs-keyword">const</span> downloads = <span class="hljs-keyword">await</span> sdk.<span class="hljs-title function_">getDownloads</span>(<span class="hljs-string">"155"</span>);
<span class="hljs-comment">// TV Episode downloads</span>
<span class="hljs-keyword">const</span> tvDownloads = <span class="hljs-keyword">await</span> sdk.<span class="hljs-title function_">getDownloads</span>(<span class="hljs-string">"1399"</span>, <span class="hljs-number">1</span>, <span class="hljs-number">1</span>);
</code></pre>
<h4 id="example-response-format-2">Example Response Format</h4>
<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://download-node.com/file/the-dark-knight-1080p.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>
<hr>
<h2 id="provider--health-management">Provider &#x26; Health Management</h2>
<h3 id="getsourcesexcludedisabled"><code>getSources(excludeDisabled?)</code></h3>
<p>Retrieves provider configurations registered in the SDK.</p>
<pre><code class="hljs language-javascript"><span class="hljs-comment">// Get all sources including temporarily disabled ones</span>
<span class="hljs-keyword">const</span> allSources = sdk.<span class="hljs-title function_">getSources</span>(<span class="hljs-literal">false</span>);
<span class="hljs-comment">// Get only active/enabled sources</span>
<span class="hljs-keyword">const</span> activeSources = sdk.<span class="hljs-title function_">getSources</span>(<span class="hljs-literal">true</span>);
</code></pre>
<h4 id="parameters-2">Parameters</h4>
<table>
<thead>
<tr>
<th align="left">Parameter</th>
<th align="left">Type</th>
<th align="left">Required</th>
<th align="left">Default</th>
<th align="left">Description</th>
</tr>
</thead>
<tbody>
<tr>
<td align="left"><code>excludeDisabled</code></td>
<td align="left"><code>boolean</code></td>
<td align="left"><strong>No</strong></td>
<td align="left"><code>false</code></td>
<td align="left">When <code>true</code>, filters out providers marked as disabled
(<code>disabled: true</code>).</td>
</tr>
</tbody>
</table>
<hr>
<h3 id="probesourcekey"><code>probeSource(key)</code></h3>
<p>Executes a health ping test against a single provider to determine current availability and latency using a
probe payload (<code>TMDB ID: 155</code>).</p>
<pre><code class="hljs language-javascript"><span class="hljs-keyword">const</span> health = <span class="hljs-keyword">await</span> sdk.<span class="hljs-title function_">probeSource</span>(<span class="hljs-string">"vidrock"</span>);
<span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(health);
<span class="hljs-comment">// Output: { ok: true, ms: 840 }</span>
</code></pre>
<hr>
<h3 id="probeallsources"><code>probeAllSources()</code></h3>
<p>Executes health tests in parallel across all non-disabled provider modules.</p>
<pre><code class="hljs language-javascript"><span class="hljs-keyword">const</span> results = <span class="hljs-keyword">await</span> sdk.<span class="hljs-title function_">probeAllSources</span>();
<span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(results);
</code></pre>
<h4 id="example-response-format-3">Example Response Format</h4>
<pre><code class="hljs language-json"><span class="hljs-punctuation">{</span>
<span class="hljs-attr">"sourcename1"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span> <span class="hljs-attr">"ok"</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">true</span></span><span class="hljs-punctuation">,</span> <span class="hljs-attr">"ms"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">420</span> <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span>
<span class="hljs-attr">"sourcename2"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span> <span class="hljs-attr">"ok"</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">true</span></span><span class="hljs-punctuation">,</span> <span class="hljs-attr">"ms"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1150</span> <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span>
<span class="hljs-attr">"sourcename3"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span> <span class="hljs-attr">"ok"</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">false</span></span><span class="hljs-punctuation">,</span> <span class="hljs-attr">"ms"</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">null</span></span> <span class="hljs-punctuation">}</span>
<span class="hljs-punctuation">}</span>
</code></pre>
<hr>
<h2 id="provider-metadata-properties">Provider Metadata Properties</h2>
<p>When calling <code>getSources()</code>, each provider entry returns metadata describing its features and
streaming behavior:</p>
<pre><code class="hljs language-javascript">{
<span class="hljs-attr">key</span>: <span class="hljs-string">'example'</span>,
<span class="hljs-attr">label</span>: <span class="hljs-string">'Example'</span>,
<span class="hljs-attr">sourceFile</span>: <span class="hljs-string">'example'</span>,
<span class="hljs-attr">proxyParam</span>: <span class="hljs-string">'ex'</span>,
<span class="hljs-attr">timeout</span>: <span class="hljs-number">20000</span>,
<span class="hljs-attr">jitter</span>: <span class="hljs-number">800</span>,
<span class="hljs-attr">retries</span>: <span class="hljs-number">3</span>,
<span class="hljs-attr">multiUrl</span>: <span class="hljs-literal">true</span>,
<span class="hljs-attr">skipProxy</span>: <span class="hljs-literal">false</span>,
<span class="hljs-attr">cdnHeaders</span>: [{
<span class="hljs-attr">pattern</span>: <span class="hljs-regexp">/./</span>,
<span class="hljs-attr">headers</span>: {
<span class="hljs-title class_">Referer</span>: <span class="hljs-string">'https://example.com/'</span>,
<span class="hljs-title class_">Origin</span>: <span class="hljs-string">'https://example.com'</span>
}
}]
}
</code></pre>
<h3 id="property-reference">Property Reference</h3>
<table>
<thead>
<tr>
<th align="left">Field</th>
<th align="left">Type</th>
<th align="left">Description</th>
</tr>
</thead>
<tbody>
<tr>
<td align="left"><code>key</code></td>
<td align="left"><code>string</code></td>
<td align="left">Unique identifier used in <code>getStream()</code> and <code>probeSource()</code>.</td>
</tr>
<tr>
<td align="left"><code>label</code></td>
<td align="left"><code>string</code></td>
<td align="left">Human-readable name for UI display.</td>
</tr>
<tr>
<td align="left"><code>skipProxy</code></td>
<td align="left"><code>boolean</code></td>
<td align="left">When <code>true</code>, stream URLs can be fed directly to web video players (e.g.,
HLS.js, Video.js) without needing an intermediate backend proxy.</td>
</tr>
<tr>
<td align="left"><code>multiUrl</code></td>
<td align="left"><code>boolean</code></td>
<td align="left">Indicates whether the source returns multiple stream resolution choices.</td>
</tr>
<tr>
<td align="left"><code>timeout</code></td>
<td align="left"><code>number</code></td>
<td align="left">Internal request timeout threshold in milliseconds.</td>
</tr>
<tr>
<td align="left"><code>retries</code></td>
<td align="left"><code>number</code></td>
<td align="left">Max automatic retry attempts executed by health/stream scrapers.</td>
</tr>
<tr>
<td align="left"><code>cdnHeaders</code></td>
<td align="left"><code>Array</code></td>
<td align="left">Regex pattern matching rules detailing required request headers (<code>Referer</code>,
<code>Origin</code>) needed to playback stream segments without <code>403 Forbidden</code> errors.
</td>
</tr>
<tr>
<td align="left"><code>disabled</code></td>
<td align="left"><code>boolean</code></td>
<td align="left">Set to <code>true</code> if a source is down or undergoing maintenance.</td>
</tr>
</tbody>
</table>
<hr>
<h2 id="practical-expressjs-backend-example">Practical Express.js Backend Example</h2>
<p>Here is an example showing how to serve stream links to your client web application while handling custom CDN
playback headers and subtitle merging:</p>
<pre><code class="hljs language-javascript"><span class="hljs-keyword">import</span> express <span class="hljs-keyword">from</span> <span class="hljs-string">"express"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-title class_">VylaSDK</span> <span class="hljs-keyword">from</span> <span class="hljs-string">"@vyla-entertainment/sdk"</span>;
<span class="hljs-keyword">const</span> app = <span class="hljs-title function_">express</span>();
<span class="hljs-keyword">const</span> sdk = <span class="hljs-keyword">new</span> <span class="hljs-title class_">VylaSDK</span>({ <span class="hljs-attr">tmdbApiKey</span>: process.<span class="hljs-property">env</span>.<span class="hljs-property">TMDB_API_KEY</span> });
<span class="hljs-comment">// Scrape stream route</span>
app.<span class="hljs-title function_">get</span>(<span class="hljs-string">"/api/stream"</span>, <span class="hljs-title function_">async</span> (req, res) => {
<span class="hljs-keyword">const</span> { provider, tmdbId, season, episode } = req.<span class="hljs-property">query</span>;
<span class="hljs-keyword">const</span> clientIP = req.<span class="hljs-property">headers</span>[<span class="hljs-string">"x-forwarded-for"</span>] || req.<span class="hljs-property">socket</span>.<span class="hljs-property">remoteAddress</span>;
<span class="hljs-keyword">try</span> {
<span class="hljs-keyword">const</span> streamData = <span class="hljs-keyword">await</span> sdk.<span class="hljs-title function_">getStream</span>(
provider,
tmdbId,
season || <span class="hljs-literal">null</span>,
episode || <span class="hljs-literal">null</span>,
clientIP
);
<span class="hljs-keyword">if</span> (!streamData || !streamData.<span class="hljs-property">url</span>) {
<span class="hljs-keyword">return</span> res.<span class="hljs-title function_">status</span>(<span class="hljs-number">404</span>).<span class="hljs-title function_">json</span>({ <span class="hljs-attr">error</span>: <span class="hljs-string">"No stream found for this provider"</span> });
}
<span class="hljs-keyword">return</span> res.<span class="hljs-title function_">json</span>({
<span class="hljs-attr">success</span>: <span class="hljs-literal">true</span>,
provider,
<span class="hljs-attr">stream</span>: streamData,
});
} <span class="hljs-keyword">catch</span> (error) {
<span class="hljs-keyword">return</span> res.<span class="hljs-title function_">status</span>(<span class="hljs-number">500</span>).<span class="hljs-title function_">json</span>({ <span class="hljs-attr">error</span>: error.<span class="hljs-property">message</span> });
}
});
<span class="hljs-comment">// Fetch subtitles route</span>
app.<span class="hljs-title function_">get</span>(<span class="hljs-string">"/api/subtitles"</span>, <span class="hljs-title function_">async</span> (req, res) => {
<span class="hljs-keyword">const</span> { tmdbId, season, episode } = req.<span class="hljs-property">query</span>;
<span class="hljs-keyword">try</span> {
<span class="hljs-keyword">const</span> subtitles = <span class="hljs-keyword">await</span> sdk.<span class="hljs-title function_">getSubtitles</span>(
tmdbId,
season || <span class="hljs-literal">null</span>,
episode || <span class="hljs-literal">null</span>
);
<span class="hljs-keyword">return</span> res.<span class="hljs-title function_">json</span>({ subtitles });
} <span class="hljs-keyword">catch</span> (error) {
<span class="hljs-keyword">return</span> res.<span class="hljs-title function_">status</span>(<span class="hljs-number">500</span>).<span class="hljs-title function_">json</span>({ <span class="hljs-attr">subtitles</span>: [] });
}
});
app.<span class="hljs-title function_">listen</span>(<span class="hljs-number">3000</span>, <span class="hljs-function">() =></span> <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">"Vyla SDK Server running on port 3000"</span>));
</code></pre>
<h2 id="sdk-vs-self-hosted-api">SDK vs Self-Hosted API</h2>
<p>The SDK and the self-hosted API server provide the same streaming functionality but in different deployment
models:</p>
<table>
<thead>
<tr>
<th>Aspect</th>
<th>SDK</th>
<th>Self-Hosted API</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Deployment</strong></td>
<td>Embedded in your Node.js app</td>
<td>Separate server process</td>
</tr>
<tr>
<td><strong>Network</strong></td>
<td>Direct source CDN access</td>
<td>Built-in proxy for CORS</td>
</tr>
<tr>
<td><strong>Rate Limiting</strong></td>
<td>None (you control it)</td>
<td>Per-API-key limits</td>
</tr>
<tr>
<td><strong>Setup</strong></td>
<td>npm install + init</td>
<td>Clone repo + configure</td>
</tr>
<tr>
<td><strong>Use Case</strong></td>
<td>Existing Node.js apps</td>
<td>Standalone streaming service</td>
</tr>
</tbody>
</table>
<p>Choose the SDK if you want to integrate streaming directly into an existing Node.js application. Choose the
self-hosted API if you want a standalone streaming service that can be used by multiple clients.</p>
</main>
<aside id="toc">
<p class="toc-title">On this page</p>
<ul>
<li><a href="#features">Features</a></li>
<li><a href="#installation">Installation</a></li>
<li><a href="#initialization">Initialization</a></li>
<li><a href="#core-methods">Core Methods</a></li>
<li><a href="#provider--health-management">Provider & Health Management</a></li>
<li><a href="#provider-metadata-properties">Provider Metadata Properties</a></li>
<li><a href="#practical-expressjs-backend-example">Practical Express.js Backend Example</a></li>
<li><a href="#sdk-vs-self-hosted-api">SDK vs Self-Hosted API</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>

346
sdk/vyla-sdk/index.md Normal file
View File

@@ -0,0 +1,346 @@
# SDK Reference
<p class="page-description">Scrape video streams, fetch subtitles, retrieve download links, and monitor provider health directly from your Node.js application.</p>
The **Vyla SDK** (`@vyla-entertainment/sdk`) is a server-side JavaScript/TypeScript package that provides direct access to Vyla's stream resolvers, health probes, download resolvers, and subtitle providers.
By embedding the SDK directly into your backend application, provider resolution executes in your own environment. It is an alternative to operating the Stream API as a separate process: your application owns its request boundary, caching, authorization, timeouts, and response shape.
:::callout-note
The SDK can be used independently of the self-hosted Stream API. If you prefer to embed Vyla capabilities in an existing Node.js application rather than run a separate HTTP service, begin with the [SDK quickstart](/sdk/vyla-sdk/quickstart).
:::
---
## Features
- **Direct-to-Client Playback**: Scraped streams link directly to source CDNs. No proxy server overhead required unless specified by provider headers.
- **Built-In Health Probing**: Monitor latency and uptime per provider with built-in health check utilities.
- **Automated Anime Sub/Dub Routing**: Automatically configures audio track requirements (`sub` vs `dub`) based on provider naming conventions.
- **Multi-Source Subtitle Aggregation**: Merges subtitle tracks from multiple redundant subtitle mirrors into a unified VTT/SRT format.
- **Metadata Fallbacks**: Automatically maps TMDB IDs to external IDs (such as IMDB and AniList) for smooth stream lookups.
- **Self-Contained**: No need to deploy a separate API server — all streaming functionality runs within your existing Node.js application.
---
## Installation
Install the package into your Node.js backend using your package manager of choice:
:::code-group
```bash [npm]
npm install @vyla-entertainment/sdk
```
```bash [pnpm]
pnpm install @vyla-entertainment/sdk
```
```bash [yarn]
yarn add @vyla-entertainment/sdk
```
:::
> **Note**: Node.js **v18+** is required due to modern standard `fetch` and `AbortSignal.timeout` usage.
---
## Initialization
Initialize the `VylaSDK` client by supplying your **TMDB API Key**. The key resolves media metadata, validates movie/TV show IDs, and translates TMDB IDs into AniList IDs for anime sources.
You may optionally provide a pengu.uk manifest URL to enable the Pengu source.
```javascript
import VylaSDK from "@vyla-entertainment/sdk";
const sdk = new VylaSDK({
tmdbApiKey: process.env.TMDB_API_KEY,
penguManifest: "" // Optional, get from pengu.uk
});
```
---
## Core Methods
### `getStream(key, id, s?, e?, clientIP?)`
Scrapes video stream URLs and playability headers for a specified provider key.
```javascript
// Fetch a movie stream (TMDB ID: 155 - The Dark Knight)
const movieStream = await sdk.getStream("sourcename", "155");
// Fetch a TV show episode (TMDB ID: 1399, Season 1, Episode 1)
const tvStream = await sdk.getStream("sourcename", "1399", 1, 1);
// Pass client IP for providers that use geo-verification
const geoStream = await sdk.getStream("sourcename", "155", null, null, "203.0.113.195");
```
#### Parameters
| Parameter | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `key` | `string` | **Yes** | — | Unique key identifier for the provider (e.g. `"sourcename"`). |
| `id` | `string` | **Yes** | — | TMDB media ID (e.g., `"155"`). |
| `s` | `string \| number` | **No** | `null` | Season number (required for TV shows). |
| `e` | `string \| number` | **No** | `null` | Episode number (required for TV shows). |
| `clientIP` | `string` | **No** | `null` | End-user IP address forwarded for geo-restricted scrapers. |
#### Example Response Format
```json
{
"url": "https://example-cdn.com/hls/manifest.m3u8",
"quality": "auto",
"isM3U8": true,
"headers": {
"Referer": "https://example.com/",
"Origin": "https://example.com"
},
"allUrls": [
{
"url": "https://example-cdn.com/hls/manifest.m3u8",
"quality": "1080p",
"isM3U8": true
}
]
}
```
---
### `getSubtitles(id, s?, e?)`
Fetches subtitle files aggregated across multiple subtitle backend mirrors (`v1`, `v2`, `febbox`). Returns clean VTT and SRT tracks.
```javascript
// Fetch movie subtitles
const movieSubs = await sdk.getSubtitles("155");
// Fetch TV show episode subtitles
const tvSubs = await sdk.getSubtitles("1399", 1, 1);
```
#### Parameters
| Parameter | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `id` | `string` | **Yes** | — | TMDB media ID. |
| `s` | `string \| number` | **No** | `null` | Season number (required for TV shows). |
| `e` | `string \| number` | **No** | `null` | Episode number (required for TV shows). |
#### Example Response Format
```json
[
{
"label": "English",
"file": "https://sub.vdrk.site/v1/vtt/movie/155/English.vtt",
"type": "vtt",
"source": "v1"
},
{
"label": "Spanish",
"file": "https://fed-subs.pstream.mov/subtitles/spanish.srt",
"type": "srt",
"source": "febbox"
}
]
```
---
### `getDownloads(id, s?, e?)`
Fetches direct downloadable media files (MP4/MKV) paired with resolution and file size descriptors.
```javascript
// Movie downloads
const downloads = await sdk.getDownloads("155");
// TV Episode downloads
const tvDownloads = await sdk.getDownloads("1399", 1, 1);
```
#### Example Response Format
```json
[
{
"url": "https://download-node.com/file/the-dark-knight-1080p.mp4",
"quality": "1080p",
"size": "2.14 GB",
"format": "MP4"
}
]
```
---
## Provider & Health Management
### `getSources(excludeDisabled?)`
Retrieves provider configurations registered in the SDK.
```javascript
// Get all sources including temporarily disabled ones
const allSources = sdk.getSources(false);
// Get only active/enabled sources
const activeSources = sdk.getSources(true);
```
#### Parameters
| Parameter | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `excludeDisabled` | `boolean` | **No** | `false` | When `true`, filters out providers marked as disabled (`disabled: true`). |
---
### `probeSource(key)`
Executes a health ping test against a single provider to determine current availability and latency using a probe payload (`TMDB ID: 155`).
```javascript
const health = await sdk.probeSource("vidrock");
console.log(health);
// Output: { ok: true, ms: 840 }
```
---
### `probeAllSources()`
Executes health tests in parallel across all non-disabled provider modules.
```javascript
const results = await sdk.probeAllSources();
console.log(results);
```
#### Example Response Format
```json
{
"sourcename1": { "ok": true, "ms": 420 },
"sourcename2": { "ok": true, "ms": 1150 },
"sourcename3": { "ok": false, "ms": null }
}
```
---
## Provider Metadata Properties
When calling `getSources()`, each provider entry returns metadata describing its features and streaming behavior:
```javascript
{
key: 'example',
label: 'Example',
sourceFile: 'example',
proxyParam: 'ex',
timeout: 20000,
jitter: 800,
retries: 3,
multiUrl: true,
skipProxy: false,
cdnHeaders: [{
pattern: /./,
headers: {
Referer: 'https://example.com/',
Origin: 'https://example.com'
}
}]
}
```
### Property Reference
| Field | Type | Description |
| --- | --- | --- |
| `key` | `string` | Unique identifier used in `getStream()` and `probeSource()`. |
| `label` | `string` | Human-readable name for UI display. |
| `skipProxy` | `boolean` | When `true`, stream URLs can be fed directly to web video players (e.g., HLS.js, Video.js) without needing an intermediate backend proxy. |
| `multiUrl` | `boolean` | Indicates whether the source returns multiple stream resolution choices. |
| `timeout` | `number` | Internal request timeout threshold in milliseconds. |
| `retries` | `number` | Max automatic retry attempts executed by health/stream scrapers. |
| `cdnHeaders` | `Array` | Regex pattern matching rules detailing required request headers (`Referer`, `Origin`) needed to playback stream segments without `403 Forbidden` errors. |
| `disabled` | `boolean` | Set to `true` if a source is down or undergoing maintenance. |
---
## Practical Express.js Backend Example
Here is an example showing how to serve stream links to your client web application while handling custom CDN playback headers and subtitle merging:
```javascript
import express from "express";
import VylaSDK from "@vyla-entertainment/sdk";
const app = express();
const sdk = new VylaSDK({ tmdbApiKey: process.env.TMDB_API_KEY });
app.get("/api/stream", async (req, res) => {
const { provider, tmdbId, season, episode } = req.query;
const clientIP = req.headers["x-forwarded-for"] || req.socket.remoteAddress;
try {
const streamData = await sdk.getStream(
provider,
tmdbId,
season || null,
episode || null,
clientIP
);
if (!streamData || !streamData.url) {
return res.status(404).json({ error: "No stream found for this provider" });
}
return res.json({
success: true,
provider,
stream: streamData,
});
} catch (error) {
return res.status(500).json({ error: error.message });
}
});
app.get("/api/subtitles", async (req, res) => {
const { tmdbId, season, episode } = req.query;
try {
const subtitles = await sdk.getSubtitles(
tmdbId,
season || null,
episode || null
);
return res.json({ subtitles });
} catch (error) {
return res.status(500).json({ subtitles: [] });
}
});
app.listen(3000, () => console.log("Vyla SDK Server running on port 3000"));
```
## SDK vs Self-Hosted API
The SDK and the self-hosted API server provide the same streaming functionality but in different deployment models:
| Aspect | SDK | Self-Hosted API |
| --- | --- | --- |
| **Deployment** | Embedded in your Node.js app | Separate server process |
| **Network** | Direct source CDN access | Built-in proxy for CORS |
| **Rate Limiting** | None (you control it) | Per-API-key limits |
| **Setup** | npm install + init | Clone repo + configure |
| **Use Case** | Existing Node.js apps | Standalone streaming service |
Choose the SDK if you want to integrate streaming directly into an existing Node.js application. Choose the self-hosted API if you want a standalone streaming service that can be used by multiple clients.

View File

@@ -0,0 +1,82 @@
# SDK quickstart
<p class="page-description">Install Vyla SDK, resolve a source, and build a safe server-side integration.</p>
## Before you start
Vyla SDK is for **server-side Node.js**. It contains provider integrations and makes outbound requests from the process where you install it. Do not import it into a browser bundle or expose provider results, credentials, or internal source keys directly to untrusted clients.
You need Node.js 18 or later and, for sources that resolve title metadata, a TMDB API key.
## Install and initialize
:::code-group
```bash [npm]
npm install @vyla-entertainment/sdk
```
```bash [pnpm]
pnpm install @vyla-entertainment/sdk
```
```bash [yarn]
yarn add @vyla-entertainment/sdk
```
:::
```javascript
import VylaSDK from "@vyla-entertainment/sdk";
const vyla = new VylaSDK({
tmdbApiKey: process.env.TMDB_API_KEY,
penguManifest: process.env.PENGU_MANIFEST
});
```
:::callout-warning
Keep `TMDB_API_KEY` and any deployment configuration in server-side environment variables. Never commit them or send them to a client.
:::
## Resolve a movie
First inspect active providers. Provider availability changes over time, so select a key from the running configuration instead of hard-coding a placeholder.
```javascript
const providers = vyla.getSources(true);
console.table(providers.map(({ key, label }) => ({ key, label })));
const provider = providers[0];
const result = await vyla.getStream(provider.key, 155);
const candidates = result.allUrls ?? [result];
console.log(candidates.map(({ url, type, quality }) => ({ url, type, quality })));
```
`155` is a TMDB movie ID. For an episode, include season and episode:
```javascript
const result = await vyla.getStream(provider.key, 1396, 1, 1);
```
## Make it resilient
Provider results are inherently variable: a source can fail, time out, return more than one candidate, or be temporarily disabled. In production:
1. Probe sources or maintain a recent availability signal before choosing one.
2. Give every provider call a request deadline in your application.
3. Treat `allUrls` as fallback candidates, not a guarantee that every URL will play.
4. Return only the fields your client needs and validate requests at your own API boundary.
5. Cache and rate-limit at your boundary to avoid repeatedly triggering upstream work.
```javascript
const health = await vyla.probeAllSources();
const usableKey = Object.entries(health).find(([, result]) => result.ok)?.[0];
if (!usableKey) throw new Error("No providers are currently available");
const stream = await vyla.getStream(usableKey, 155);
```
## Next steps
- Read the complete [SDK reference](/sdk) for methods and return shapes.
- Learn provider flags, timeout behavior, and source keys in [Providers](/concepts/providers).
- Choose [Stream API](/stream-api/overview) only when an SSE HTTP interface better fits your client architecture.

View File

@@ -1,280 +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>Self-host Stream API — Vyla</title>
<meta name="description" content="Deploy the optional Stream API when your clients need an HTTP/SSE layer." />
<meta name="keywords"
content="self-hosting, Stream API deployment, Node.js deployment, HTTP API, SSE, server setup, infrastructure" />
<link rel="canonical" href="https://vyla.cc/self-hosting.html" />
<!-- Open Graph / Facebook -->
<meta property="og:type" content="website" />
<meta property="og:url" content="https://vyla.cc/self-hosting.html" />
<meta property="og:title" content="Self-host Stream API — Vyla" />
<meta property="og:description" content="Deploy the optional Stream API when your clients need an HTTP/SSE layer." />
<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/self-hosting.html" />
<meta property="twitter:title" content="Self-host Stream API — Vyla" />
<meta property="twitter:description"
content="Deploy the optional Stream API when your clients need an HTTP/SSE layer." />
<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": "Self-host Stream API",
"description": "Deploy the optional Stream API when your clients need an HTTP/SSE layer.",
"url": "https://vyla.cc/self-hosting.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="" href="/stream-api/operations">Operations</a></li>
</ul>
</div>
<div class="group">
<p class="group-title">Deployment</p>
<ul>
<li><a class="active" href="/self-hosting">Self Hosting</a></li>
</ul>
</div>
</nav>
<div id="content-col">
<main class="prose">
<h1>Self-host Stream API</h1>
<p class="page-description">Deploy the optional Stream API when your clients need an HTTP/SSE layer.</p>
<h2 id="overview">Overview</h2>
<p>Stream API is a <strong>self-hosted Node.js</strong> 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 <code>cluster</code> module. No database is required by the
service itself.</p>
<div class="callout callout-warning">
<div class="callout-content">
<p>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 <a href="/sdk">Vyla SDK</a>
directly.</p>
</div>
</div>
<h2 id="requirements">Requirements</h2>
<ul>
<li>Node.js 18 or later</li>
<li>A TMDB API key if you want title validation and metadata lookups</li>
</ul>
<h2 id="environment-variables">Environment variables</h2>
<p>Create a <code>.env</code> file in the project root:</p>
<pre><code class="hljs language-bash">TMDB_API_KEY=
PORT=7860
WORKER_COUNT=1
ENABLE_DEBUG_ROUTE=<span class="hljs-literal">false</span>
PROXY_STREAMS=<span class="hljs-literal">false</span>
PROXY_URL=
<span class="hljs-comment"># Optional telemetry configuration</span>
GA_MEASUREMENT_ID=
GA_API_SECRET=
</code></pre>
<div class="card">
<div class="card-title">Variable reference</div>
<div class="card-body">
<table>
<thead>
<tr>
<th>Variable</th>
<th>Required</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>TMDB_API_KEY</code></td>
<td>Yes</td>
<td>Enables TMDB-based ID validation, metadata lookups, and anime detection. Some sources will not
work without it.</td>
</tr>
<tr>
<td><code>GA_MEASUREMENT_ID</code> / <code>GA_API_SECRET</code></td>
<td>No</td>
<td>Enables Google Analytics event reporting. Leave blank to disable.</td>
</tr>
<tr>
<td><code>PORT</code></td>
<td>No</td>
<td>Port the server listens on. Defaults to <code>7860</code>.</td>
</tr>
<tr>
<td><code>WORKER_COUNT</code></td>
<td>No</td>
<td>Number of cluster workers to fork. Defaults to <code>1</code> locally.</td>
</tr>
<tr>
<td><code>ENABLE_DEBUG_ROUTE</code></td>
<td>No</td>
<td>Set to <code>true</code> to enable <code>/api/debug/:id</code>. Keep this <code>false</code> in
production — it exposes upstream request/response headers and raw source output.</td>
</tr>
<tr>
<td><code>PROXY_STREAMS</code></td>
<td>No</td>
<td>Set to <code>true</code> to route stream playback through your own server's <code>/api</code>
proxy endpoint instead of direct URLs.</td>
</tr>
<tr>
<td><code>PROXY_URL</code></td>
<td>No</td>
<td>An external proxy base URL to use instead of the built-in <code>/api</code> proxy. Only used if
<code>PROXY_STREAMS</code> is enabled.
</td>
</tr>
</tbody>
</table>
</div>
</div>
<h2 id="installing-and-running">Installing and running</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
node server.js
</code></pre>
<p>The server listens on:</p>
<pre><code>http://localhost:7860
</code></pre>
<p>Visit <code>/health</code> to confirm it is running and check which sources are currently reachable from your
host.</p>
<h2 id="running-with-multiple-workers">Running with multiple workers</h2>
<p>Setting <code>WORKER_COUNT</code> above <code>1</code> forks additional worker processes via
<code>cluster</code>.
</p>
<p>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.</p>
<pre><code class="hljs language-bash">WORKER_COUNT=4
node server.js
</code></pre>
<p>Each worker independently loads source modules and handles outbound requests. Only shared cache operations
and concurrency limits are coordinated through the primary process.</p>
<h2 id="network-considerations">Network considerations</h2>
<p>Several sources are documented in <code>config.js</code> as blocked on datacenter IPs. This commonly affects
cloud hosting providers and VPS environments.</p>
<p>If a source's <code>getStream</code> 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.</p>
<p>When running on a local desktop environment, you should have minimal issues with source availability since
you're using a residential IP address.</p>
<h2 id="reverse-proxy--tls">Reverse proxy / TLS</h2>
<p>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 <code>PORT</code>.</p>
<p>CORS is already configured at the application layer with:</p>
<pre><code>Access-Control-Allow-Origin: *
</code></pre>
<p>No additional CORS configuration is required on the proxy.</p>
<h2 id="verifying-your-deployment">Verifying your deployment</h2>
<p>Run these checks in order:</p>
<ol>
<li>
<p><code>GET /health</code></p>
<p>Confirms the process is running and reports per-source reachability from your host.</p>
</li>
<li>
<p><code>GET /api/test/155?source=vidrock</code></p>
<p>Runs a real source resolution using a known TMDB movie ID and returns whether the source succeeded.</p>
</li>
<li>
<p><code>GET /api/debug/155?source=vidrock</code></p>
<p>Only available when <code>ENABLE_DEBUG_ROUTE=true</code>.</p>
<p>Returns request traces, headers, and raw candidate URLs for debugging source failures.</p>
</li>
</ol>
<div class="callout callout-warning">
<div class="callout-content">
<p>Leave <code>ENABLE_DEBUG_ROUTE</code> disabled on public deployments. The debug route exposes upstream
request information and raw stream URLs, which can leak internal details.</p>
</div>
</div>
</main>
<aside id="toc">
<p class="toc-title">On this page</p>
<ul>
<li><a href="#overview">Overview</a></li>
<li><a href="#requirements">Requirements</a></li>
<li><a href="#environment-variables">Environment variables</a></li>
<li><a href="#installing-and-running">Installing and running</a></li>
<li><a href="#running-with-multiple-workers">Running with multiple workers</a></li>
<li><a href="#network-considerations">Network considerations</a></li>
<li><a href="#reverse-proxy--tls">Reverse proxy / TLS</a></li>
<li><a href="#verifying-your-deployment">Verifying your deployment</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

@@ -2,92 +2,110 @@
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
<loc>https://vyla.cc/</loc>
<changefreq>daily</changefreq>
<lastmod>2025-02-15</lastmod>
<changefreq>weekly</changefreq>
<priority>1.0</priority>
</url>
<url>
<loc>https://vyla.cc/architecture.html</loc>
<changefreq>weekly</changefreq>
<loc>https://vyla.cc/start-here/architecture</loc>
<lastmod>2025-02-15</lastmod>
<changefreq>monthly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>https://vyla.cc/home.html</loc>
<loc>https://vyla.cc/products/home</loc>
<lastmod>2025-02-15</lastmod>
<changefreq>weekly</changefreq>
<priority>0.9</priority>
</url>
<url>
<loc>https://vyla.cc/go.html</loc>
<loc>https://vyla.cc/products/go</loc>
<lastmod>2025-02-15</lastmod>
<changefreq>weekly</changefreq>
<priority>0.9</priority>
</url>
<url>
<loc>https://vyla.cc/tv.html</loc>
<loc>https://vyla.cc/products/tv</loc>
<lastmod>2025-02-15</lastmod>
<changefreq>weekly</changefreq>
<priority>0.9</priority>
</url>
<url>
<loc>https://vyla.cc/player.html</loc>
<loc>https://vyla.cc/products/player</loc>
<lastmod>2025-02-15</lastmod>
<changefreq>monthly</changefreq>
<priority>0.7</priority>
</url>
<url>
<loc>https://vyla.cc/concepts/providers</loc>
<lastmod>2025-02-15</lastmod>
<changefreq>weekly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>https://vyla.cc/sdk.html</loc>
<loc>https://vyla.cc/sdk</loc>
<lastmod>2025-02-15</lastmod>
<changefreq>weekly</changefreq>
<priority>0.9</priority>
</url>
<url>
<loc>https://vyla.cc/sdk-quickstart.html</loc>
<loc>https://vyla.cc/sdk/vyla-sdk/quickstart</loc>
<lastmod>2025-02-15</lastmod>
<changefreq>monthly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>https://vyla.cc/stream-api/overview</loc>
<lastmod>2025-02-15</lastmod>
<changefreq>weekly</changefreq>
<priority>0.9</priority>
</url>
<url>
<loc>https://vyla.cc/stream-api/overview.html</loc>
<changefreq>weekly</changefreq>
<priority>0.9</priority>
</url>
<url>
<loc>https://vyla.cc/stream-api/paths.html</loc>
<changefreq>weekly</changefreq>
<loc>https://vyla.cc/stream-api/reference/paths</loc>
<lastmod>2025-02-15</lastmod>
<changefreq>monthly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>https://vyla.cc/stream-api/media-data.html</loc>
<changefreq>weekly</changefreq>
<loc>https://vyla.cc/stream-api/reference/media-data</loc>
<lastmod>2025-02-15</lastmod>
<changefreq>monthly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>https://vyla.cc/stream-api/operations.html</loc>
<changefreq>weekly</changefreq>
<loc>https://vyla.cc/stream-api/reference/operations</loc>
<lastmod>2025-02-15</lastmod>
<changefreq>monthly</changefreq>
<priority>0.7</priority>
</url>
<url>
<loc>https://vyla.cc/stream-api/deployment/self-hosting</loc>
<lastmod>2025-02-15</lastmod>
<changefreq>monthly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>https://vyla.cc/self-hosting.html</loc>
<changefreq>weekly</changefreq>
<priority>0.9</priority>
</url>
<url>
<loc>https://vyla.cc/concepts/providers.html</loc>
<changefreq>weekly</changefreq>
<loc>https://vyla.cc/guides/integrations</loc>
<lastmod>2025-02-15</lastmod>
<changefreq>monthly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>https://vyla.cc/guides/integrations.html</loc>
<changefreq>weekly</changefreq>
<priority>0.8</priority>
<loc>https://vyla.cc/guides/error-handling</loc>
<lastmod>2025-02-15</lastmod>
<changefreq>monthly</changefreq>
<priority>0.7</priority>
</url>
<url>
<loc>https://vyla.cc/guides/error-handling.html</loc>
<changefreq>weekly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>https://vyla.cc/misc/credit.html</loc>
<loc>https://vyla.cc/misc/project/credit</loc>
<lastmod>2025-02-15</lastmod>
<changefreq>monthly</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://vyla.cc/misc/dmca.html</loc>
<loc>https://vyla.cc/misc/project/dmca</loc>
<lastmod>2025-02-15</lastmod>
<changefreq>monthly</changefreq>
<priority>0.5</priority>
<priority>0.6</priority>
</url>
</urlset>

View File

@@ -0,0 +1,38 @@
# Our Architecture
<p class="page-description">How Vyla Home, SDK, and Stream API relate, and where each responsibility belongs.</p>
## The Vyla stack
Vyla separates local application management, provider resolution, and client-facing delivery. That separation lets each layer be used on its own while keeping the integration points clear.
### Vyla Home
Home is responsible for a desktop session: local configuration, encrypted local credentials, update coordination, managed processes, and the embedded interface. It is the right layer for launching and supervising a local experience.
### Vyla SDK
The SDK contains the provider-facing work: source configuration, stream resolution, subtitle retrieval, download lookup, and health probes. It is the right layer when your Node.js application needs to decide how requests are authorized, cached, selected, and returned.
### Stream API
Stream API wraps the SDK in a self-hosted HTTP service. Its movie and TV endpoints use Server-Sent Events so metadata can arrive first and usable sources can be delivered progressively. It also provides subtitle, download, health, test, and optional debug routes.
## Common deployment patterns
| Pattern | Best for | Key decision |
| --- | --- | --- |
| Home only | a managed local desktop experience | Home owns the lifecycle of bundled local services |
| App + SDK | a Node.js backend with a custom product API | your app owns authentication, policy, and response design |
| Player + Stream API | browser or mobile clients that need a stable HTTP contract | deploy Stream API and place it behind TLS when exposed remotely |
| Home + Stream API | a local player backed by a local service | keep the service bound and access-controlled for the local environment |
## Data flow for playback
1. A client provides a TMDB media ID (plus season and episode for TV).
2. Stream API, or your own SDK-based backend, asks active providers for candidates.
3. Candidates are checked and successful sources are returned progressively.
4. The player begins with the first compatible candidate and keeps later candidates as fallbacks.
5. Subtitle and download lookups are separate operations; neither should block first playback.
Provider availability varies by source and network environment. Build the player experience around fallbacks and empty-result states, as described in [Error handling](/guides/error-handling).

View File

@@ -0,0 +1,54 @@
# Vyla Entertainment
<p class="page-description">A community-built media project focused on freedom, privacy, and giving people control over their own experience.</p>
<img class="vyla-banner" src="/assets/images/devices.png" alt="A collage of films and shows available through a Vyla experience" loading="lazy" fetchpriority="low" />
## About Vyla
Vyla is a community-built media project created around a simple idea: **people should have control over how they experience their media.**
We started Vyla because we wanted something clean, private, and easy to use without relying on a single company or centralized service. What began as a small project has grown into a collection of applications and tools for desktop, Android, Android TV, and more.
Vyla is not a streaming service, and we do not host or provide the media itself. Instead, we build the software that brings the experience together while leaving control in the hands of the person using it.
## Where we come from
Vyla started as a small web project focused on creating a better way to discover and watch movies and shows. Over time, it grew into a broader project with applications for different platforms alongside the tools that power them.
We care about keeping Vyla open, accessible, and community-driven. Our project has also been shared by communities such as **FMHY** and **r/Piracy**, where people interested in alternative and self-hosted media solutions have discovered Vyla.
We are proud to be part of a wider community of people who build, share, and explore alternatives to traditional media platforms.
## What we make
Vyla is made up of several projects designed for different ways of using the platform.
| Platform | Description | Link |
| --- | --- | --- |
| Vyla Home | Desktop app | [Learn more](/products/home) |
| Vyla Go | Android app | [Learn more](/products/go) |
| Vyla TV | Android TV app | [Learn more](/products/tv) |
| Vyla SDK | Developer SDK | [Learn more](/sdk) |
| Stream API | Self-hosted API | [Learn more](/stream-api/overview) |
## Made for people, not platforms
We believe software should give people more control, not less.
That means keeping Vyla local and self-hostable, avoiding unnecessary centralized dependencies, and allowing developers and users to decide how they want to use the project.
Vyla can be as simple as a TV or mobile app, a desktop application, or the foundation for a completely custom media experience. **You decide how much of Vyla you use and where it runs.**
## Start exploring
- **On desktop?** See [Vyla Home](/products/home).
- **On Android?** See [Vyla Go](/products/go).
- **Using Android TV?** See [Vyla TV](/products/tv).
- **Building something with Vyla?** Start with the [SDK quickstart](/sdk/vyla-sdk/quickstart).
- **Building your own client?** See the [Stream API](/stream-api/overview).
- **Want to understand how everything fits together?** Visit [Our Architecture](/start-here/architecture).
:::callout-info
Vyla is self-hosted software and does not host or distribute media. Please respect applicable laws, upstream terms, and rights-holder requests. See the [DMCA policy](/misc/project/dmca) for removal information.
:::

17
start-here/who-are-we.md Normal file
View File

@@ -0,0 +1,17 @@
# Welcome to Vyla Documentation
This is the main documentation page for Vyla. Here you can find information about the product, its features, and how to use it.
You may wonder who **we are**?
Vyla started from the web project which initially intended to create a clean environment where one could stream movies and TV shows free from all the unnecessary elements. Originally, it was developed mainly as a frontend which embedded other players. With time I got involved in the process of developing the infrastructure.
Another core component of Vyla is Vyla API which dealt with searching streams from various providers, verifying their availability and playing them. Later on, it evolved into being able to cope with many kinds of streams, deal with possible issues related to their quality and reliability.
With constant changing and breakage of providers, I had to develop Vyla SDK. The purpose of it was to isolate the source implementations from the rest of Vyla so that they could be independently managed and used in other projects. I made each source the provider modules which had the same interface. Vyla SDK remains updated as sources change.
Among the main challenges associated with the development of Vyla was hosting. Initially I did self-hosting of frontend, API, player, Nginx, PM2 and other infrastructure on a VPS. Managing them was a struggle with constant downtime, provider issues, Cloudflare problems and, lastly the DMCA complains.
However, after receiving a DMCA strike and experiencing problems due to frequent reports in Cloudflare, I figured out that it wouldn’t be efficient to self host the streaming infrastructure. Therefore, the choice was made to focus more on SDK and the application rather than using a remote backend.
Thanks for Reading ❤️

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.

View File

@@ -1,157 +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>Vyla TV — Vyla</title>
<meta name="description"
content="The Android TV application for running the Vyla media experience on your television." />
<meta name="keywords"
content="Vyla TV, Android TV, TV streaming, smart TV, television app, media streaming, Android TV app" />
<link rel="canonical" href="https://vyla.cc/tv.html" />
<!-- Open Graph / Facebook -->
<meta property="og:type" content="website" />
<meta property="og:url" content="https://vyla.cc/tv.html" />
<meta property="og:title" content="Vyla TV — Vyla" />
<meta property="og:description"
content="The Android TV application for running the Vyla media experience on your television." />
<meta property="og:image" content="https://vyla.cc/assets/images/android-tv-preview.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/tv.html" />
<meta property="twitter:title" content="Vyla TV — Vyla" />
<meta property="twitter:description"
content="The Android TV application for running the Vyla media experience on your television." />
<meta property="twitter:image" content="https://vyla.cc/assets/images/android-tv-preview.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": "Vyla TV",
"description": "The Android TV application for running the Vyla media experience on your television.",
"url": "https://vyla.cc/tv.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">Start here</p>
<ul>
<li><a class="active" href="/">Introduction</a></li>
</li>
<li><a class="" href="/architecture">Architecture</a></li>
</ul>
</div>
<div class="group">
<p class="group-title">Products</p>
<ul>
<li><a class="" href="/home">Home</a></li>
<li><a class="" href="/go">Go</a></li>
<li><a class="active" href="/tv">Tv</a></li>
<li><a class="" href="/player">Player</a></li>
</ul>
</div>
<div class="group">
<p class="group-title">Concepts</p>
<ul>
<li><a class="" href="/concepts/providers">Providers</a></li>
</ul>
</div>
</nav>
<div id="content-col">
<main class="prose">
<h1>Vyla TV</h1>
<p class="page-description">The Android TV application for running the Vyla media experience on your television.
</p>
<img class="vyla-banner" src="/assets/images/android-tv/site-preview.png"
alt="A collage of films and shows available through a Vyla experience" loading="lazy" fetchpriority="low">
<h2 id="vyla-tv">Vyla TV</h2>
<p><strong>Vyla TV</strong> is an Android TV streaming app built with Jetpack Compose and ExoPlayer that lets
you browse and watch movies and TV shows from TMDB.</p>
<h2 id="downloads">Downloads</h2>
<p>Vyla TV is available for Android TV. Get the latest version below:</p>
<table>
<thead>
<tr>
<th>Platform</th>
<th>Download</th>
</tr>
</thead>
<tbody>
<tr>
<td>Android TV</td>
<td><a href="https://gitlab.com/vyla-entertainment/TV/-/releases">Download .apk</a></td>
</tr>
</tbody>
</table>
<p>Inside the releases page, you can find the latest version of Vyla TV for Android TV, find the
<code>.apk</code> file, and install it on your device.
</p>
</main>
<aside id="toc">
<p class="toc-title">On this page</p>
<ul>
<li><a href="#vyla-tv">Vyla TV</a></li>
<li><a href="#downloads">Downloads</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>