@mediafox/react 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +110 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1 -0
- package/dist/useMediaFox.d.ts +68 -0
- package/dist/useMediaFox.d.ts.map +1 -0
- package/package.json +60 -0
package/README.md
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# @mediafox/react
|
|
2
|
+
|
|
3
|
+
React hooks for [MediaFox](https://github.com/wiedymi/mediafox) Media Player.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @mediafox/react @mediafox/core
|
|
9
|
+
# or
|
|
10
|
+
bun add @mediafox/react @mediafox/core
|
|
11
|
+
# or
|
|
12
|
+
yarn add @mediafox/react @mediafox/core
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Usage
|
|
16
|
+
|
|
17
|
+
### Basic Example
|
|
18
|
+
|
|
19
|
+
```tsx
|
|
20
|
+
import { useRef, useEffect } from 'react';
|
|
21
|
+
import { useMediaFox } from '@mediafox/react';
|
|
22
|
+
|
|
23
|
+
function VideoPlayer() {
|
|
24
|
+
const canvasRef = useRef<HTMLCanvasElement>(null);
|
|
25
|
+
const { state, play, pause, load } = useMediaFox({
|
|
26
|
+
renderTarget: canvasRef.current,
|
|
27
|
+
onError: (error) => console.error('Player error:', error),
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
useEffect(() => {
|
|
31
|
+
load('/path/to/video.mp4');
|
|
32
|
+
}, [load]);
|
|
33
|
+
|
|
34
|
+
return (
|
|
35
|
+
<div>
|
|
36
|
+
<canvas ref={canvasRef} />
|
|
37
|
+
<div>
|
|
38
|
+
<button onClick={play} disabled={!state?.canPlay}>
|
|
39
|
+
Play
|
|
40
|
+
</button>
|
|
41
|
+
<button onClick={pause}>Pause</button>
|
|
42
|
+
<p>
|
|
43
|
+
{state?.currentTime.toFixed(2)}s / {state?.duration.toFixed(2)}s
|
|
44
|
+
</p>
|
|
45
|
+
</div>
|
|
46
|
+
</div>
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### With Event Handlers
|
|
52
|
+
|
|
53
|
+
```tsx
|
|
54
|
+
import { useMediaFox } from '@mediafox/react';
|
|
55
|
+
|
|
56
|
+
function VideoPlayer() {
|
|
57
|
+
const canvasRef = useRef<HTMLCanvasElement>(null);
|
|
58
|
+
const { state, play, pause, seek, load } = useMediaFox({
|
|
59
|
+
renderTarget: canvasRef.current,
|
|
60
|
+
volume: 0.8,
|
|
61
|
+
onPlay: () => console.log('Playing'),
|
|
62
|
+
onPause: () => console.log('Paused'),
|
|
63
|
+
onEnded: () => console.log('Ended'),
|
|
64
|
+
onTimeUpdate: ({ currentTime }) => console.log('Time:', currentTime),
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
// ... rest of component
|
|
68
|
+
}
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## API
|
|
72
|
+
|
|
73
|
+
### `useMediaFox(options?)`
|
|
74
|
+
|
|
75
|
+
Returns an object with:
|
|
76
|
+
|
|
77
|
+
- `player` - The MediaFox instance (or null if not initialized)
|
|
78
|
+
- `state` - Current player state (reactive)
|
|
79
|
+
- `load(source, options?)` - Load a media source
|
|
80
|
+
- `play()` - Start playback
|
|
81
|
+
- `pause()` - Pause playback
|
|
82
|
+
- `seek(time, options?)` - Seek to time in seconds
|
|
83
|
+
- `stop()` - Stop playback and reset to start
|
|
84
|
+
- `screenshot(options?)` - Capture current frame
|
|
85
|
+
- `setRenderTarget(canvas)` - Update render target
|
|
86
|
+
|
|
87
|
+
### Options
|
|
88
|
+
|
|
89
|
+
All `PlayerOptions` from `@mediafox/core` plus event handlers:
|
|
90
|
+
|
|
91
|
+
- `onLoadStart`, `onLoadedMetadata`, `onLoadedData`
|
|
92
|
+
- `onCanPlay`, `onCanPlayThrough`
|
|
93
|
+
- `onPlay`, `onPause`, `onPlaying`, `onEnded`
|
|
94
|
+
- `onTimeUpdate`, `onDurationChange`
|
|
95
|
+
- `onVolumeChange`, `onRateChange`
|
|
96
|
+
- `onSeeking`, `onSeeked`, `onWaiting`
|
|
97
|
+
- `onProgress`, `onError`, `onWarning`
|
|
98
|
+
- `onTrackChange`, `onStateChange`
|
|
99
|
+
|
|
100
|
+
## Features
|
|
101
|
+
|
|
102
|
+
- ✅ Automatic cleanup on unmount
|
|
103
|
+
- ✅ Optimized with `useSyncExternalStore` for React 18+
|
|
104
|
+
- ✅ SSR safe (lazy loads MediaFox)
|
|
105
|
+
- ✅ Fully typed with TypeScript
|
|
106
|
+
- ✅ Framework-agnostic core
|
|
107
|
+
|
|
108
|
+
## License
|
|
109
|
+
|
|
110
|
+
MIT
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export type { AudioTrackInfo, LoadOptions, MediaInfo, MediaSource, PlayerEventListener, PlayerEventMap, PlayerOptions, PlayerState, PlayerStateData, QualityLevel, ScreenshotOptions, SeekOptions, SubtitleTrackInfo, TimeRange, VideoTrackInfo, } from '@mediafox/core';
|
|
2
|
+
export type { UseMediaFoxOptions, UseMediaFoxReturn } from './useMediaFox';
|
|
3
|
+
export { useMediaFox } from './useMediaFox';
|
|
4
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,YAAY,EACV,cAAc,EACd,WAAW,EACX,SAAS,EACT,WAAW,EACX,mBAAmB,EACnB,cAAc,EACd,aAAa,EACb,WAAW,EACX,eAAe,EACf,YAAY,EACZ,iBAAiB,EACjB,WAAW,EACX,iBAAiB,EACjB,SAAS,EACT,cAAc,GACf,MAAM,gBAAgB,CAAC;AACxB,YAAY,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAC3E,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var m=Object.create;var{getPrototypeOf:E,defineProperty:B,getOwnPropertyNames:w}=Object;var O=Object.prototype.hasOwnProperty;var v=(x,I,F)=>{F=x!=null?m(E(x)):{};let U=I||!x||!x.__esModule?B(F,"default",{value:x,enumerable:!0}):F;for(let V of w(x))if(!O.call(U,V))B(U,V,{get:()=>x[V],enumerable:!0});return U};var S=((x)=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(x,{get:(I,F)=>(typeof require<"u"?require:I)[F]}):x)(function(x){if(typeof require<"u")return require.apply(this,arguments);throw Error('Dynamic require of "'+x+'" is not supported')});import{useCallback as T,useEffect as G,useMemo as k,useRef as j,useSyncExternalStore as C}from"react";function h(x={}){let I=j(null),F=j(null),U=j(new Set),V=k(()=>({renderTarget:x.renderTarget,audioContext:x.audioContext,volume:x.volume,muted:x.muted,playbackRate:x.playbackRate,autoplay:x.autoplay,preload:x.preload,crossOrigin:x.crossOrigin,maxCacheSize:x.maxCacheSize,renderer:x.renderer}),[x.renderTarget,x.audioContext,x.volume,x.muted,x.playbackRate,x.autoplay,x.preload,x.crossOrigin,x.maxCacheSize,x.renderer]);G(()=>{import("@mediafox/core").then(({MediaFox:L})=>{let M=new L(V);I.current=M;let P=M.subscribe((D)=>{F.current=D;for(let Q of U.current)Q()});F.current=M.getState();for(let D of U.current)D();return()=>{P.unsubscribe(),M.destroy(),I.current=null,F.current=null}})},[V]),G(()=>{let L=I.current;if(!L)return;let M=[],P=(D,Q)=>{if(Q)L.on(D,Q),M.push([D,Q])};return P("loadstart",x.onLoadStart),P("loadedmetadata",x.onLoadedMetadata),P("loadeddata",x.onLoadedData),P("canplay",x.onCanPlay),P("canplaythrough",x.onCanPlayThrough),P("play",x.onPlay),P("pause",x.onPause),P("playing",x.onPlaying),P("ended",x.onEnded),P("timeupdate",x.onTimeUpdate),P("durationchange",x.onDurationChange),P("volumechange",x.onVolumeChange),P("ratechange",x.onRateChange),P("seeking",x.onSeeking),P("seeked",x.onSeeked),P("waiting",x.onWaiting),P("progress",x.onProgress),P("error",x.onError),P("warning",x.onWarning),P("trackchange",x.onTrackChange),P("statechange",x.onStateChange),P("rendererchange",x.onRendererChange),P("rendererfallback",x.onRendererFallback),()=>{for(let[D,Q]of M)L.off(D,Q)}},[x.onLoadStart,x.onLoadedMetadata,x.onLoadedData,x.onCanPlay,x.onCanPlayThrough,x.onPlay,x.onPause,x.onPlaying,x.onEnded,x.onTimeUpdate,x.onDurationChange,x.onVolumeChange,x.onRateChange,x.onSeeking,x.onSeeked,x.onWaiting,x.onProgress,x.onError,x.onWarning,x.onTrackChange,x.onStateChange,x.onRendererChange,x.onRendererFallback]);let J=T((L)=>{return U.current.add(L),()=>{U.current.delete(L)}},[]),q=T(()=>F.current,[]),A=C(J,q,q),K=T(async(L,M)=>{if(!I.current)throw Error("Player not initialized");return I.current.load(L,M)},[]),N=T(async()=>{if(!I.current)throw Error("Player not initialized");return I.current.play()},[]),W=T(()=>{if(!I.current)throw Error("Player not initialized");I.current.pause()},[]),X=T(async(L,M)=>{if(!I.current)throw Error("Player not initialized");return I.current.seek(L,M)},[]),Y=T(async()=>{if(!I.current)throw Error("Player not initialized");return I.current.stop()},[]),Z=T(async(L)=>{if(!I.current)throw Error("Player not initialized");return I.current.screenshot(L)},[]),_=T(async(L)=>{if(!I.current)throw Error("Player not initialized");return I.current.setRenderTarget(L)},[]),$=T(async(L)=>{if(!I.current)throw Error("Player not initialized");return I.current.switchRenderer(L)},[]),z=A?.rendererType??null;return{player:I.current,state:A,load:K,play:N,pause:W,seek:X,stop:Y,screenshot:Z,setRenderTarget:_,switchRenderer:$,rendererType:z}}export{h as useMediaFox};
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import type { LoadOptions, MediaFox, MediaSource, PlayerEventListener, PlayerOptions, PlayerStateData, RendererType, ScreenshotOptions, SeekOptions } from '@mediafox/core';
|
|
2
|
+
export interface UseMediaFoxOptions extends PlayerOptions {
|
|
3
|
+
onLoadStart?: PlayerEventListener<'loadstart'>;
|
|
4
|
+
onLoadedMetadata?: PlayerEventListener<'loadedmetadata'>;
|
|
5
|
+
onLoadedData?: PlayerEventListener<'loadeddata'>;
|
|
6
|
+
onCanPlay?: PlayerEventListener<'canplay'>;
|
|
7
|
+
onCanPlayThrough?: PlayerEventListener<'canplaythrough'>;
|
|
8
|
+
onPlay?: PlayerEventListener<'play'>;
|
|
9
|
+
onPause?: PlayerEventListener<'pause'>;
|
|
10
|
+
onPlaying?: PlayerEventListener<'playing'>;
|
|
11
|
+
onEnded?: PlayerEventListener<'ended'>;
|
|
12
|
+
onTimeUpdate?: PlayerEventListener<'timeupdate'>;
|
|
13
|
+
onDurationChange?: PlayerEventListener<'durationchange'>;
|
|
14
|
+
onVolumeChange?: PlayerEventListener<'volumechange'>;
|
|
15
|
+
onRateChange?: PlayerEventListener<'ratechange'>;
|
|
16
|
+
onSeeking?: PlayerEventListener<'seeking'>;
|
|
17
|
+
onSeeked?: PlayerEventListener<'seeked'>;
|
|
18
|
+
onWaiting?: PlayerEventListener<'waiting'>;
|
|
19
|
+
onProgress?: PlayerEventListener<'progress'>;
|
|
20
|
+
onError?: PlayerEventListener<'error'>;
|
|
21
|
+
onWarning?: PlayerEventListener<'warning'>;
|
|
22
|
+
onTrackChange?: PlayerEventListener<'trackchange'>;
|
|
23
|
+
onStateChange?: PlayerEventListener<'statechange'>;
|
|
24
|
+
onRendererChange?: PlayerEventListener<'rendererchange'>;
|
|
25
|
+
onRendererFallback?: PlayerEventListener<'rendererfallback'>;
|
|
26
|
+
}
|
|
27
|
+
export interface UseMediaFoxReturn {
|
|
28
|
+
player: MediaFox | null;
|
|
29
|
+
state: PlayerStateData | null;
|
|
30
|
+
load: (source: MediaSource, options?: LoadOptions) => Promise<void>;
|
|
31
|
+
play: () => Promise<void>;
|
|
32
|
+
pause: () => void;
|
|
33
|
+
seek: (time: number, options?: SeekOptions) => Promise<void>;
|
|
34
|
+
stop: () => Promise<void>;
|
|
35
|
+
screenshot: (options?: ScreenshotOptions) => Promise<Blob | null>;
|
|
36
|
+
setRenderTarget: (canvas: HTMLCanvasElement | OffscreenCanvas) => Promise<void>;
|
|
37
|
+
switchRenderer: (type: RendererType) => Promise<void>;
|
|
38
|
+
rendererType: RendererType | null;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* React hook for MediaFox media player.
|
|
42
|
+
* Creates and manages a MediaFox instance with automatic cleanup.
|
|
43
|
+
*
|
|
44
|
+
* @example
|
|
45
|
+
* ```tsx
|
|
46
|
+
* function VideoPlayer() {
|
|
47
|
+
* const canvasRef = useRef<HTMLCanvasElement>(null);
|
|
48
|
+
* const { player, state, play, pause, load } = useMediaFox({
|
|
49
|
+
* renderTarget: canvasRef.current,
|
|
50
|
+
* onError: (error) => console.error(error)
|
|
51
|
+
* });
|
|
52
|
+
*
|
|
53
|
+
* useEffect(() => {
|
|
54
|
+
* load('/video.mp4');
|
|
55
|
+
* }, [load]);
|
|
56
|
+
*
|
|
57
|
+
* return (
|
|
58
|
+
* <div>
|
|
59
|
+
* <canvas ref={canvasRef} />
|
|
60
|
+
* <button onClick={play}>Play</button>
|
|
61
|
+
* <button onClick={pause}>Pause</button>
|
|
62
|
+
* </div>
|
|
63
|
+
* );
|
|
64
|
+
* }
|
|
65
|
+
* ```
|
|
66
|
+
*/
|
|
67
|
+
export declare function useMediaFox(options?: UseMediaFoxOptions): UseMediaFoxReturn;
|
|
68
|
+
//# sourceMappingURL=useMediaFox.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useMediaFox.d.ts","sourceRoot":"","sources":["../src/useMediaFox.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@mediafox/react",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "React hooks for MediaFox media player",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"module": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.js"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist"
|
|
17
|
+
],
|
|
18
|
+
"scripts": {
|
|
19
|
+
"dev": "bun run --watch src/index.ts",
|
|
20
|
+
"build": "bun run build:js && bun run build:types",
|
|
21
|
+
"build:js": "bun build src/index.ts --outdir=dist --target=browser --format=esm --minify --external react --external @mediafox/core",
|
|
22
|
+
"build:types": "tsc --emitDeclarationOnly",
|
|
23
|
+
"test": "bun test",
|
|
24
|
+
"lint": "biome lint ./src",
|
|
25
|
+
"format": "biome format ./src",
|
|
26
|
+
"check": "biome check ./src",
|
|
27
|
+
"typecheck": "tsc --noEmit",
|
|
28
|
+
"prepublishOnly": "bun run build"
|
|
29
|
+
},
|
|
30
|
+
"publishConfig": {
|
|
31
|
+
"access": "public"
|
|
32
|
+
},
|
|
33
|
+
"peerDependencies": {
|
|
34
|
+
"@mediafox/core": "^1.0.0",
|
|
35
|
+
"react": ">=18.0.0"
|
|
36
|
+
},
|
|
37
|
+
"devDependencies": {
|
|
38
|
+
"@types/bun": "latest",
|
|
39
|
+
"@types/react": "^18.3.18"
|
|
40
|
+
},
|
|
41
|
+
"keywords": [
|
|
42
|
+
"react",
|
|
43
|
+
"hooks",
|
|
44
|
+
"video",
|
|
45
|
+
"player",
|
|
46
|
+
"mediafox",
|
|
47
|
+
"mediabunny"
|
|
48
|
+
],
|
|
49
|
+
"author": "",
|
|
50
|
+
"license": "MIT",
|
|
51
|
+
"repository": {
|
|
52
|
+
"type": "git",
|
|
53
|
+
"url": "https://github.com/wiedymi/mediafox.git",
|
|
54
|
+
"directory": "packages/react"
|
|
55
|
+
},
|
|
56
|
+
"bugs": {
|
|
57
|
+
"url": "https://github.com/wiedymi/mediafox/issues"
|
|
58
|
+
},
|
|
59
|
+
"homepage": "https://github.com/wiedymi/mediafox#readme"
|
|
60
|
+
}
|