Skip to main content

Before You Start

You’ll need a TMDB ID for the content you want to stream. Every movie and TV show on themoviedb.org has one — it’s the number in the URL.
URLTMDB IDTitle
themoviedb.org/movie/550550Fight Club
themoviedb.org/movie/2720527205Inception
themoviedb.org/tv/13961396Breaking Bad
themoviedb.org/tv/9499794997House of the Dragon

Step 1 — Open the SSE Stream

The /movie and /tv endpoints are Server-Sent Events — not JSON. Open a streaming connection and handle three event types as they arrive: meta, source, and done.
curl -N "https://missourimonster-vyla.hf.space/movie?id=550"
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"},{"label":"Spanish","file":"https://sub.vdrk.site/v1/vtt/movie/550/Spanish.vtt","type":"vtt","source":"v1"}]}

data: {"type":"source","source":{"source":"provider-a","label":"Provider A","url":"https://missourimonster-vyla.hf.space/api?url=...&pa=1"}}

data: {"type":"source","source":{"source":"provider-b","label":"Provider B","url":"https://missourimonster-vyla.hf.space/api?url=...&pb=1"}}

data: {"type":"done","total":2}

Step 2 — Handle Events and Play

Parse each data: line as JSON and act on the type field. Start playback on the first source event — don’t wait for done.
<!DOCTYPE html>
<html>
<head>
  <script src="https://cdn.jsdelivr.net/npm/hls.js@latest/dist/hls.min.js"></script>
</head>
<body>
  <video id="player" controls style="width:100%"></video>
  <div id="status">Connecting…</div>

  <script>
    const BASE   = 'https://missourimonster-vyla.hf.space';
    const video  = document.getElementById('player');
    const status = document.getElementById('status');

    let hls;
    let sources = [];
    let started = false;

    function playSource(url) {
      hls?.destroy();
      if (!Hls.isSupported()) { video.src = url; return; }
      hls = new Hls();
      hls.loadSource(url);
      hls.attachMedia(video);
      hls.on(Hls.Events.MANIFEST_PARSED, () => {
        status.textContent = 'Playing';
        video.play();
      });
      hls.on(Hls.Events.ERROR, (_, err) => {
        if (err.fatal) {
          const next = sources.find(s => s.url !== url);
          if (next) { status.textContent = `Trying ${next.label}…`; playSource(next.url); }
          else status.textContent = 'All sources failed.';
        }
      });
    }

    async function loadMovie(tmdbId) {
      const res     = await fetch(`${BASE}/movie?id=${tmdbId}`);
      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') {
            status.textContent = `Loading ${event.meta?.title ?? ''}…`;
            event.subtitles?.forEach((sub, i) => {
              const track    = document.createElement('track');
              track.kind     = 'subtitles';
              track.label    = sub.label;
              track.src      = sub.file;
              track.default  = i === 0;
              video.appendChild(track);
            });
          }

          if (event.type === 'source') {
            sources.push(event.source);
            if (!started) { started = true; playSource(event.source.url); }
          }

          if (event.type === 'done' && !started) {
            status.textContent = 'No sources available.';
          }
        }
      }
    }

    loadMovie(550);
  </script>
</body>
</html>

Step 3 — Build a Fallback Queue

Sources stream in order of speed. Load the first one immediately and queue the rest — if one dies mid-stream, try the next.
const BASE = 'https://missourimonster-vyla.hf.space';

async function streamWithFallback(
  videoEl: HTMLVideoElement,
  tmdbId: number,
  season?: number,
  episode?: number
) {
  const endpoint = season && episode
    ? `${BASE}/tv?id=${tmdbId}&season=${season}&episode=${episode}`
    : `${BASE}/movie?id=${tmdbId}`;

  const queue: string[] = [];
  let started = false;
  let hls: any;

  function tryNext() {
    if (!queue.length) return;
    hls?.destroy();
    hls = new Hls();

    const url = queue.shift()!;
    hls.loadSource(url);
    hls.attachMedia(videoEl);
    hls.on(Hls.Events.MANIFEST_PARSED, () => videoEl.play());
    hls.on(Hls.Events.ERROR, (_: any, err: any) => {
      if (err.fatal) tryNext();
    });
  }

  const res     = await fetch(endpoint);
  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 === 'source') {
        queue.push(event.source.url);
        if (!started) { started = true; tryNext(); }
      }

      if (event.type === 'done' && !started) {
        throw new Error('No sources available');
      }
    }
  }
}

Step 4 — Add Subtitles

Subtitles arrive in the meta event, before any source. Attach them to your video element immediately.
// In your meta event handler:
if (event.type === 'meta') {
  event.subtitles.forEach((sub, i) => {
    const track   = document.createElement('track');
    track.kind    = 'subtitles';
    track.label   = sub.label;
    track.src     = sub.file;
    track.default = i === 0;
    videoEl.appendChild(track);
  });
}

// Or fetch subtitles standalone (no streaming needed):
const subtitles = await fetch(
  'https://missourimonster-vyla.hf.space/subtitles/movie/550'
).then(r => r.json());

Check Provider Health

Before building, verify which providers are currently live:
curl https://missourimonster-vyla.hf.space/health
{
  "status": "ok",
  "timestamp": "2025-01-01T00:00:00.000Z",
  "tmdb": true,
  "cache": 0,
  "probe_id": "155",
  "sources": {
    "provider-a": { "ok": true,  "ms": 1204 },
    "provider-b": { "ok": true,  "ms": 980  },
    "provider-c": { "ok": false, "ms": 435  }
  }
}
status: "degraded" means at least one provider is down — the rest still work and your fallback queue handles it automatically.

Debug a Single Provider

Use /test to isolate a specific provider without running the full fanout:
curl "https://missourimonster-vyla.hf.space/test/550?source=<provider-key>"
Available provider keys are listed in the /health response under the sources field.

Full API Reference →

See complete SSE event docs for every endpoint.