docs(rfc): update player api to match implementation (#375)

This commit is contained in:
rahim
2026-02-02 22:03:58 +11:00
committed by GitHub
parent a419f5ef19
commit 43cf39cbf3
8 changed files with 935 additions and 592 deletions
+80 -43
View File
@@ -7,10 +7,11 @@ Progressive journey from simple to fully custom.
### React
```tsx
import { createPlayer, features } from '@videojs/react';
import { createPlayer } from '@videojs/react';
import { features } from '@videojs/core/dom';
const { Provider: VideoProvider, Container: VideoContainer, usePlayer } = createPlayer({
features: [features.video]
const { Provider: VideoProvider, Container: VideoContainer } = createPlayer({
features: [...features.video],
});
function App() {
@@ -28,7 +29,7 @@ function App() {
### HTML
```ts
import '@videojs/html/player/video'; // includes features.video
import '@videojs/html/player/video';
```
```html
@@ -45,11 +46,12 @@ import '@videojs/html/player/video'; // includes features.video
```tsx
import '@videojs/react/skin/video.css';
import { createPlayer, features } from '@videojs/react';
import { createPlayer } from '@videojs/react';
import { features } from '@videojs/core/dom';
import { VideoSkin } from '@videojs/react/skin/video';
const { Provider: VideoProvider } = createPlayer({
features: [features.video]
features: [...features.video],
});
function App() {
@@ -86,12 +88,14 @@ Skin detects chapters feature, shows chapter menu.
### React
```tsx
import { createPlayer, features } from '@videojs/react';
import '@videojs/react/skin/video.css';
import { createPlayer } from '@videojs/react';
import { features, chaptersSlice } from '@videojs/core/dom';
import { VideoSkin } from '@videojs/react/skin/video';
const { Provider: VideoProvider } = createPlayer({
features: [features.video, features.chapters]
features: [...features.video, chaptersSlice],
});
function App() {
@@ -129,13 +133,15 @@ Skin detects streaming features, shows quality/tracks menus.
### React
```tsx
import { createPlayer, features } from '@videojs/react';
import { HlsVideo } from '@videojs/react/media/hls';
import '@videojs/react/skin/video.css';
import { createPlayer } from '@videojs/react';
import { features } from '@videojs/core/dom';
import { HlsVideo } from '@videojs/react/media/hls';
import { VideoSkin } from '@videojs/react/skin/video';
const { Provider: VideoProvider } = createPlayer({
features: [features.video, features.streaming]
features: [...features.video, ...features.streaming],
});
function App() {
@@ -174,13 +180,15 @@ Same skin adapts to show ad UI.
### React
```tsx
import { createPlayer, features } from '@videojs/react';
import { HlsVideo } from '@videojs/react/media/hls';
import '@videojs/react/skin/video.css';
import { createPlayer } from '@videojs/react';
import { features } from '@videojs/core/dom';
import { HlsVideo } from '@videojs/react/media/hls';
import { VideoSkin } from '@videojs/react/skin/video';
const { Provider: VideoProvider } = createPlayer({
features: [features.video, features.streaming, features.ads]
features: [...features.video, ...features.streaming, ...features.ads],
});
function App() {
@@ -210,22 +218,21 @@ import '@videojs/html/skin/video';
### React
```tsx
import { createPlayer, features } from '@videojs/react';
import { createPlayer } from '@videojs/react';
import { defineSlice } from '@videojs/store';
import { features, selectPlayback, selectTime, type PlayerTarget } from '@videojs/core/dom';
import { HlsVideo } from '@videojs/react/media/hls';
const myAnalytics = createMediaFeature({
name: 'analytics',
initialState: {},
subscribe: ({ store, signal }) => {
// Custom analytics logic
},
request: {
trackEvent: (name) => console.log(name),
},
const analyticsSlice = defineSlice<PlayerTarget>()({
state: ({ task }) => ({
trackEvent: task('trackEvent', ({ get }, name: string) => {
console.log('track', name, get());
}),
}),
});
const { Provider: VideoProvider, Container: VideoContainer, usePlayer } = createPlayer({
features: [features.video, features.streaming, myAnalytics]
features: [...features.video, ...features.streaming, analyticsSlice],
});
function App() {
@@ -240,9 +247,8 @@ function App() {
}
function MyCustomSkin() {
const playback = usePlayer(features.playback);
const time = usePlayer(features.time);
const analytics = usePlayer(myAnalytics);
const playback = usePlayer(selectPlayback);
const time = usePlayer(selectTime);
if (!playback || !time) return null;
@@ -260,18 +266,19 @@ function MyCustomSkin() {
### HTML
```ts
import { createPlayer, features, MediaElement } from '@videojs/html';
import { createPlayer, MediaElement } from '@videojs/html';
import { features, selectPlayback } from '@videojs/core/dom';
const { PlayerElement, PlayerController } = createPlayer({
features: [features.video, features.streaming, myCustomFeature]
const { PlayerElement, PlayerController, context } = createPlayer({
features: [...features.video, ...features.streaming, myCustomSlice],
});
// Define player element
customElements.define('my-video-player', PlayerElement);
// Custom play button — host is the button
// Custom play button
class MyPlayButton extends MediaElement {
#playback = new PlayerController(this, features.playback);
#playback = new PlayerController(this, context, selectPlayback);
override connectedCallback() {
super.connectedCallback();
@@ -328,22 +335,23 @@ customElements.define('my-play-button', MyPlayButton);
### React
```tsx
import { createPlayer, features } from '@videojs/react';
import { createPlayer } from '@videojs/react';
import { features, selectPlayback, selectTime } from '@videojs/core/dom';
const { Provider: VideoProvider, usePlayer } = createPlayer({
features: [features.playback, features.time]
const { Provider: AudioProvider, usePlayer } = createPlayer({
features: [...features.audio],
});
function AudioController() {
const playback = usePlayer(features.playback);
const time = usePlayer(features.time);
const playback = usePlayer(selectPlayback);
const time = usePlayer(selectTime);
// Programmatic control, no UI
useEffect(() => {
if (time && time.currentTime > 60) {
playback?.pause();
}
}, [time?.currentTime]);
}, [time?.currentTime, playback]);
return <audio src="audio.mp3" />;
}
@@ -356,7 +364,7 @@ function AudioController() {
```tsx
function TimeDisplay() {
// Only re-renders when currentTime changes
const currentTime = usePlayer(features.time, s => s.currentTime);
const currentTime = usePlayer((s) => s.currentTime);
if (currentTime === undefined) return null;
@@ -369,7 +377,7 @@ function TimeDisplay() {
```tsx
function PlayState() {
// Derived value, re-renders when paused OR ended changes
const isPlaying = usePlayer(features.playback, s => !s.paused && !s.ended);
const isPlaying = usePlayer((s) => !s.paused && !s.ended);
if (isPlaying === undefined) return null;
@@ -381,8 +389,8 @@ function PlayState() {
```tsx
function DebugPanel() {
// Global selector across all features
const debug = usePlayer(s => ({
// Select across multiple properties
const debug = usePlayer((s) => ({
paused: s.paused,
currentTime: s.currentTime,
volume: s.volume,
@@ -390,3 +398,32 @@ function DebugPanel() {
return <pre>{JSON.stringify(debug, null, 2)}</pre>;
}
```
## 10. Using Mixins (HTML)
When you need custom player behavior:
```ts
import { createPlayer, MediaElement } from '@videojs/html';
import { features } from '@videojs/core/dom';
const { PlayerMixin } = createPlayer({
features: [...features.video],
});
class MyVideoPlayer extends PlayerMixin(MediaElement) {
override connectedCallback() {
super.connectedCallback();
// Custom initialization
this.addEventListener('fullscreenchange', this.#onFullscreen);
}
#onFullscreen = () => {
// Custom fullscreen handling
};
}
customElements.define('my-video-player', MyVideoPlayer);
```