@frameset/plex-player 1.0.6 → 2.0.1
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/LICENSE +1 -1
- package/README.md +310 -505
- package/dist/index.d.ts +438 -0
- package/dist/index.esm.js +2 -0
- package/dist/index.esm.js.map +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/styles.css +1 -0
- package/package.json +67 -104
- package/dist/plex-player.cjs.js +0 -1027
- package/dist/plex-player.cjs.js.map +0 -1
- package/dist/plex-player.css +0 -1651
- package/dist/plex-player.css.map +0 -1
- package/dist/plex-player.d.ts +0 -457
- package/dist/plex-player.esm.js +0 -1021
- package/dist/plex-player.esm.js.map +0 -1
- package/dist/plex-player.js +0 -1033
- package/dist/plex-player.js.map +0 -1
- package/dist/plex-player.min.js +0 -9
- package/dist/plex-player.min.js.map +0 -1
- package/dist/react/index.esm.js +0 -257
- package/dist/react/index.esm.js.map +0 -1
- package/dist/react/index.js +0 -265
- package/dist/react/index.js.map +0 -1
- package/dist/styles.tmp.js +0 -1
- package/dist/vue/index.esm.js +0 -283
- package/dist/vue/index.esm.js.map +0 -1
- package/dist/vue/index.js +0 -291
- package/dist/vue/index.js.map +0 -1
- package/src/core/index.js +0 -1082
- package/src/core/player-core.js +0 -225
- package/src/react/index.jsx +0 -277
- package/src/styles.js +0 -8
- package/src/types/index.d.ts +0 -457
- package/src/vue/index.js +0 -304
package/src/core/player-core.js
DELETED
|
@@ -1,225 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* PlexPlayer - Core Module
|
|
3
|
-
* Handles video element and basic playback functionality
|
|
4
|
-
*/
|
|
5
|
-
const PlexPlayerCore = (function() {
|
|
6
|
-
'use strict';
|
|
7
|
-
|
|
8
|
-
let container = null;
|
|
9
|
-
let video = null;
|
|
10
|
-
let player = null;
|
|
11
|
-
let state = {
|
|
12
|
-
isPlaying: false,
|
|
13
|
-
currentTime: 0,
|
|
14
|
-
duration: 0,
|
|
15
|
-
playbackRate: 1
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
function init(playerInstance) {
|
|
19
|
-
player = playerInstance;
|
|
20
|
-
container = player.container;
|
|
21
|
-
|
|
22
|
-
createVideoElement();
|
|
23
|
-
bindEvents();
|
|
24
|
-
|
|
25
|
-
// Apply initial settings
|
|
26
|
-
if (player.options.muted) {
|
|
27
|
-
video.muted = true;
|
|
28
|
-
}
|
|
29
|
-
if (player.options.volume !== undefined) {
|
|
30
|
-
video.volume = player.options.volume;
|
|
31
|
-
}
|
|
32
|
-
if (player.options.loop) {
|
|
33
|
-
video.loop = true;
|
|
34
|
-
}
|
|
35
|
-
if (player.options.poster) {
|
|
36
|
-
video.poster = player.options.poster;
|
|
37
|
-
}
|
|
38
|
-
if (player.options.preload) {
|
|
39
|
-
video.preload = player.options.preload;
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
function createVideoElement() {
|
|
44
|
-
video = document.createElement('video');
|
|
45
|
-
video.className = 'plex-video';
|
|
46
|
-
video.setAttribute('playsinline', '');
|
|
47
|
-
video.setAttribute('webkit-playsinline', '');
|
|
48
|
-
container.insertBefore(video, container.firstChild);
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
function bindEvents() {
|
|
52
|
-
video.addEventListener('play', onPlay);
|
|
53
|
-
video.addEventListener('pause', onPause);
|
|
54
|
-
video.addEventListener('ended', onEnded);
|
|
55
|
-
video.addEventListener('timeupdate', onTimeUpdate);
|
|
56
|
-
video.addEventListener('progress', onProgress);
|
|
57
|
-
video.addEventListener('loadedmetadata', onLoadedMetadata);
|
|
58
|
-
video.addEventListener('error', onError);
|
|
59
|
-
video.addEventListener('waiting', onWaiting);
|
|
60
|
-
video.addEventListener('canplay', onCanPlay);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
function onPlay() {
|
|
64
|
-
state.isPlaying = true;
|
|
65
|
-
container.classList.add('plex-playing');
|
|
66
|
-
container.classList.remove('plex-paused');
|
|
67
|
-
player._emit('play');
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
function onPause() {
|
|
71
|
-
state.isPlaying = false;
|
|
72
|
-
container.classList.remove('plex-playing');
|
|
73
|
-
container.classList.add('plex-paused');
|
|
74
|
-
player._emit('pause');
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
function onEnded() {
|
|
78
|
-
state.isPlaying = false;
|
|
79
|
-
container.classList.remove('plex-playing');
|
|
80
|
-
player._emit('ended');
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
function onTimeUpdate() {
|
|
84
|
-
state.currentTime = video.currentTime;
|
|
85
|
-
state.duration = video.duration;
|
|
86
|
-
player._emit('timeupdate', {
|
|
87
|
-
currentTime: video.currentTime,
|
|
88
|
-
duration: video.duration
|
|
89
|
-
});
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
function onProgress() {
|
|
93
|
-
const buffered = video.buffered;
|
|
94
|
-
let loaded = 0;
|
|
95
|
-
if (buffered.length > 0) {
|
|
96
|
-
loaded = buffered.end(buffered.length - 1) / video.duration * 100;
|
|
97
|
-
}
|
|
98
|
-
player._emit('progress', { buffered: loaded });
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
function onLoadedMetadata() {
|
|
102
|
-
state.duration = video.duration;
|
|
103
|
-
player._emit('loadedmetadata', { duration: video.duration });
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
function onError(e) {
|
|
107
|
-
player._emit('error', {
|
|
108
|
-
code: video.error?.code,
|
|
109
|
-
message: video.error?.message
|
|
110
|
-
});
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
function onWaiting() {
|
|
114
|
-
container.classList.add('plex-loading');
|
|
115
|
-
player._emit('waiting');
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
function onCanPlay() {
|
|
119
|
-
container.classList.remove('plex-loading');
|
|
120
|
-
player._emit('canplay');
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
// Public API
|
|
124
|
-
function load(src) {
|
|
125
|
-
video.src = src;
|
|
126
|
-
video.load();
|
|
127
|
-
|
|
128
|
-
if (player.options.autoplay) {
|
|
129
|
-
play();
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
function play() {
|
|
134
|
-
return video.play();
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
function pause() {
|
|
138
|
-
video.pause();
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
function togglePlay() {
|
|
142
|
-
if (video.paused) {
|
|
143
|
-
play();
|
|
144
|
-
} else {
|
|
145
|
-
pause();
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
function seek(time) {
|
|
150
|
-
video.currentTime = Math.max(0, Math.min(time, video.duration));
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
function seekPercent(percent) {
|
|
154
|
-
const time = (percent / 100) * video.duration;
|
|
155
|
-
seek(time);
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
function setPlaybackRate(rate) {
|
|
159
|
-
video.playbackRate = rate;
|
|
160
|
-
state.playbackRate = rate;
|
|
161
|
-
player._emit('ratechange', { rate });
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
function getPlaybackRate() {
|
|
165
|
-
return video.playbackRate;
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
function getCurrentTime() {
|
|
169
|
-
return video.currentTime;
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
function getDuration() {
|
|
173
|
-
return video.duration;
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
function isPlaying() {
|
|
177
|
-
return !video.paused && !video.ended;
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
function isPaused() {
|
|
181
|
-
return video.paused;
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
function getVideo() {
|
|
185
|
-
return video;
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
function destroy() {
|
|
189
|
-
video.removeEventListener('play', onPlay);
|
|
190
|
-
video.removeEventListener('pause', onPause);
|
|
191
|
-
video.removeEventListener('ended', onEnded);
|
|
192
|
-
video.removeEventListener('timeupdate', onTimeUpdate);
|
|
193
|
-
video.removeEventListener('progress', onProgress);
|
|
194
|
-
video.removeEventListener('loadedmetadata', onLoadedMetadata);
|
|
195
|
-
video.removeEventListener('error', onError);
|
|
196
|
-
video.removeEventListener('waiting', onWaiting);
|
|
197
|
-
video.removeEventListener('canplay', onCanPlay);
|
|
198
|
-
video.remove();
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
return {
|
|
202
|
-
init,
|
|
203
|
-
load,
|
|
204
|
-
play,
|
|
205
|
-
pause,
|
|
206
|
-
togglePlay,
|
|
207
|
-
seek,
|
|
208
|
-
seekPercent,
|
|
209
|
-
setPlaybackRate,
|
|
210
|
-
getPlaybackRate,
|
|
211
|
-
getCurrentTime,
|
|
212
|
-
getDuration,
|
|
213
|
-
isPlaying,
|
|
214
|
-
isPaused,
|
|
215
|
-
getVideo,
|
|
216
|
-
destroy
|
|
217
|
-
};
|
|
218
|
-
})();
|
|
219
|
-
|
|
220
|
-
// Export for ES modules
|
|
221
|
-
if (typeof module !== 'undefined' && module.exports) {
|
|
222
|
-
module.exports = { PlexPlayerCore };
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
export { PlexPlayerCore };
|
package/src/react/index.jsx
DELETED
|
@@ -1,277 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @frameset/plex-player - React Component
|
|
3
|
-
* Professional Video Player by FRAMESET Studio
|
|
4
|
-
* https://frameset.dev
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import React, {
|
|
8
|
-
useEffect,
|
|
9
|
-
useRef,
|
|
10
|
-
useImperativeHandle,
|
|
11
|
-
forwardRef,
|
|
12
|
-
useState,
|
|
13
|
-
useCallback,
|
|
14
|
-
createContext,
|
|
15
|
-
useContext,
|
|
16
|
-
} from 'react';
|
|
17
|
-
|
|
18
|
-
// Import core player
|
|
19
|
-
import PlexPlayer from '../core/index.js';
|
|
20
|
-
|
|
21
|
-
// Context for nested components
|
|
22
|
-
const PlexPlayerContext = createContext(null);
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Hook to access player instance from child components
|
|
26
|
-
*/
|
|
27
|
-
export const usePlexPlayer = () => {
|
|
28
|
-
const player = useContext(PlexPlayerContext);
|
|
29
|
-
return player;
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* PlexPlayer React Component
|
|
34
|
-
*/
|
|
35
|
-
export const PlexPlayerReact = forwardRef(function PlexPlayerReact(props, ref) {
|
|
36
|
-
const {
|
|
37
|
-
src,
|
|
38
|
-
playlist,
|
|
39
|
-
className = '',
|
|
40
|
-
style = {},
|
|
41
|
-
autoplay = false,
|
|
42
|
-
muted = false,
|
|
43
|
-
loop = false,
|
|
44
|
-
volume = 1,
|
|
45
|
-
poster,
|
|
46
|
-
preload = 'metadata',
|
|
47
|
-
keyboard = true,
|
|
48
|
-
touch = true,
|
|
49
|
-
pip = true,
|
|
50
|
-
cast = true,
|
|
51
|
-
fullscreen = true,
|
|
52
|
-
controlsHideDelay = 3000,
|
|
53
|
-
theme,
|
|
54
|
-
subtitles,
|
|
55
|
-
ads,
|
|
56
|
-
i18n,
|
|
57
|
-
// Event callbacks
|
|
58
|
-
onPlay,
|
|
59
|
-
onPause,
|
|
60
|
-
onEnded,
|
|
61
|
-
onTimeUpdate,
|
|
62
|
-
onProgress,
|
|
63
|
-
onVolumeChange,
|
|
64
|
-
onFullscreenChange,
|
|
65
|
-
onError,
|
|
66
|
-
onReady,
|
|
67
|
-
// Children for custom overlays
|
|
68
|
-
children,
|
|
69
|
-
...restProps
|
|
70
|
-
} = props;
|
|
71
|
-
|
|
72
|
-
const containerRef = useRef(null);
|
|
73
|
-
const playerRef = useRef(null);
|
|
74
|
-
const [isReady, setIsReady] = useState(false);
|
|
75
|
-
|
|
76
|
-
// Initialize player
|
|
77
|
-
useEffect(() => {
|
|
78
|
-
if (!containerRef.current) return;
|
|
79
|
-
|
|
80
|
-
// Create player instance
|
|
81
|
-
const player = new PlexPlayer({
|
|
82
|
-
container: containerRef.current,
|
|
83
|
-
autoplay,
|
|
84
|
-
muted,
|
|
85
|
-
loop,
|
|
86
|
-
volume,
|
|
87
|
-
poster,
|
|
88
|
-
preload,
|
|
89
|
-
keyboard,
|
|
90
|
-
touch,
|
|
91
|
-
pip,
|
|
92
|
-
cast,
|
|
93
|
-
fullscreen,
|
|
94
|
-
controlsHideDelay,
|
|
95
|
-
theme,
|
|
96
|
-
subtitles,
|
|
97
|
-
ads,
|
|
98
|
-
i18n,
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
playerRef.current = player;
|
|
102
|
-
|
|
103
|
-
// Bind events
|
|
104
|
-
if (onPlay) player.on('play', onPlay);
|
|
105
|
-
if (onPause) player.on('pause', onPause);
|
|
106
|
-
if (onEnded) player.on('ended', onEnded);
|
|
107
|
-
if (onTimeUpdate) {
|
|
108
|
-
player.on('timeupdate', (data) => onTimeUpdate(data.currentTime));
|
|
109
|
-
}
|
|
110
|
-
if (onProgress) {
|
|
111
|
-
player.on('progress', (data) => onProgress(data.buffered));
|
|
112
|
-
}
|
|
113
|
-
if (onVolumeChange) {
|
|
114
|
-
player.on('volumechange', (data) => onVolumeChange(data.volume, data.muted));
|
|
115
|
-
}
|
|
116
|
-
if (onFullscreenChange) {
|
|
117
|
-
player.on('fullscreenchange', (data) => onFullscreenChange(data.isFullscreen));
|
|
118
|
-
}
|
|
119
|
-
if (onError) {
|
|
120
|
-
player.on('error', onError);
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
setIsReady(true);
|
|
124
|
-
|
|
125
|
-
if (onReady) {
|
|
126
|
-
onReady(player);
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
// Cleanup
|
|
130
|
-
return () => {
|
|
131
|
-
player.destroy();
|
|
132
|
-
playerRef.current = null;
|
|
133
|
-
};
|
|
134
|
-
}, []);
|
|
135
|
-
|
|
136
|
-
// Load source when src changes
|
|
137
|
-
useEffect(() => {
|
|
138
|
-
if (!playerRef.current || !isReady) return;
|
|
139
|
-
|
|
140
|
-
if (src) {
|
|
141
|
-
playerRef.current.load(src);
|
|
142
|
-
}
|
|
143
|
-
}, [src, isReady]);
|
|
144
|
-
|
|
145
|
-
// Load playlist when playlist changes
|
|
146
|
-
useEffect(() => {
|
|
147
|
-
if (!playerRef.current || !isReady || !playlist) return;
|
|
148
|
-
|
|
149
|
-
playerRef.current.loadPlaylist(playlist);
|
|
150
|
-
}, [playlist, isReady]);
|
|
151
|
-
|
|
152
|
-
// Update volume
|
|
153
|
-
useEffect(() => {
|
|
154
|
-
if (playerRef.current && isReady) {
|
|
155
|
-
playerRef.current.setVolume(volume);
|
|
156
|
-
}
|
|
157
|
-
}, [volume, isReady]);
|
|
158
|
-
|
|
159
|
-
// Update muted
|
|
160
|
-
useEffect(() => {
|
|
161
|
-
if (playerRef.current && isReady) {
|
|
162
|
-
if (muted) {
|
|
163
|
-
playerRef.current.mute();
|
|
164
|
-
} else {
|
|
165
|
-
playerRef.current.unmute();
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
}, [muted, isReady]);
|
|
169
|
-
|
|
170
|
-
// Expose player methods via ref
|
|
171
|
-
useImperativeHandle(ref, () => ({
|
|
172
|
-
play: () => playerRef.current?.play(),
|
|
173
|
-
pause: () => playerRef.current?.pause(),
|
|
174
|
-
togglePlay: () => playerRef.current?.togglePlay(),
|
|
175
|
-
seek: (time) => playerRef.current?.seek(time),
|
|
176
|
-
seekPercent: (percent) => playerRef.current?.seekPercent(percent),
|
|
177
|
-
setVolume: (vol) => playerRef.current?.setVolume(vol),
|
|
178
|
-
getVolume: () => playerRef.current?.getVolume(),
|
|
179
|
-
mute: () => playerRef.current?.mute(),
|
|
180
|
-
unmute: () => playerRef.current?.unmute(),
|
|
181
|
-
toggleMute: () => playerRef.current?.toggleMute(),
|
|
182
|
-
isMuted: () => playerRef.current?.isMuted(),
|
|
183
|
-
setPlaybackRate: (rate) => playerRef.current?.setPlaybackRate(rate),
|
|
184
|
-
enterFullscreen: () => playerRef.current?.enterFullscreen(),
|
|
185
|
-
exitFullscreen: () => playerRef.current?.exitFullscreen(),
|
|
186
|
-
toggleFullscreen: () => playerRef.current?.toggleFullscreen(),
|
|
187
|
-
enterPiP: () => playerRef.current?.enterPiP(),
|
|
188
|
-
exitPiP: () => playerRef.current?.exitPiP(),
|
|
189
|
-
togglePiP: () => playerRef.current?.togglePiP(),
|
|
190
|
-
cast: () => playerRef.current?.cast(),
|
|
191
|
-
getState: () => playerRef.current?.getState(),
|
|
192
|
-
getPlayer: () => playerRef.current,
|
|
193
|
-
next: () => playerRef.current?.next(),
|
|
194
|
-
previous: () => playerRef.current?.previous(),
|
|
195
|
-
playAt: (index) => playerRef.current?.playAt(index),
|
|
196
|
-
}));
|
|
197
|
-
|
|
198
|
-
return (
|
|
199
|
-
<PlexPlayerContext.Provider value={playerRef.current}>
|
|
200
|
-
<div
|
|
201
|
-
ref={containerRef}
|
|
202
|
-
className={`plex-player-container ${className}`}
|
|
203
|
-
style={{
|
|
204
|
-
width: '100%',
|
|
205
|
-
aspectRatio: '16 / 9',
|
|
206
|
-
position: 'relative',
|
|
207
|
-
...style,
|
|
208
|
-
}}
|
|
209
|
-
{...restProps}
|
|
210
|
-
>
|
|
211
|
-
{children}
|
|
212
|
-
</div>
|
|
213
|
-
</PlexPlayerContext.Provider>
|
|
214
|
-
);
|
|
215
|
-
});
|
|
216
|
-
|
|
217
|
-
// Additional hooks for convenience
|
|
218
|
-
export const usePlexPlayerState = () => {
|
|
219
|
-
const player = usePlexPlayer();
|
|
220
|
-
const [state, setState] = useState(null);
|
|
221
|
-
|
|
222
|
-
useEffect(() => {
|
|
223
|
-
if (!player) return;
|
|
224
|
-
|
|
225
|
-
const updateState = () => {
|
|
226
|
-
setState(player.getState());
|
|
227
|
-
};
|
|
228
|
-
|
|
229
|
-
// Update on every timeupdate
|
|
230
|
-
player.on('timeupdate', updateState);
|
|
231
|
-
player.on('play', updateState);
|
|
232
|
-
player.on('pause', updateState);
|
|
233
|
-
player.on('volumechange', updateState);
|
|
234
|
-
player.on('fullscreenchange', updateState);
|
|
235
|
-
|
|
236
|
-
// Initial state
|
|
237
|
-
updateState();
|
|
238
|
-
|
|
239
|
-
return () => {
|
|
240
|
-
player.off('timeupdate', updateState);
|
|
241
|
-
player.off('play', updateState);
|
|
242
|
-
player.off('pause', updateState);
|
|
243
|
-
player.off('volumechange', updateState);
|
|
244
|
-
player.off('fullscreenchange', updateState);
|
|
245
|
-
};
|
|
246
|
-
}, [player]);
|
|
247
|
-
|
|
248
|
-
return state;
|
|
249
|
-
};
|
|
250
|
-
|
|
251
|
-
export const usePlexPlayerTime = () => {
|
|
252
|
-
const player = usePlexPlayer();
|
|
253
|
-
const [time, setTime] = useState({ current: 0, duration: 0 });
|
|
254
|
-
|
|
255
|
-
useEffect(() => {
|
|
256
|
-
if (!player) return;
|
|
257
|
-
|
|
258
|
-
const updateTime = (data) => {
|
|
259
|
-
setTime({
|
|
260
|
-
current: data.currentTime,
|
|
261
|
-
duration: data.duration,
|
|
262
|
-
});
|
|
263
|
-
};
|
|
264
|
-
|
|
265
|
-
player.on('timeupdate', updateTime);
|
|
266
|
-
|
|
267
|
-
return () => {
|
|
268
|
-
player.off('timeupdate', updateTime);
|
|
269
|
-
};
|
|
270
|
-
}, [player]);
|
|
271
|
-
|
|
272
|
-
return time;
|
|
273
|
-
};
|
|
274
|
-
|
|
275
|
-
// Named exports
|
|
276
|
-
export { PlexPlayer };
|
|
277
|
-
export default PlexPlayerReact;
|