TV Subtitles
curl --request GET \
--url https://missourimonster-vyla.hf.space/api/subtitles/tv/{tmdb_id}/{season}/{episode}{
"[]": [
{
"label": "<string>",
"file": "<string>",
"type": "<string>",
"source": "<string>"
}
]
}Subtitles
TV Subtitles
Fetch all available subtitle tracks for a TV episode.
GET
/
api
/
subtitles
/
tv
/
{tmdb_id}
/
{season}
/
{episode}
TV Subtitles
curl --request GET \
--url https://missourimonster-vyla.hf.space/api/subtitles/tv/{tmdb_id}/{season}/{episode}{
"[]": [
{
"label": "<string>",
"file": "<string>",
"type": "<string>",
"source": "<string>"
}
]
}Returns an array of subtitle tracks for a specific TV episode. Each track is a direct
.vtt or .srt link — no proxy needed. Subtitle tracks are also bundled automatically in /api/tv responses; use this endpoint when you need them independently.
Path Parameters
TMDB series ID — not episode ID.Example:
1396 for Breaking BadSeason number.
Episode number within the season.
Request
curl https://missourimonster-vyla.hf.space/api/subtitles/tv/1396/1/1
Response
Returns a JSON array.404 if no subtitles are found for this episode.
Responses
- 200 — Success
- 404 — Not Found
- 500 — Server Error
[
{
"label": "English",
"file": "https://sub.vdrk.site/v1/tv/1396/1/1/English.vtt",
"type": "vtt",
"source": "v1"
},
{
"label": "Spanish",
"file": "https://sub.vdrk.site/v1/tv/1396/1/1/Spanish.vtt",
"type": "vtt",
"source": "v1"
}
]
{ "error": "no subtitles found" }
{ "error": "<error message>" }
Building an Episode Selector with Subtitles
const BASE = 'https://missourimonster-vyla.hf.space';
async function loadEpisode(
seriesId: number,
season: number,
episode: number,
videoEl: HTMLVideoElement
) {
// Remove existing subtitle tracks
Array.from(videoEl.querySelectorAll('track')).forEach(t => t.remove());
// /tv is an SSE stream — must be consumed line by line
const res = await fetch(`${BASE}/tv?id=${seriesId}&season=${season}&episode=${episode}`);
const reader = res.body!.getReader();
const decoder = new TextDecoder();
let buffer = '';
let started = false;
let hls: any;
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') {
// Attach subtitle tracks as soon as meta arrives
(event.subtitles ?? []).forEach((sub: { label: string; file: string }, i: number) => {
const track = document.createElement('track');
track.kind = 'subtitles';
track.label = sub.label;
track.src = sub.file;
track.default = i === 0;
videoEl.appendChild(track);
});
}
if (event.type === 'source' && !started) {
started = true;
hls?.destroy();
// Import Hls dynamically if needed; assume it's already in scope here
hls = new Hls();
hls.loadSource(event.source.url);
hls.attachMedia(videoEl);
hls.on(Hls.Events.MANIFEST_PARSED, () => videoEl.play());
}
if (event.type === 'done' && !started) {
throw new Error('No sources available for this episode');
}
}
}
}
Subtitle availability varies by episode. Newer or less popular shows may return an empty array. Always handle
subtitles.length === 0 gracefully in your UI.⌘I

