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

# API Reference

> Complete reference for createPlayer, PlayerOpts, BlindcastPlayer, events, metrics, and error codes.

## `createPlayer(videoEl, opts)`

Creates a BlindCast encrypted HLS player.

**Parameters:**

| Parameter | Type               | Description                      |
| --------- | ------------------ | -------------------------------- |
| `videoEl` | `HTMLVideoElement` | Video element already in the DOM |
| `opts`    | `PlayerOpts`       | Player configuration             |

**Returns:** `Result<BlindcastPlayer, PlayerError>`

## `isPlayerSupported()`

Returns `true` if the browser supports MSE and the Web Crypto API.

**Returns:** `boolean`

## `PlayerOpts`

```typescript theme={null}
interface PlayerOpts {
  /** Key server URL including the /keys path (e.g., "https://your-worker.workers.dev/keys") */
  keyServerUrl: string

  /** Returns a Bearer token for key server auth (called per key request) */
  keyServerAuth?: () => Promise<string>

  /** Passthrough config to hls.js (loader and fLoader are reserved) */
  hlsConfig?: Record<string, unknown>

  /** Custom key URI parser. Return null to use default parsing. */
  parseKeyUri?: (uri: string) => { contentId: string; epoch?: number } | null

  /** Lease configuration for opt-in key revocation */
  lease?: {
    leaseEndpoint: string
    requestedTtlMs?: number      // default: 300_000 (5 min)
    renewalFraction?: number     // default: 0.75
    minRenewalBufferMs?: number  // default: 30_000 ms
  }
}
```

## `BlindcastPlayer`

```typescript theme={null}
interface BlindcastPlayer {
  /** Load and play an HLS manifest */
  load(manifestUrl: string): void

  /** Destroy the player and release all resources */
  destroy(): void

  /** Subscribe to a player event. Returns an unsubscribe function. */
  on<E extends PlayerEvent>(event: E, handler: PlayerEventMap[E]): () => void

  /** Get a snapshot of current playback metrics */
  getMetrics(): PlaybackMetrics

  /** The underlying video element */
  readonly videoElement: HTMLVideoElement
}
```

## Events

| Event           | Payload                                  | When                                      |
| --------------- | ---------------------------------------- | ----------------------------------------- |
| `ready`         | `{ levels: number }`                     | Manifest parsed, quality levels available |
| `playing`       | *(none)*                                 | Playback started or resumed               |
| `paused`        | *(none)*                                 | Playback paused                           |
| `buffering`     | *(none)*                                 | Waiting for data                          |
| `ended`         | *(none)*                                 | Playback reached the end                  |
| `keyLoaded`     | `{ contentId: string, epoch?: number }`  | Content key fetched and imported          |
| `qualitySwitch` | `{ fromLevel: number, toLevel: number }` | ABR quality level changed                 |
| `error`         | `PlayerError`                            | Any error occurred                        |
| `destroyed`     | *(none)*                                 | Player destroyed and resources released   |

## `PlaybackMetrics`

```typescript theme={null}
interface PlaybackMetrics {
  timeToFirstFrame: number   // ms from load() to first frame
  avgDecryptTime: number     // ms per segment decryption
  avgFragLoadTime: number    // ms per fragment download
  avgKeyFetchTime: number    // ms per key server request
  keyFetchCount: number      // total key requests
  qualitySwitches: number    // ABR level changes
  fragmentsLoaded: number    // total segments loaded
  stallCount: number         // buffer stall events
}
```

## Error codes

| Code                    | Cause                                                 |
| ----------------------- | ----------------------------------------------------- |
| `UNSUPPORTED_BROWSER`   | MSE or Web Crypto API not available                   |
| `INVALID_VIDEO_ELEMENT` | `videoEl` is null or not an `HTMLVideoElement`        |
| `INVALID_OPTIONS`       | Required options missing or malformed                 |
| `MANIFEST_LOAD_FAILED`  | Could not fetch or parse the HLS manifest             |
| `KEY_FETCH_FAILED`      | Key server request failed (network error or non-200)  |
| `KEY_AUTH_FAILED`       | Key server returned 401 or 403                        |
| `KEY_LEASE_EXPIRED`     | Lease was revoked or expired and cannot be renewed    |
| `DECRYPT_FAILED`        | Segment decryption failed (corrupt data or wrong key) |
| `MEDIA_ERROR`           | hls.js media error (codec issue, corrupt segment)     |
| `NETWORK_ERROR`         | Segment download failed (CDN error, offline)          |
| `PLAYER_DESTROYED`      | Operation called after `destroy()`                    |

See [Error Codes](/reference/error-codes) for all error codes across BlindCast.
