# cloudflare-video

Video component that plays Cloudflare Stream videos through the Stream player

[Cloudflare Stream](https://developers.cloudflare.com/stream/) runs in an embedded player. Its own chrome stays hidden by default, so it drops into a [player skin](https://videojs.org/docs/framework/html/guides/skins) like any other media component; set `controls` to use Cloudflare’s UI instead.

## Import

```bash
pnpm add @videojs/cloudflare-video
```

```ts
import '@videojs/html/media/cloudflare-video';
```

Or load it from the [CDN](https://videojs.org/docs/framework/html/guides/cdn):

```html
<script type="module" src="https://cdn.jsdelivr.net/npm/@videojs/cdn@10.0.0-rc.4/media/cloudflare-video.js"></script>
```

## Load a source

`src` takes a raw 32-character video UID, any `cloudflarestream.com` or `videodelivery.net` URL, or a signed token. Watch, embed, iframe, manifest, and thumbnail URLs all carry the UID in the same position, and a signed token stands in for the UID wherever one appears.

```html
<cloudflare-video src="https://watch.videodelivery.net/bfbd585059e33391d67b0f1d15fe6ea4"></cloudflare-video>
```

## Signed playback

For [signed URLs](https://developers.cloudflare.com/stream/viewing-videos/securing-your-stream/), pass the signed token wherever the UID would go: as the whole `src`, or inside a Stream URL. Keep your customer subdomain when your URLs name one. Cloudflare serves signed and access-controlled videos only from `customer-<code>.cloudflarestream.com`, so the element preserves that origin instead of collapsing it onto the shared host.

## Behavior

A few things work differently from a native `<video>` element, because the Stream embed doesn’t expose them:

- The embed owns text tracks: `textTracks` stays empty, and `engine.cloudflare.defaultTextTrack` picks the track to show.
- Picture-in-picture is unavailable, and the player reports it as unsupported.
- Fullscreen targets the iframe, so Cloudflare’s own chrome shows in fullscreen.
- `playsInline` has no effect: the Stream embed always plays inline.

## Examples

### Basic Usage

**index.html**

```html
<cloudflare-video class="cloudflare-video" src="https://watch.videodelivery.net/bfbd585059e33391d67b0f1d15fe6ea4" controls></cloudflare-video>
```

**index.css**

```css
.cloudflare-video {
  width: 100%;
  aspect-ratio: 16 / 9;
}
```

**index.ts**

```ts
import '@videojs/html/media/cloudflare-video';
```

## API Reference

### Attributes

These attributes configure the embedded media adapter:

| Attribute | Type | Default |
| --- | --- | --- |
| `autoplay` | `boolean` | `false` |
| `controls` | `boolean` | `false` |
| `loop` | `boolean` | `false` |
| `muted` | `boolean` | `false` |
| `playsinline` | `boolean` | `true` |
| `poster` | `string` | `''` |
| `preload` | `MediaPreloadType` | `'metadata'` |
| `src` | `string` | `''` |

### Properties

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `autoplay` | `boolean` | `false` | |
| `buffered` | `TimeRangeLike` | — | Read-only. |
| `controls` | `boolean` | `false` | |
| `currentSrc` | `string` | — | Read-only. |
| `currentTime` | `number` | — | |
| `defaultMuted` | `boolean` | `false` | |
| `duration` | `number` | — | Read-only. |
| `ended` | `boolean` | — | Read-only. |
| `engine` | `{ play(): Promise<void> \| void; pause(): void; addEventListener(type: string, listener: ((event: Event) => void)): void; removeEventListener(type: string, listener: ((event: Event) => void)): void; src: string; currentTime: number; volume: number; muted: boolean; playbackRate: number; loop: boolean; autoplay: boolean; controls: boolean; preload: string; poster: string; paused: boolean; ended: boolean; seeking: boolean; duration: number; buffered: TimeRanges; played: TimeRanges; videoWidth: number; videoHeight: number } \| null` | — | Read-only. Underlying Stream SDK player instance (null until the SDK loads). |
| `error` | `MediaError \| null` | — | Read-only. |
| `isFullscreen` | `boolean` | — | Read-only. |
| `loop` | `boolean` | `false` | |
| `muted` | `boolean` | `false` | |
| `paused` | `boolean` | — | Read-only. |
| `playbackRate` | `number` | — | |
| `played` | `{ length: number; start(index: number): number; end(index: number): number }` | — | Read-only. |
| `playsInline` | `boolean` | `true` | |
| `poster` | `string` | `''` | |
| `preload` | `MediaPreloadType` | `'metadata'` | |
| `readyState` | `number` | — | Read-only. |
| `seekable` | `TimeRangeLike` | — | Read-only. |
| `seeking` | `boolean` | — | Read-only. |
| `source` | `{ src?: string; engine?: CloudflareSourceEngineConfig } \| null` | `null` | Structured source: `src` plus embed parameters under `engine.cloudflare`. Replacing it re-derives `src`. |
| `src` | `string` | `''` | |
| `textTracks` | `TextTrackListLike` | — | Read-only. Always empty: the SDK exposes no track API, and the embed owns the track picked by `defaultTextTrack`. |
| `videoHeight` | `number` | — | Read-only. |
| `videoWidth` | `number` | — | Read-only. |
| `volume` | `number` | — | |

### Engine options

#### `source.engine.cloudflare`

Pass [Stream player parameters](https://developers.cloudflare.com/stream/viewing-videos/using-the-stream-player/) under `source.engine.cloudflare`, spelled exactly as Cloudflare spells them, plus anything Cloudflare adds next. They’re serialized onto the embed URL untouched, and the embed reads them once, when its URL is built. [Media Sources](https://videojs.org/docs/framework/html/guides/media-sources) covers how engine options fit into a structured source.

`controls`, `autoplay`, `loop`, `muted`, `preload`, and `poster` come from the props of the same name, so they have no `engine.cloudflare` spelling.

```ts
const video = document.querySelector('cloudflare-video');
video.source = {
  src: 'bfbd585059e33391d67b0f1d15fe6ea4',
  engine: { cloudflare: { primaryColor: '#f03e3e', startTime: '5m30s' } },
};
```

| Option | Type | Description |
| --- | --- | --- |
| `ad-url` | `string \| undefined` | VAST tag to play as a pre-roll ad. Reported through the `stream-ad*` events. |
| `defaultTextTrack` | `string \| undefined` | BCP 47 language of the text track to show by default (`'en'`, `'de'`). |
| `letterboxColor` | `string \| undefined` | Any CSS color for the letterbox bars around a video that does not fill the player. |
| `primaryColor` | `string \| undefined` | Any CSS color for the progress bar and other player accents. |
| `referrerPolicy` | `ReferrerPolicy \| undefined` | `referrerpolicy` for the embed iframe. Not a Cloudflare player parameter. |
| `startTime` | `number \| string \| undefined` | Where playback starts, in seconds or as a time string (`'5m30s'`). |

### Methods

Supports these media methods: `exitFullscreen`, `load`, `pause`, `play`, `requestFullscreen`.

### Events

Implements these standard media events through the embedded player: `durationchange`, `emptied`, `error`, `loadedmetadata`, `loadstart`, `volumechange`.

Also emits these Video.js-specific events:

| Event | Description |
| --- | --- |
| `loadcomplete` | |
| `sourcechange` | Fired when `source` changes, either directly or by resolving a new `src`. Read `source` for the new value. |

---

HTML documentation: https://videojs.org/docs/framework/html/llms.txt
All documentation: https://videojs.org/llms.txt
