Movie Sources
curl --request GET \
--url http://localhost:7860/movie \
--header 'Authorization: Bearer <token>'import requests
url = "http://localhost:7860/movie"
headers = {"Authorization": "Bearer <token>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
fetch('http://localhost:7860/movie', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_PORT => "7860",
CURLOPT_URL => "http://localhost:7860/movie",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "http://localhost:7860/movie"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("http://localhost:7860/movie")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("http://localhost:7860/movie")
http = Net::HTTP.new(url.host, url.port)
request = Net::HTTP::Get.new(url)
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_bodydata: {"type":"meta","meta":{"id":550,"title":"Fight Club","release_date":"1999-10-15","runtime":139},"subtitles":[{"label":"English","file":"https://sub.vdrk.site/v1/movie/550/English.vtt","type":"vtt","source":"v1"}]}
data: {"type":"source","source":{"source":"vidlink","label":"VidLink","url":"https://api.vyla.cc/api?url=https%3A%2F%2F...&vl=1"}}
data: {"type":"source","source":{"source":"meowtv","label":"MeowTV","url":"https://api.vyla.cc/api?url=https%3A%2F%2F...&mt=1"}}
data: {"type":"done","total":2}
{ "error": "missing id", "route": "/movie?id=:tmdb_id", "example": "/movie?id=155" }
{ "error": "Public keys cannot access streaming endpoints" }
Stream Sources
Movie Sources
Stream verified HLS sources, subtitle tracks, and TMDB metadata for a movie via Server-Sent Events.
GET
/
movie
Movie Sources
curl --request GET \
--url http://localhost:7860/movie \
--header 'Authorization: Bearer <token>'import requests
url = "http://localhost:7860/movie"
headers = {"Authorization": "Bearer <token>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
fetch('http://localhost:7860/movie', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_PORT => "7860",
CURLOPT_URL => "http://localhost:7860/movie",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "http://localhost:7860/movie"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("http://localhost:7860/movie")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("http://localhost:7860/movie")
http = Net::HTTP.new(url.host, url.port)
request = Net::HTTP::Get.new(url)
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_bodydata: {"type":"meta","meta":{"id":550,"title":"Fight Club","release_date":"1999-10-15","runtime":139},"subtitles":[{"label":"English","file":"https://sub.vdrk.site/v1/movie/550/English.vtt","type":"vtt","source":"v1"}]}
data: {"type":"source","source":{"source":"vidlink","label":"VidLink","url":"https://api.vyla.cc/api?url=https%3A%2F%2F...&vl=1"}}
data: {"type":"source","source":{"source":"meowtv","label":"MeowTV","url":"https://api.vyla.cc/api?url=https%3A%2F%2F...&mt=1"}}
data: {"type":"done","total":2}
{ "error": "missing id", "route": "/movie?id=:tmdb_id", "example": "/movie?id=155" }
{ "error": "Public keys cannot access streaming endpoints" }
Opens a Server-Sent Events connection and queries all configured providers in parallel. Results stream in as each provider resolves — you don’t wait for all providers to finish before playback can begin.
The
Emitted first, before any provider resolves.
Emitted once per verified, working provider.
Emitted when all providers have resolved or timed out.
meta event fires first with TMDB metadata and subtitles. Each working source arrives as its own source event. A final done event closes the stream.
Every url in a source event is fully qualified and already routed through the proxy. HLS sources have M3U8 segment paths rewritten — pass to hls.loadSource(). MP4 sources can be set directly as video.src.
This endpoint requires a
standard or partner API key, or a session token from POST /api/auth. The public key will receive a 403 response.Query Parameters
string
required
TMDB movie ID. Find it on themoviedb.org — it’s the number in the URL.Example:
themoviedb.org/movie/550 → id=550string
Comma-separated list of provider keys to query. When omitted, all active providers are queried.Example:
sources=vidlink,vixsrcUse GET /api?sources_meta=1 to retrieve the full list of available provider keys.Request
curl -N "http://localhost:7860/movie?id=550" \
--header "Authorization: Bearer YOUR_API_KEY"
TOKEN=$(curl -s -X POST http://localhost:7860/api/auth | jq -r .token)
curl -N "http://localhost:7860/movie?id=550" \
-H "X-Session-Token: $TOKEN"
const BASE = 'http://localhost:7860';
const { token } = await fetch(`${BASE}/api/auth`, { method: 'POST' }).then(r => r.json());
const res = await fetch(`${BASE}/movie?id=550`, {
headers: { 'X-Session-Token': token }
});
const reader = res.body.getReader();
const decoder = new TextDecoder();
let buffer = '';
while (true) {
const { done, value } = await reader.read();
if (done) break;
buffer += decoder.decode(value, { stream: true });
const lines = buffer.split('\n');
buffer = lines.pop();
for (const line of lines) {
if (!line.startsWith('data: ')) continue;
const event = JSON.parse(line.slice(6));
if (event.type === 'meta') handleMeta(event);
if (event.type === 'source') handleSource(event.source);
if (event.type === 'done') console.log(`Done. ${event.total} sources.`);
}
}
const BASE = 'http://localhost:7860';
interface Source {
source: string;
label: string;
url: string;
}
interface Subtitle {
label: string;
file: string;
type: string;
source: string;
}
interface MetaEvent { type: 'meta'; meta: Record<string, unknown> | null; subtitles: Subtitle[]; }
interface SourceEvent { type: 'source'; source: Source; }
interface DoneEvent { type: 'done'; total: number; }
type SSEEvent = MetaEvent | SourceEvent | DoneEvent;
async function streamMovie(tmdbId: number, onSource: (s: Source) => void) {
const { token } = await fetch(`${BASE}/api/auth`, { method: 'POST' }).then(r => r.json());
const res = await fetch(`${BASE}/movie?id=${tmdbId}`, {
headers: { 'X-Session-Token': token }
});
const reader = res.body!.getReader();
const decoder = new TextDecoder();
let buffer = '';
while (true) {
const { done, value } = await reader.read();
if (done) break;
buffer += decoder.decode(value, { stream: true });
const lines = buffer.split('\n');
buffer = lines.pop()!;
for (const line of lines) {
if (!line.startsWith('data: ')) continue;
const event: SSEEvent = JSON.parse(line.slice(6));
if (event.type === 'source') onSource(event.source);
}
}
}
import requests, json
BASE = 'http://localhost:7860'
with requests.get(
f'{BASE}/movie',
params={'id': 550},
headers={'Authorization': 'Bearer your_standard_api_key'},
stream=True
) as res:
for line in res.iter_lines():
if not line or not line.startswith(b'data: '):
continue
event = json.loads(line[6:])
if event['type'] == 'meta':
print('Title:', event['meta'].get('title'))
elif event['type'] == 'source':
s = event['source']
print(f"{s['label']}: {s['url']}")
elif event['type'] == 'done':
print(f"Done. {event['total']} sources.")
break
SSE Event Reference
meta
Emitted first, before any provider resolves.
string
required
Always
"meta".object | null
Raw TMDB movie metadata.
null if no TMDB_API_KEY is configured on the server.Subtitle[]
required
Available subtitle tracks. Empty array
[] if none are found.source
Emitted once per verified, working provider.
string
required
Always
"source".Source
required
Show Source object
Show Source object
string
Internal provider key. Matches the keys in
/api/health.string
Human-readable provider name.
string
Fully-qualified, proxied stream URL. For HLS sources, pass to
hls.loadSource() — all M3U8 segment and encryption key URIs are rewritten to route through the proxy. For MP4 sources, set as video.src directly. No base URL prepending needed.done
Emitted when all providers have resolved or timed out.
string
required
Always
"done".number
required
The total number of working
source events that were emitted during this stream.Example SSE Stream
data: {"type":"meta","meta":{"id":550,"title":"Fight Club","release_date":"1999-10-15","runtime":139},"subtitles":[{"label":"English","file":"https://sub.vdrk.site/v1/vtt/movie/550/English.vtt","type":"vtt","source":"v1"}]}
data: {"type":"source","source":{"source":"provider-a","label":"Provider A","url":"https://api.vyla.cc/api?url=...&pa=1"}}
data: {"type":"source","source":{"source":"provider-b","label":"Provider B","url":"https://api.vyla.cc/api?url=...&pb=1"}}
data: {"type":"done","total":2}
Status Codes
| Status | Meaning |
|---|---|
200 | SSE stream opened successfully — events follow |
400 | Missing id parameter |
401 | Missing or invalid authentication |
403 | Key tier does not have streaming access (public key) |
500 | Server error before the stream could begin |
There is no
502 at the HTTP level for movies. If all providers fail, the stream will emit zero source events and then a done event with total: 0. Always check that you received at least one source event before attempting playback.data: {"type":"meta","meta":{"id":550,"title":"Fight Club","release_date":"1999-10-15","runtime":139},"subtitles":[{"label":"English","file":"https://sub.vdrk.site/v1/movie/550/English.vtt","type":"vtt","source":"v1"}]}
data: {"type":"source","source":{"source":"vidlink","label":"VidLink","url":"https://api.vyla.cc/api?url=https%3A%2F%2F...&vl=1"}}
data: {"type":"source","source":{"source":"meowtv","label":"MeowTV","url":"https://api.vyla.cc/api?url=https%3A%2F%2F...&mt=1"}}
data: {"type":"done","total":2}
{ "error": "missing id", "route": "/movie?id=:tmdb_id", "example": "/movie?id=155" }
{ "error": "Public keys cannot access streaming endpoints" }
Notes
Filtering providers with sources=
Filtering providers with sources=
Pass a comma-separated list of provider keys to query only specific providers:Keys that don’t match any active provider are silently ignored. If none of the requested keys match, the response will emit
/movie?id=550&sources=vidlink,vixsrc
done with total: 0. Omit the parameter entirely to query all active providers.Response time
Response time
The
meta event fires almost instantly. Individual source events arrive throughout the stream as providers resolve — typically within 3–8 seconds of opening the connection. The stream closes after the slowest configured provider times out.Source ordering
Source ordering
Sources arrive in the order providers resolve, which generally correlates with speed. Use the first
source event to begin playback and queue the rest as fallbacks.Zero sources
Zero sources
If all providers fail, you’ll receive a
done event with total: 0 and no source events. Check /api/health to see which providers are up.Using EventSource vs fetch streaming
Using EventSource vs fetch streaming
EventSource does not support custom headers, so you cannot send X-Session-Token through it. Use the fetch + ReadableStream approach shown in the examples above when authenticating with a session token.
