mirror of
https://gitlab.com/vyla-entertainment/documentation.git
synced 2026-11-14 22:47:26 -05:00
501 lines
14 KiB
Markdown
501 lines
14 KiB
Markdown
# SDK Reference
|
||
|
||
<p class="page-description">Scrape video streams, fetch subtitles, search manga catalogs, resolve chapter pages, 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.
|
||
- **Manga & Comic Aggregation**: Search title catalogs, inspect volume/chapter indexes, and resolve full reader image sets.
|
||
- **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 and reading 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);
|
||
```
|
||
|
||
#### 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
|
||
[
|
||
{
|
||
"url": "https://download-node.com/file/the-dark-knight-1080p.mp4",
|
||
"quality": "1080p",
|
||
"size": "2.14 GB",
|
||
"format": "MP4"
|
||
}
|
||
]
|
||
```
|
||
|
||
---
|
||
|
||
### `searchManga(query, page?, perPage?)`
|
||
|
||
Searches manga catalogs by title, author, or keyword.
|
||
|
||
```javascript
|
||
const searchResults = await sdk.searchManga("Steel Ball Run", 1, 10);
|
||
```
|
||
|
||
#### Parameters
|
||
|
||
| Parameter | Type | Required | Default | Description |
|
||
| --- | --- | --- | --- | --- |
|
||
| `query` | `string` | **Yes** | - | Search query text. |
|
||
| `page` | `number` | **No** | `1` | Page number. |
|
||
| `perPage` | `number` | **No** | `12` | Results per page limit. |
|
||
|
||
#### Example Response Format
|
||
|
||
```json
|
||
{
|
||
"page": 1,
|
||
"total": 2,
|
||
"results": [
|
||
{
|
||
"id": "aKXN8",
|
||
"title": "JoJo's Bizarre Adventure: Part 7–Steel Ball Run",
|
||
"englishTitle": null,
|
||
"authors": ["ARAKI Hirohiko"],
|
||
"type": "Manga",
|
||
"medium": "Comic",
|
||
"status": "Completed",
|
||
"year": 2004,
|
||
"rating": 9.03681428571429,
|
||
"popularity": "1.6M",
|
||
"poster": "https://cdn.atsu.moe/static/posters/nR3hGYk9GmOqgD52.jpg",
|
||
"posterSmall": "https://cdn.atsu.moe/static/posters/nR3hGYk9GmOqgD52-small.avif",
|
||
"posterMedium": "https://cdn.atsu.moe/static/posters/nR3hGYk9GmOqgD52-medium.avif"
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### `getManga(id)`
|
||
|
||
Retrieves manga entry details, author info, synopsis, and full chapter list.
|
||
|
||
```javascript
|
||
const manga = await sdk.getManga("aKXN8");
|
||
```
|
||
|
||
#### Parameters
|
||
|
||
| Parameter | Type | Required | Default | Description |
|
||
| --- | --- | --- | --- | --- |
|
||
| `id` | `string` | **Yes** | - | Unique manga ID. |
|
||
|
||
#### Example Response Format
|
||
|
||
```json
|
||
{
|
||
"id": "aKXN8",
|
||
"title": "JoJo's Bizarre Adventure: Part 7–Steel Ball Run",
|
||
"englishTitle": null,
|
||
"synopsis": "Riders from around the world gather in the Wild West for the race of the century...",
|
||
"status": "Completed",
|
||
"type": "Manga",
|
||
"medium": "Comic",
|
||
"rating": 9.03681428571429,
|
||
"views": "330.5K",
|
||
"authors": ["Hirohiko Araki"],
|
||
"genres": ["Action", "Adventure", "Drama"],
|
||
"tags": ["19th Century", "Seinen"],
|
||
"poster": "https://cdn.atsu.moe/static/posters/nR3hGYk9GmOqgD52.jpg",
|
||
"totalChapters": 95,
|
||
"chapters": [
|
||
{
|
||
"id": "8KyKadCx",
|
||
"number": 1,
|
||
"title": "# 1",
|
||
"pageCount": 48,
|
||
"createdAt": "2026-04-18T00:00:00.000Z"
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### `getMangaChapter(mangaId, chapterId)`
|
||
|
||
Resolves all page image assets for a given manga chapter.
|
||
|
||
```javascript
|
||
const chapter = await sdk.getMangaChapter("aKXN8", "8KyKadCx");
|
||
```
|
||
|
||
#### Parameters
|
||
|
||
| Parameter | Type | Required | Default | Description |
|
||
| --- | --- | --- | --- | --- |
|
||
| `mangaId` | `string` | **Yes** | - | Unique manga ID. |
|
||
| `chapterId` | `string` | **Yes** | - | Unique chapter ID. |
|
||
|
||
#### Example Response Format
|
||
|
||
```json
|
||
{
|
||
"id": "8KyKadCx",
|
||
"number": 1,
|
||
"title": "# 1",
|
||
"scanlatorId": "cmo3e2t970003ktp80ip0n5hb",
|
||
"pages": [
|
||
{
|
||
"number": 0,
|
||
"url": "https://cdn.atsu.moe/static/pages/aKXN8/8KyKadCx/0.webp",
|
||
"width": 1200,
|
||
"height": 1722,
|
||
"aspectRatio": 0.6968641114982579
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
## 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.get("/api/manga/search", async (req, res) => {
|
||
const { q, page, perPage } = req.query;
|
||
try {
|
||
const results = await sdk.searchManga(q, page || 1, perPage || 12);
|
||
return res.json(results);
|
||
} catch (error) {
|
||
return res.status(500).json({ error: error.message });
|
||
}
|
||
});
|
||
|
||
app.get("/api/manga/:mangaId/chapter/:chapterId", async (req, res) => {
|
||
const { mangaId, chapterId } = req.params;
|
||
try {
|
||
const pages = await sdk.getMangaChapter(mangaId, chapterId);
|
||
return res.json(pages);
|
||
} catch (error) {
|
||
return res.status(500).json({ error: error.message });
|
||
}
|
||
});
|
||
|
||
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.
|