mirror of
https://github.com/zoriya/v10.git
synced 2026-08-16 02:45:09 +00:00
docs(rfc): update player api to match implementation (#375)
This commit is contained in:
+80
-43
@@ -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);
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user