> ## Documentation Index
> Fetch the complete documentation index at: https://docs.vyla.cc/llms.txt
> Use this file to discover all available pages before exploring further.

# Movie Sources

> Stream verified HLS sources, subtitle tracks, and TMDB metadata for a movie via Server-Sent Events.

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 `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`.

<Warning>
  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.
</Warning>

***

## Query Parameters

<ParamField query="id" type="string" required>
  TMDB movie ID. Find it on [themoviedb.org](https://www.themoviedb.org) — it's the number in the URL.

  **Example:** `themoviedb.org/movie/550` → `id=550`
</ParamField>

<ParamField query="sources" type="string">
  Comma-separated list of provider keys to query. When omitted, all active providers are queried.

  **Example:** `sources=vidlink,vixsrc`

  Use `GET /api?sources_meta=1` to retrieve the full list of available provider keys.
</ParamField>

***

## Request

<CodeGroup>
  ```bash cURL (API key) theme={null}
  curl -N "https://1c34-y.hf.space/movie?id=550" \
    --header "Authorization: Bearer YOUR_API_KEY"
  ```

  ```bash cURL (session token) theme={null}
  TOKEN=$(curl -s -X POST https://1c34-y.hf.space/api/auth | jq -r .token)

  curl -N "https://1c34-y.hf.space/movie?id=550" \
    -H "X-Session-Token: $TOKEN"
  ```

  ```javascript JavaScript (session token) theme={null}
  const BASE = 'https://1c34-y.hf.space';

  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.`);
    }
  }
  ```

  ```typescript TypeScript (session token) theme={null}
  const BASE = 'https://1c34-y.hf.space';

  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);
      }
    }
  }
  ```

  ```python Python (server-side key) theme={null}
  import requests, json

  BASE = 'https://1c34-y.hf.space'

  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
  ```
</CodeGroup>

***

## SSE Event Reference

### `meta`

Emitted first, before any provider resolves.

<ResponseField name="type" type="string" required>
  Always `"meta"`.
</ResponseField>

<ResponseField name="meta" type="object | null">
  Raw TMDB movie metadata. `null` if no `TMDB_API_KEY` is configured on the server.
</ResponseField>

<ResponseField name="subtitles" type="Subtitle[]" required>
  Available subtitle tracks. Empty array `[]` if none are found.

  <Expandable title="Subtitle object">
    <ResponseField name="label" type="string">
      Language name, e.g. `English`, `Spanish`, `Arabic`.
    </ResponseField>

    <ResponseField name="file" type="string">
      Direct URL to a `.vtt` or `.srt` subtitle file. Use as `<track src="...">` or pass to your player's subtitle API.
    </ResponseField>

    <ResponseField name="type" type="string">
      Subtitle format — `vtt` or `srt`.
    </ResponseField>

    <ResponseField name="source" type="string">
      Internal subtitle source identifier.
    </ResponseField>
  </Expandable>
</ResponseField>

***

### `source`

Emitted once per verified, working provider.

<ResponseField name="type" type="string" required>
  Always `"source"`.
</ResponseField>

<ResponseField name="source" type="Source" required>
  <Expandable title="Source object">
    <ResponseField name="source" type="string">
      Internal provider key. Matches the keys in `/api/health`.
    </ResponseField>

    <ResponseField name="label" type="string">
      Human-readable provider name.
    </ResponseField>

    <ResponseField name="url" type="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.
    </ResponseField>
  </Expandable>
</ResponseField>

***

### `done`

Emitted when all providers have resolved or timed out.

<ResponseField name="type" type="string" required>
  Always `"done"`.
</ResponseField>

<ResponseField name="total" type="number" required>
  The total number of working `source` events that were emitted during this stream.
</ResponseField>

***

## 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://1c34-y.hf.space/api?url=...&pa=1"}}

data: {"type":"source","source":{"source":"provider-b","label":"Provider B","url":"https://1c34-y.hf.space/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             |

<Note>
  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.
</Note>

<ResponseExample>
  ```text 200 theme={null}
  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://1c34-y.hf.space/api?url=https%3A%2F%2F...&vl=1"}}

  data: {"type":"source","source":{"source":"meowtv","label":"MeowTV","url":"https://1c34-y.hf.space/api?url=https%3A%2F%2F...&mt=1"}}

  data: {"type":"done","total":2}
  ```

  ```json 400 theme={null}
  { "error": "missing id", "route": "/movie?id=:tmdb_id", "example": "/movie?id=155" }
  ```

  ```json 403 theme={null}
  { "error": "Public keys cannot access streaming endpoints" }
  ```
</ResponseExample>

***

## Notes

<AccordionGroup>
  <Accordion title="Filtering providers with sources=">
    Pass a comma-separated list of provider keys to query only specific providers:

    ```
    /movie?id=550&sources=vidlink,vixsrc
    ```

    Keys that don't match any active provider are silently ignored. If none of the requested keys match, the response will emit `done` with `total: 0`. Omit the parameter entirely to query all active providers.
  </Accordion>

  <Accordion title="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.
  </Accordion>

  <Accordion title="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.
  </Accordion>

  <Accordion title="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.
  </Accordion>

  <Accordion title="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.
  </Accordion>
</AccordionGroup>
