@frameset/plex-player 1.0.6 → 2.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/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 +66 -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/dist/index.d.ts
ADDED
|
@@ -0,0 +1,438 @@
|
|
|
1
|
+
import React$1 from 'react';
|
|
2
|
+
|
|
3
|
+
interface VideoSource {
|
|
4
|
+
src: string;
|
|
5
|
+
type?: string;
|
|
6
|
+
quality?: string;
|
|
7
|
+
label?: string;
|
|
8
|
+
}
|
|
9
|
+
interface TextTrack {
|
|
10
|
+
src: string;
|
|
11
|
+
kind: 'subtitles' | 'captions' | 'descriptions' | 'chapters' | 'metadata';
|
|
12
|
+
srclang: string;
|
|
13
|
+
label: string;
|
|
14
|
+
default?: boolean;
|
|
15
|
+
}
|
|
16
|
+
interface VastConfig {
|
|
17
|
+
url: string;
|
|
18
|
+
skipDelay?: number;
|
|
19
|
+
position?: 'preroll' | 'midroll' | 'postroll';
|
|
20
|
+
midrollTime?: number;
|
|
21
|
+
}
|
|
22
|
+
interface PlexVideoPlayerProps {
|
|
23
|
+
/** Video source URL or array of sources */
|
|
24
|
+
src: string | VideoSource[];
|
|
25
|
+
/** Poster image URL */
|
|
26
|
+
poster?: string;
|
|
27
|
+
/** Enable autoplay */
|
|
28
|
+
autoPlay?: boolean;
|
|
29
|
+
/** Mute video on load */
|
|
30
|
+
muted?: boolean;
|
|
31
|
+
/** Loop video playback */
|
|
32
|
+
loop?: boolean;
|
|
33
|
+
/** Preload behavior */
|
|
34
|
+
preload?: 'none' | 'metadata' | 'auto';
|
|
35
|
+
/** Player width */
|
|
36
|
+
width?: number | string;
|
|
37
|
+
/** Player height */
|
|
38
|
+
height?: number | string;
|
|
39
|
+
/** Enable controls */
|
|
40
|
+
controls?: boolean;
|
|
41
|
+
/** Enable Picture-in-Picture */
|
|
42
|
+
pip?: boolean;
|
|
43
|
+
/** Enable fullscreen */
|
|
44
|
+
fullscreen?: boolean;
|
|
45
|
+
/** Enable playback speed control */
|
|
46
|
+
playbackSpeed?: boolean;
|
|
47
|
+
/** Available playback speeds */
|
|
48
|
+
playbackSpeeds?: number[];
|
|
49
|
+
/** Enable volume control */
|
|
50
|
+
volume?: boolean;
|
|
51
|
+
/** Initial volume (0-1) */
|
|
52
|
+
initialVolume?: number;
|
|
53
|
+
/** Enable progress bar */
|
|
54
|
+
progressBar?: boolean;
|
|
55
|
+
/** Enable time display */
|
|
56
|
+
timeDisplay?: boolean;
|
|
57
|
+
/** Enable quality selector */
|
|
58
|
+
qualitySelector?: boolean;
|
|
59
|
+
/** Text tracks (subtitles) */
|
|
60
|
+
textTracks?: TextTrack[];
|
|
61
|
+
/** VAST ads configuration */
|
|
62
|
+
vast?: VastConfig | VastConfig[];
|
|
63
|
+
/** Enable keyboard shortcuts */
|
|
64
|
+
keyboard?: boolean;
|
|
65
|
+
/** Hotkey mappings */
|
|
66
|
+
hotkeys?: HotkeyConfig;
|
|
67
|
+
/** Custom CSS class */
|
|
68
|
+
className?: string;
|
|
69
|
+
/** Inline styles */
|
|
70
|
+
style?: React.CSSProperties;
|
|
71
|
+
/** Accent color for controls */
|
|
72
|
+
accentColor?: string;
|
|
73
|
+
/** Control bar color theme */
|
|
74
|
+
theme?: 'dark' | 'light' | 'auto';
|
|
75
|
+
/** Timeout for hiding controls (ms) */
|
|
76
|
+
controlsTimeout?: number;
|
|
77
|
+
/** Enable double-click to fullscreen */
|
|
78
|
+
doubleClickFullscreen?: boolean;
|
|
79
|
+
/** Enable click to play/pause */
|
|
80
|
+
clickToPlay?: boolean;
|
|
81
|
+
/** Thumbnail preview on progress bar */
|
|
82
|
+
thumbnailPreview?: {
|
|
83
|
+
enabled: boolean;
|
|
84
|
+
sprites?: string;
|
|
85
|
+
interval?: number;
|
|
86
|
+
width?: number;
|
|
87
|
+
height?: number;
|
|
88
|
+
};
|
|
89
|
+
onPlay?: () => void;
|
|
90
|
+
onPause?: () => void;
|
|
91
|
+
onEnded?: () => void;
|
|
92
|
+
onTimeUpdate?: (time: number) => void;
|
|
93
|
+
onProgress?: (buffered: number) => void;
|
|
94
|
+
onVolumeChange?: (volume: number, muted: boolean) => void;
|
|
95
|
+
onSeeking?: (time: number) => void;
|
|
96
|
+
onSeeked?: (time: number) => void;
|
|
97
|
+
onRateChange?: (rate: number) => void;
|
|
98
|
+
onQualityChange?: (quality: string) => void;
|
|
99
|
+
onFullscreenChange?: (isFullscreen: boolean) => void;
|
|
100
|
+
onPipChange?: (isPip: boolean) => void;
|
|
101
|
+
onError?: (error: MediaError | null) => void;
|
|
102
|
+
onReady?: () => void;
|
|
103
|
+
onAdStart?: (ad: VastAdInfo) => void;
|
|
104
|
+
onAdEnd?: () => void;
|
|
105
|
+
onAdSkip?: () => void;
|
|
106
|
+
onAdError?: (error: Error) => void;
|
|
107
|
+
}
|
|
108
|
+
interface HotkeyConfig {
|
|
109
|
+
play?: string;
|
|
110
|
+
mute?: string;
|
|
111
|
+
fullscreen?: string;
|
|
112
|
+
pip?: string;
|
|
113
|
+
seekForward?: string;
|
|
114
|
+
seekBackward?: string;
|
|
115
|
+
volumeUp?: string;
|
|
116
|
+
volumeDown?: string;
|
|
117
|
+
}
|
|
118
|
+
interface VastAdInfo {
|
|
119
|
+
id: string;
|
|
120
|
+
title: string;
|
|
121
|
+
duration: number;
|
|
122
|
+
skipOffset?: number;
|
|
123
|
+
clickThrough?: string;
|
|
124
|
+
}
|
|
125
|
+
interface PlexVideoPlayerRef {
|
|
126
|
+
play: () => Promise<void>;
|
|
127
|
+
pause: () => void;
|
|
128
|
+
stop: () => void;
|
|
129
|
+
seek: (time: number) => void;
|
|
130
|
+
setVolume: (volume: number) => void;
|
|
131
|
+
mute: () => void;
|
|
132
|
+
unmute: () => void;
|
|
133
|
+
toggleMute: () => void;
|
|
134
|
+
enterFullscreen: () => Promise<void>;
|
|
135
|
+
exitFullscreen: () => Promise<void>;
|
|
136
|
+
toggleFullscreen: () => Promise<void>;
|
|
137
|
+
enterPip: () => Promise<void>;
|
|
138
|
+
exitPip: () => Promise<void>;
|
|
139
|
+
togglePip: () => Promise<void>;
|
|
140
|
+
setPlaybackRate: (rate: number) => void;
|
|
141
|
+
setQuality: (quality: string) => void;
|
|
142
|
+
getCurrentTime: () => number;
|
|
143
|
+
getDuration: () => number;
|
|
144
|
+
getVolume: () => number;
|
|
145
|
+
isMuted: () => boolean;
|
|
146
|
+
isPlaying: () => boolean;
|
|
147
|
+
isFullscreen: () => boolean;
|
|
148
|
+
isPip: () => boolean;
|
|
149
|
+
getVideoElement: () => HTMLVideoElement | null;
|
|
150
|
+
}
|
|
151
|
+
interface PlayerState {
|
|
152
|
+
isPlaying: boolean;
|
|
153
|
+
isPaused: boolean;
|
|
154
|
+
isEnded: boolean;
|
|
155
|
+
isBuffering: boolean;
|
|
156
|
+
isSeeking: boolean;
|
|
157
|
+
isFullscreen: boolean;
|
|
158
|
+
isPip: boolean;
|
|
159
|
+
isMuted: boolean;
|
|
160
|
+
isAdPlaying: boolean;
|
|
161
|
+
volume: number;
|
|
162
|
+
currentTime: number;
|
|
163
|
+
duration: number;
|
|
164
|
+
buffered: number;
|
|
165
|
+
playbackRate: number;
|
|
166
|
+
currentQuality: string | null;
|
|
167
|
+
error: MediaError | null;
|
|
168
|
+
}
|
|
169
|
+
interface UsePlayerOptions {
|
|
170
|
+
autoPlay?: boolean;
|
|
171
|
+
muted?: boolean;
|
|
172
|
+
loop?: boolean;
|
|
173
|
+
volume?: number;
|
|
174
|
+
playbackRate?: number;
|
|
175
|
+
}
|
|
176
|
+
interface UsePlayerReturn {
|
|
177
|
+
state: PlayerState;
|
|
178
|
+
videoRef: React.RefObject<HTMLVideoElement>;
|
|
179
|
+
containerRef: React.RefObject<HTMLDivElement>;
|
|
180
|
+
play: () => Promise<void>;
|
|
181
|
+
pause: () => void;
|
|
182
|
+
togglePlay: () => void;
|
|
183
|
+
seek: (time: number) => void;
|
|
184
|
+
setVolume: (volume: number) => void;
|
|
185
|
+
toggleMute: () => void;
|
|
186
|
+
setPlaybackRate: (rate: number) => void;
|
|
187
|
+
enterFullscreen: () => Promise<void>;
|
|
188
|
+
exitFullscreen: () => Promise<void>;
|
|
189
|
+
toggleFullscreen: () => Promise<void>;
|
|
190
|
+
enterPip: () => Promise<void>;
|
|
191
|
+
exitPip: () => Promise<void>;
|
|
192
|
+
togglePip: () => Promise<void>;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
declare const PlexVideoPlayer: React$1.ForwardRefExoticComponent<PlexVideoPlayerProps & React$1.RefAttributes<PlexVideoPlayerRef>>;
|
|
196
|
+
|
|
197
|
+
interface ProgressBarProps {
|
|
198
|
+
currentTime: number;
|
|
199
|
+
duration: number;
|
|
200
|
+
buffered: number;
|
|
201
|
+
onSeek: (time: number) => void;
|
|
202
|
+
thumbnailPreview?: {
|
|
203
|
+
enabled: boolean;
|
|
204
|
+
sprites?: string;
|
|
205
|
+
interval?: number;
|
|
206
|
+
width?: number;
|
|
207
|
+
height?: number;
|
|
208
|
+
};
|
|
209
|
+
disabled?: boolean;
|
|
210
|
+
}
|
|
211
|
+
declare const ProgressBar: React$1.FC<ProgressBarProps>;
|
|
212
|
+
|
|
213
|
+
interface VolumeControlProps {
|
|
214
|
+
volume: number;
|
|
215
|
+
muted: boolean;
|
|
216
|
+
onVolumeChange: (volume: number) => void;
|
|
217
|
+
onToggleMute: () => void;
|
|
218
|
+
}
|
|
219
|
+
declare const VolumeControl: React$1.FC<VolumeControlProps>;
|
|
220
|
+
|
|
221
|
+
interface SettingsMenuProps {
|
|
222
|
+
playbackRate: number;
|
|
223
|
+
playbackSpeeds: number[];
|
|
224
|
+
onPlaybackRateChange: (rate: number) => void;
|
|
225
|
+
qualityEnabled: boolean;
|
|
226
|
+
sources?: VideoSource[];
|
|
227
|
+
currentQuality?: string;
|
|
228
|
+
onQualityChange?: (quality: string) => void;
|
|
229
|
+
captionsEnabled?: boolean;
|
|
230
|
+
textTracks?: TextTrack[];
|
|
231
|
+
currentTrack?: string;
|
|
232
|
+
onTrackChange?: (track: string | null) => void;
|
|
233
|
+
}
|
|
234
|
+
declare const SettingsMenu: React$1.FC<SettingsMenuProps>;
|
|
235
|
+
|
|
236
|
+
interface AdOverlayProps {
|
|
237
|
+
ad: VastAdInfo;
|
|
238
|
+
timeRemaining: number;
|
|
239
|
+
canSkip: boolean;
|
|
240
|
+
onSkip: () => void;
|
|
241
|
+
onClick: () => void;
|
|
242
|
+
}
|
|
243
|
+
declare const AdOverlay: React$1.FC<AdOverlayProps>;
|
|
244
|
+
|
|
245
|
+
interface ErrorDisplayProps {
|
|
246
|
+
error: MediaError | null;
|
|
247
|
+
onRetry: () => void;
|
|
248
|
+
}
|
|
249
|
+
declare const ErrorDisplay: React$1.FC<ErrorDisplayProps>;
|
|
250
|
+
|
|
251
|
+
interface LoaderProps {
|
|
252
|
+
visible: boolean;
|
|
253
|
+
}
|
|
254
|
+
declare const Loader: React$1.FC<LoaderProps>;
|
|
255
|
+
|
|
256
|
+
interface IconProps {
|
|
257
|
+
className?: string;
|
|
258
|
+
size?: number;
|
|
259
|
+
}
|
|
260
|
+
declare const PlayIcon: React$1.FC<IconProps>;
|
|
261
|
+
declare const PauseIcon: React$1.FC<IconProps>;
|
|
262
|
+
declare const VolumeHighIcon: React$1.FC<IconProps>;
|
|
263
|
+
declare const VolumeMediumIcon: React$1.FC<IconProps>;
|
|
264
|
+
declare const VolumeLowIcon: React$1.FC<IconProps>;
|
|
265
|
+
declare const VolumeMuteIcon: React$1.FC<IconProps>;
|
|
266
|
+
declare const FullscreenIcon: React$1.FC<IconProps>;
|
|
267
|
+
declare const FullscreenExitIcon: React$1.FC<IconProps>;
|
|
268
|
+
declare const PipIcon: React$1.FC<IconProps>;
|
|
269
|
+
declare const PipExitIcon: React$1.FC<IconProps>;
|
|
270
|
+
declare const SettingsIcon: React$1.FC<IconProps>;
|
|
271
|
+
declare const CaptionsIcon: React$1.FC<IconProps>;
|
|
272
|
+
declare const SpeedIcon: React$1.FC<IconProps>;
|
|
273
|
+
declare const QualityIcon: React$1.FC<IconProps>;
|
|
274
|
+
declare const ForwardIcon: React$1.FC<IconProps>;
|
|
275
|
+
declare const RewindIcon: React$1.FC<IconProps>;
|
|
276
|
+
declare const SkipNextIcon: React$1.FC<IconProps>;
|
|
277
|
+
declare const SkipPrevIcon: React$1.FC<IconProps>;
|
|
278
|
+
declare const ErrorIcon: React$1.FC<IconProps>;
|
|
279
|
+
declare const ExternalLinkIcon: React$1.FC<IconProps>;
|
|
280
|
+
declare const CheckIcon: React$1.FC<IconProps>;
|
|
281
|
+
|
|
282
|
+
declare const usePlayer: (options?: UsePlayerOptions) => UsePlayerReturn;
|
|
283
|
+
|
|
284
|
+
interface UseKeyboardOptions {
|
|
285
|
+
enabled: boolean;
|
|
286
|
+
hotkeys?: HotkeyConfig;
|
|
287
|
+
onPlay: () => void;
|
|
288
|
+
onMute: () => void;
|
|
289
|
+
onFullscreen: () => void;
|
|
290
|
+
onPip: () => void;
|
|
291
|
+
onSeek: (delta: number) => void;
|
|
292
|
+
onVolume: (delta: number) => void;
|
|
293
|
+
containerRef: React.RefObject<HTMLDivElement>;
|
|
294
|
+
}
|
|
295
|
+
declare const useKeyboard: ({ enabled, hotkeys, onPlay, onMute, onFullscreen, onPip, onSeek, onVolume, containerRef, }: UseKeyboardOptions) => void;
|
|
296
|
+
|
|
297
|
+
interface UseVastOptions {
|
|
298
|
+
vastConfig?: VastConfig | VastConfig[];
|
|
299
|
+
videoRef: React.RefObject<HTMLVideoElement>;
|
|
300
|
+
onAdStart?: (ad: VastAdInfo) => void;
|
|
301
|
+
onAdEnd?: () => void;
|
|
302
|
+
onAdSkip?: () => void;
|
|
303
|
+
onAdError?: (error: Error) => void;
|
|
304
|
+
}
|
|
305
|
+
interface UseVastReturn {
|
|
306
|
+
isAdPlaying: boolean;
|
|
307
|
+
currentAd: VastAdInfo | null;
|
|
308
|
+
adTimeRemaining: number;
|
|
309
|
+
canSkip: boolean;
|
|
310
|
+
skipAd: () => void;
|
|
311
|
+
handleAdClick: () => void;
|
|
312
|
+
checkForAd: (currentTime: number, duration: number) => void;
|
|
313
|
+
}
|
|
314
|
+
declare const useVast: ({ vastConfig, videoRef, onAdStart, onAdEnd, onAdSkip, onAdError, }: UseVastOptions) => UseVastReturn;
|
|
315
|
+
|
|
316
|
+
/**
|
|
317
|
+
* Format time in seconds to MM:SS or HH:MM:SS format
|
|
318
|
+
*/
|
|
319
|
+
declare const formatTime: (seconds: number) => string;
|
|
320
|
+
/**
|
|
321
|
+
* Parse time string to seconds
|
|
322
|
+
*/
|
|
323
|
+
declare const parseTime: (time: string) => number;
|
|
324
|
+
/**
|
|
325
|
+
* Calculate percentage
|
|
326
|
+
*/
|
|
327
|
+
declare const percentage: (value: number, total: number) => number;
|
|
328
|
+
/**
|
|
329
|
+
* Clamp a value between min and max
|
|
330
|
+
*/
|
|
331
|
+
declare const clamp: (value: number, min: number, max: number) => number;
|
|
332
|
+
/**
|
|
333
|
+
* Throttle function execution
|
|
334
|
+
*/
|
|
335
|
+
declare const throttle: <T extends (...args: unknown[]) => unknown>(func: T, limit: number) => ((...args: Parameters<T>) => void);
|
|
336
|
+
/**
|
|
337
|
+
* Debounce function execution
|
|
338
|
+
*/
|
|
339
|
+
declare const debounce: <T extends (...args: unknown[]) => unknown>(func: T, wait: number) => ((...args: Parameters<T>) => void);
|
|
340
|
+
/**
|
|
341
|
+
* Check if fullscreen is supported
|
|
342
|
+
*/
|
|
343
|
+
declare const isFullscreenSupported: () => boolean;
|
|
344
|
+
/**
|
|
345
|
+
* Check if Picture-in-Picture is supported
|
|
346
|
+
*/
|
|
347
|
+
declare const isPipSupported: () => boolean;
|
|
348
|
+
/**
|
|
349
|
+
* Get current fullscreen element
|
|
350
|
+
*/
|
|
351
|
+
declare const getFullscreenElement: () => Element | null;
|
|
352
|
+
/**
|
|
353
|
+
* Request fullscreen on element
|
|
354
|
+
*/
|
|
355
|
+
declare const requestFullscreen: (element: HTMLElement) => Promise<void>;
|
|
356
|
+
/**
|
|
357
|
+
* Exit fullscreen
|
|
358
|
+
*/
|
|
359
|
+
declare const exitFullscreen: () => Promise<void>;
|
|
360
|
+
/**
|
|
361
|
+
* Detect video type from URL
|
|
362
|
+
*/
|
|
363
|
+
declare const detectVideoType: (url: string) => string;
|
|
364
|
+
/**
|
|
365
|
+
* Check if HLS is natively supported
|
|
366
|
+
*/
|
|
367
|
+
declare const isHlsNativelySupported: () => boolean;
|
|
368
|
+
/**
|
|
369
|
+
* Generate unique ID
|
|
370
|
+
*/
|
|
371
|
+
declare const generateId: () => string;
|
|
372
|
+
/**
|
|
373
|
+
* Check if device is mobile
|
|
374
|
+
*/
|
|
375
|
+
declare const isMobile: () => boolean;
|
|
376
|
+
/**
|
|
377
|
+
* Check if device is touch-enabled
|
|
378
|
+
*/
|
|
379
|
+
declare const isTouchDevice: () => boolean;
|
|
380
|
+
/**
|
|
381
|
+
* Parse buffered time ranges
|
|
382
|
+
*/
|
|
383
|
+
declare const getBufferedEnd: (video: HTMLVideoElement) => number;
|
|
384
|
+
/**
|
|
385
|
+
* Check browser support for specific video format
|
|
386
|
+
*/
|
|
387
|
+
declare const canPlayType: (type: string) => boolean;
|
|
388
|
+
|
|
389
|
+
interface VastMediaFile {
|
|
390
|
+
url: string;
|
|
391
|
+
type: string;
|
|
392
|
+
width: number;
|
|
393
|
+
height: number;
|
|
394
|
+
bitrate?: number;
|
|
395
|
+
}
|
|
396
|
+
interface VastAd {
|
|
397
|
+
id: string;
|
|
398
|
+
title: string;
|
|
399
|
+
description?: string;
|
|
400
|
+
duration: number;
|
|
401
|
+
skipOffset?: number;
|
|
402
|
+
clickThrough?: string;
|
|
403
|
+
clickTracking?: string[];
|
|
404
|
+
impressionUrls: string[];
|
|
405
|
+
mediaFiles: VastMediaFile[];
|
|
406
|
+
trackingEvents: Record<string, string[]>;
|
|
407
|
+
}
|
|
408
|
+
interface VastResponse {
|
|
409
|
+
ads: VastAd[];
|
|
410
|
+
error?: string;
|
|
411
|
+
}
|
|
412
|
+
/**
|
|
413
|
+
* Parse VAST XML response
|
|
414
|
+
*/
|
|
415
|
+
declare const parseVastXml: (xmlString: string) => VastResponse;
|
|
416
|
+
/**
|
|
417
|
+
* Fetch and parse VAST ad
|
|
418
|
+
*/
|
|
419
|
+
declare const fetchVastAd: (config: VastConfig) => Promise<VastAd | null>;
|
|
420
|
+
/**
|
|
421
|
+
* Select best media file based on device capabilities
|
|
422
|
+
*/
|
|
423
|
+
declare const selectBestMediaFile: (mediaFiles: VastMediaFile[]) => VastMediaFile | null;
|
|
424
|
+
/**
|
|
425
|
+
* Fire tracking pixel
|
|
426
|
+
*/
|
|
427
|
+
declare const fireTrackingPixel: (url: string) => void;
|
|
428
|
+
/**
|
|
429
|
+
* Fire multiple tracking pixels
|
|
430
|
+
*/
|
|
431
|
+
declare const fireTrackingPixels: (urls: string[]) => void;
|
|
432
|
+
/**
|
|
433
|
+
* Convert VAST ad to player-friendly format
|
|
434
|
+
*/
|
|
435
|
+
declare const convertToAdInfo: (ad: VastAd) => VastAdInfo;
|
|
436
|
+
|
|
437
|
+
export { AdOverlay, CaptionsIcon, CheckIcon, ErrorDisplay, ErrorIcon, ExternalLinkIcon, ForwardIcon, FullscreenExitIcon, FullscreenIcon, Loader, PauseIcon, PipExitIcon, PipIcon, PlayIcon, PlexVideoPlayer, ProgressBar, QualityIcon, RewindIcon, SettingsIcon, SettingsMenu, SkipNextIcon, SkipPrevIcon, SpeedIcon, VolumeControl, VolumeHighIcon, VolumeLowIcon, VolumeMediumIcon, VolumeMuteIcon, canPlayType, clamp, convertToAdInfo, debounce, PlexVideoPlayer as default, detectVideoType, exitFullscreen, fetchVastAd, fireTrackingPixel, fireTrackingPixels, formatTime, generateId, getBufferedEnd, getFullscreenElement, isFullscreenSupported, isHlsNativelySupported, isMobile, isPipSupported, isTouchDevice, parseTime, parseVastXml, percentage, requestFullscreen, selectBestMediaFile, throttle, useKeyboard, usePlayer, useVast };
|
|
438
|
+
export type { HotkeyConfig, PlayerState, PlexVideoPlayerProps, PlexVideoPlayerRef, TextTrack, UsePlayerOptions, UsePlayerReturn, VastAdInfo, VastConfig, VideoSource };
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{jsxs as e,jsx as t,Fragment as r}from"react/jsx-runtime";import{useRef as n,useState as i,useCallback as l,useEffect as a,forwardRef as s,useMemo as c,useImperativeHandle as o}from"react";const u=e=>{if(!isFinite(e)||isNaN(e))return"0:00";const t=Math.floor(e/3600),r=Math.floor(e%3600/60),n=Math.floor(e%60);return t>0?`${t}:${r.toString().padStart(2,"0")}:${n.toString().padStart(2,"0")}`:`${r}:${n.toString().padStart(2,"0")}`},d=e=>{const t=e.split(":").map(Number);return 3===t.length?3600*t[0]+60*t[1]+t[2]:2===t.length?60*t[0]+t[1]:t[0]||0},p=(e,t)=>0===t?0:Math.min(100,Math.max(0,e/t*100)),m=(e,t,r)=>Math.min(r,Math.max(t,e)),h=(e,t)=>{let r=!1;return(...n)=>{r||(e(...n),r=!0,setTimeout(()=>r=!1,t))}},v=(e,t)=>{let r=null;return(...n)=>{r&&clearTimeout(r),r=setTimeout(()=>e(...n),t)}},g=()=>!!(document.fullscreenEnabled||document.webkitFullscreenEnabled||document.mozFullScreenEnabled||document.msFullscreenEnabled),y=()=>"pictureInPictureEnabled"in document&&document.pictureInPictureEnabled,w=()=>document.fullscreenElement||document.webkitFullscreenElement||document.mozFullScreenElement||document.msFullscreenElement||null,f=async e=>{e.requestFullscreen?await e.requestFullscreen():e.webkitRequestFullscreen?await e.webkitRequestFullscreen():e.mozRequestFullScreen?await e.mozRequestFullScreen():e.msRequestFullscreen&&await e.msRequestFullscreen()},b=async()=>{document.exitFullscreen?await document.exitFullscreen():document.webkitExitFullscreen?await document.webkitExitFullscreen():document.mozCancelFullScreen?await document.mozCancelFullScreen():document.msExitFullscreen&&await document.msExitFullscreen()},k=e=>{const t=e.split("?")[0].split(".").pop()?.toLowerCase();return{mp4:"video/mp4",webm:"video/webm",ogg:"video/ogg",ogv:"video/ogg",m3u8:"application/x-mpegURL",mpd:"application/dash+xml",mov:"video/quicktime",avi:"video/x-msvideo",mkv:"video/x-matroska"}[t||""]||"video/mp4"},x=()=>""!==document.createElement("video").canPlayType("application/vnd.apple.mpegurl"),P=()=>`plex-${Math.random().toString(36).substring(2,11)}`,_=()=>/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent),N=()=>"ontouchstart"in window||navigator.maxTouchPoints>0,E=e=>{if(0===e.buffered.length)return 0;const t=e.currentTime;for(let r=0;r<e.buffered.length;r++)if(e.buffered.start(r)<=t&&e.buffered.end(r)>=t)return e.buffered.end(r);return e.buffered.end(e.buffered.length-1)},z=e=>{const t=document.createElement("video").canPlayType(e);return"probably"===t||"maybe"===t},C={isPlaying:!1,isPaused:!0,isEnded:!1,isBuffering:!1,isSeeking:!1,isFullscreen:!1,isPip:!1,isMuted:!1,isAdPlaying:!1,volume:1,currentTime:0,duration:0,buffered:0,playbackRate:1,currentQuality:null,error:null},M=(e={})=>{const{autoPlay:t=!1,muted:r=!1,loop:s=!1,volume:c=1,playbackRate:o=1}=e,u=n(null),d=n(null),[p,m]=i({...C,volume:c,isMuted:r,playbackRate:o}),h=l(e=>{m(t=>({...t,...e}))},[]),v=l(async()=>{const e=u.current;if(e)try{await e.play(),h({isPlaying:!0,isPaused:!1,isEnded:!1})}catch(e){}},[h]),g=l(()=>{const e=u.current;e&&(e.pause(),h({isPlaying:!1,isPaused:!0}))},[h]),k=l(()=>{p.isPlaying?g():v()},[p.isPlaying,v,g]),x=l(e=>{const t=u.current;if(!t)return;const r=Math.max(0,Math.min(e,t.duration||0));t.currentTime=r,h({currentTime:r})},[h]),P=l(e=>{const t=u.current;if(!t)return;const r=Math.max(0,Math.min(1,e));t.volume=r,t.muted=0===r,h({volume:r,isMuted:0===r})},[h]),_=l(()=>{const e=u.current;e&&(e.muted=!e.muted,h({isMuted:e.muted}))},[h]),N=l(e=>{const t=u.current;t&&(t.playbackRate=e,h({playbackRate:e}))},[h]),z=l(async()=>{const e=d.current;if(e)try{await f(e),h({isFullscreen:!0})}catch(e){}},[h]),M=l(async()=>{try{await b(),h({isFullscreen:!1})}catch(e){}},[h]),T=l(async()=>{p.isFullscreen?await M():await z()},[p.isFullscreen,z,M]),A=l(async()=>{const e=u.current;if(e&&y())try{await e.requestPictureInPicture(),h({isPip:!0})}catch(e){}},[h]),S=l(async()=>{if(document.pictureInPictureElement)try{await document.exitPictureInPicture(),h({isPip:!1})}catch(e){}},[h]),F=l(async()=>{p.isPip?await S():await A()},[p.isPip,A,S]);return a(()=>{const e=u.current;if(!e)return;e.muted=r,e.volume=c,e.playbackRate=o,e.loop=s;const n={loadedmetadata:()=>{h({duration:e.duration})},timeupdate:()=>{h({currentTime:e.currentTime,buffered:E(e)})},play:()=>{h({isPlaying:!0,isPaused:!1,isEnded:!1})},pause:()=>{h({isPlaying:!1,isPaused:!0})},ended:()=>{h({isPlaying:!1,isPaused:!0,isEnded:!0})},waiting:()=>{h({isBuffering:!0})},canplay:()=>{h({isBuffering:!1})},seeking:()=>{h({isSeeking:!0})},seeked:()=>{h({isSeeking:!1})},volumechange:()=>{h({volume:e.volume,isMuted:e.muted})},ratechange:()=>{h({playbackRate:e.playbackRate})},error:()=>{h({error:e.error})},enterpictureinpicture:()=>{h({isPip:!0})},leavepictureinpicture:()=>{h({isPip:!1})}};return Object.entries(n).forEach(([t,r])=>{e.addEventListener(t,r)}),t&&v(),()=>{Object.entries(n).forEach(([t,r])=>{e.removeEventListener(t,r)})}},[t,r,s,c,o,v,h]),a(()=>{const e=()=>{const e=!!w();h({isFullscreen:e})};return document.addEventListener("fullscreenchange",e),document.addEventListener("webkitfullscreenchange",e),document.addEventListener("mozfullscreenchange",e),document.addEventListener("MSFullscreenChange",e),()=>{document.removeEventListener("fullscreenchange",e),document.removeEventListener("webkitfullscreenchange",e),document.removeEventListener("mozfullscreenchange",e),document.removeEventListener("MSFullscreenChange",e)}},[h]),{state:p,videoRef:u,containerRef:d,play:v,pause:g,togglePlay:k,seek:x,setVolume:P,toggleMute:_,setPlaybackRate:N,enterFullscreen:z,exitFullscreen:M,toggleFullscreen:T,enterPip:A,exitPip:S,togglePip:F}},T={play:"Space",mute:"m",fullscreen:"f",pip:"p",seekForward:"ArrowRight",seekBackward:"ArrowLeft",volumeUp:"ArrowUp",volumeDown:"ArrowDown"},A=({enabled:e,hotkeys:t={},onPlay:r,onMute:n,onFullscreen:i,onPip:s,onSeek:c,onVolume:o,containerRef:u})=>{const d={...T,...t},p=l(t=>{if(!e)return;const l=t.target;if("INPUT"===l.tagName||"TEXTAREA"===l.tagName||l.isContentEditable)return;const a=u.current;if(!a||!a.contains(document.activeElement))return;switch(t.key){case d.play:case" ":t.preventDefault(),r();break;case d.mute:case"M":t.preventDefault(),n();break;case d.fullscreen:case"F":t.preventDefault(),i();break;case d.pip:case"P":t.preventDefault(),s();break;case d.seekForward:t.preventDefault(),c(t.shiftKey?30:10);break;case d.seekBackward:t.preventDefault(),c(t.shiftKey?-30:-10);break;case d.volumeUp:t.preventDefault(),o(.1);break;case d.volumeDown:t.preventDefault(),o(-.1);break;case"0":case"1":case"2":case"3":case"4":case"5":case"6":case"7":case"8":case"9":t.preventDefault(),c(-1/0)}},[e,d,r,n,i,s,c,o,u]);a(()=>{if(e)return document.addEventListener("keydown",p),()=>{document.removeEventListener("keydown",p)}},[e,p])},S=e=>{const t=(new DOMParser).parseFromString(e,"text/xml");if(t.querySelector("parsererror"))return{ads:[],error:"Failed to parse VAST XML"};const r=[];return t.querySelectorAll("Ad").forEach(e=>{const t=e.querySelector("InLine");if(!t)return;const n=e.getAttribute("id")||"",i=t.querySelector("AdTitle")?.textContent||"",l=t.querySelector("Description")?.textContent||void 0,a=t.querySelectorAll("Creative");let s,c,o=0;const u=[],d=[],p={};a.forEach(e=>{const t=e.querySelector("Linear");if(!t)return;const r=t.querySelector("Duration")?.textContent;r&&(o=F(r));const n=t.getAttribute("skipoffset");n&&(s=F(n));t.querySelectorAll("MediaFile").forEach(e=>{const t=e.textContent?.trim();t&&d.push({url:t,type:e.getAttribute("type")||"video/mp4",width:parseInt(e.getAttribute("width")||"0",10),height:parseInt(e.getAttribute("height")||"0",10),bitrate:parseInt(e.getAttribute("bitrate")||"0",10)||void 0})});const i=t.querySelector("VideoClicks");i&&(c=i.querySelector("ClickThrough")?.textContent?.trim(),i.querySelectorAll("ClickTracking").forEach(e=>{const t=e.textContent?.trim();t&&u.push(t)}));t.querySelectorAll("TrackingEvents Tracking").forEach(e=>{const t=e.getAttribute("event"),r=e.textContent?.trim();t&&r&&(p[t]||(p[t]=[]),p[t].push(r))})});const m=[];t.querySelectorAll("Impression").forEach(e=>{const t=e.textContent?.trim();t&&m.push(t)}),d.length>0&&r.push({id:n,title:i,description:l,duration:o,skipOffset:s,clickThrough:c,clickTracking:u,impressionUrls:m,mediaFiles:d,trackingEvents:p})}),{ads:r}},F=e=>{if(e.includes("%"))return-1;const t=e.split(":");if(3!==t.length)return 0;return 3600*parseInt(t[0],10)+60*parseInt(t[1],10)+parseFloat(t[2])},V=async e=>{try{const t=await fetch(e.url,{method:"GET",headers:{Accept:"application/xml"}});if(!t.ok)return null;const r=await t.text(),n=S(r);if(n.error||0===n.ads.length)return null;const i=n.ads[0];return void 0!==e.skipDelay&&void 0===i.skipOffset&&(i.skipOffset=e.skipDelay),i}catch(e){return null}},L=e=>{if(0===e.length)return null;const t=document.createElement("video"),r=e.filter(e=>""!==t.canPlayType(e.type));if(0===r.length)return null;r.sort((e,t)=>{const r=e.width*e.height,n=t.width*t.height;return r!==n?n-r:(t.bitrate||0)-(e.bitrate||0)});return/Android|iPhone|iPad|iPod/i.test(navigator.userAgent)&&r.length>1?r[r.length-1]:r[0]},R=e=>{(new Image).src=e},q=e=>{e.forEach(R)},B=e=>({id:e.id,title:e.title,duration:e.duration,skipOffset:e.skipOffset,clickThrough:e.clickThrough}),H=({vastConfig:e,videoRef:t,onAdStart:r,onAdEnd:s,onAdSkip:c,onAdError:o})=>{const[u,d]=i(!1),[p,m]=i(null),[h,v]=i(0),[g,y]=i(!1),w=n(null),f=n(""),b=n(0),k=n(new Set),x=n(null),P=Array.isArray(e)?e:e?[e]:[],_=l(async e=>{const n=t.current;if(n&&!u)try{const t=await V(e);if(!t)return void o?.(new Error("Failed to fetch VAST ad"));const i=L(t.mediaFiles);if(!i)return void o?.(new Error("No compatible media file found"));f.current=n.currentSrc,b.current=n.currentTime,q(t.impressionUrls),w.current=t,m(B(t)),d(!0),v(t.duration),y(void 0===t.skipOffset||0===t.skipOffset),n.src=i.url,n.currentTime=0,await n.play(),t.trackingEvents.start&&q(t.trackingEvents.start),r?.(B(t)),x.current=window.setInterval(()=>{const e=Math.max(0,t.duration-n.currentTime);v(e),t.skipOffset&&n.currentTime>=t.skipOffset&&y(!0);const r=n.currentTime/t.duration*100;r>=25&&t.trackingEvents.firstQuartile&&q(t.trackingEvents.firstQuartile),r>=50&&t.trackingEvents.midpoint&&q(t.trackingEvents.midpoint),r>=75&&t.trackingEvents.thirdQuartile&&q(t.trackingEvents.thirdQuartile)},250);const l=()=>{t.trackingEvents.complete&&q(t.trackingEvents.complete),N()};n.addEventListener("ended",l,{once:!0})}catch(e){o?.(e instanceof Error?e:new Error("Ad playback failed")),N()}},[t,u,r,o]),N=l(()=>{const e=t.current;e&&(x.current&&(clearInterval(x.current),x.current=null),f.current&&(e.src=f.current,e.currentTime=b.current,e.play().catch(()=>{})),d(!1),m(null),v(0),y(!1),w.current=null,s?.())},[t,s]),E=l(()=>{if(!g||!w.current)return;const e=w.current;e.trackingEvents.skip&&q(e.trackingEvents.skip),c?.(),N()},[g,N,c]),z=l(()=>{if(!w.current)return;const e=w.current;e.clickThrough&&window.open(e.clickThrough,"_blank"),e.clickTracking&&q(e.clickTracking)},[]),C=l((e,t)=>{u||0===P.length||P.forEach(r=>{const n=r.position||"preroll",i=`${n}-${r.midrollTime||0}`;if(k.current.has(i))return;let l=!1;switch(n){case"preroll":l=0===e;break;case"midroll":r.midrollTime&&e>=r.midrollTime&&(l=!0);break;case"postroll":l=e>=t-.5}l&&(k.current.add(i),_(r))})},[P,u,_]);return a(()=>()=>{x.current&&clearInterval(x.current)},[]),{isAdPlaying:u,currentAd:p,adTimeRemaining:h,canSkip:g,skipAd:E,handleAdClick:z,checkForAd:C}},D=({currentTime:r,duration:s,buffered:c,onSeek:o,thumbnailPreview:d,disabled:h=!1})=>{const v=n(null),[g,y]=i(!1),[w,f]=i(null),[b,k]=i(0),x=p(r,s),P=p(c,s),_=l(e=>{if(!v.current)return 0;const t=v.current.getBoundingClientRect();return m((e-t.left)/t.width,0,1)*s},[s]),N=l(e=>{if(!v.current)return;const t=v.current.getBoundingClientRect(),r="clientX"in e?e.clientX:0,n=m(r-t.left,0,t.width),i=_(r);k(n),f(i),g&&o(i)},[g,_,o]),E=l(e=>{if(h)return;e.preventDefault(),y(!0);const t=_(e.clientX);o(t)},[h,_,o]),z=l(()=>{y(!1)},[]),C=l(e=>{const t=_(e.clientX);f(t)},[_]),M=l(()=>{f(null)},[]);a(()=>{if(g){const e=e=>N(e);return window.addEventListener("mousemove",e),window.addEventListener("mouseup",z),()=>{window.removeEventListener("mousemove",e),window.removeEventListener("mouseup",z)}}},[g,N,z]);const T=l(e=>{if(h)return;e.preventDefault();const t=e.touches[0],r=_(t.clientX);y(!0),o(r)},[h,_,o]),A=l(e=>{if(!g)return;const t=e.touches[0],r=_(t.clientX);o(r)},[g,_,o]),S=l(()=>{y(!1)},[]);return e("div",{ref:v,className:"plex-video-player__progress-container",onMouseDown:E,onMouseEnter:C,onMouseLeave:M,onMouseMove:e=>N(e.nativeEvent),onTouchStart:T,onTouchMove:A,onTouchEnd:S,role:"slider","aria-label":"Video progress","aria-valuemin":0,"aria-valuemax":s,"aria-valuenow":r,"aria-valuetext":u(r),tabIndex:0,children:[e("div",{className:"plex-video-player__progress",children:[t("div",{className:"plex-video-player__progress-buffered",style:{width:`${P}%`}}),t("div",{className:"plex-video-player__progress-played",style:{width:`${x}%`}}),t("div",{className:"plex-video-player__progress-handle",style:{left:`${x}%`}})]}),d?.enabled&&null!==w&&e("div",{className:"plex-video-player__thumbnail-preview",style:{left:`${b}px`,width:d.width||160,height:d.height||90},children:[d.sprites&&t("div",{style:{width:"100%",height:"100%",backgroundImage:`url(${d.sprites})`,backgroundPosition:I(w,s,d.interval||10,d.width||160,d.height||90),backgroundSize:"cover"}}),t("div",{className:"plex-video-player__thumbnail-time",children:u(w)})]}),null!==w&&!d?.enabled&&t("div",{className:"plex-video-player__thumbnail-preview",style:{left:`${b}px`,width:"auto",height:"auto",padding:"4px 8px"},children:t("span",{style:{color:"white",fontSize:"12px"},children:u(w)})})]})},I=(e,t,r,n,i)=>{const l=Math.floor(e/r);return`-${l%10*n}px -${Math.floor(l/10)*i}px`},$=({className:e,size:r=24})=>t("svg",{className:e,width:r,height:r,viewBox:"0 0 24 24",fill:"currentColor",xmlns:"http://www.w3.org/2000/svg",children:t("path",{d:"M8 5v14l11-7z"})}),O=({className:e,size:r=24})=>t("svg",{className:e,width:r,height:r,viewBox:"0 0 24 24",fill:"currentColor",xmlns:"http://www.w3.org/2000/svg",children:t("path",{d:"M6 19h4V5H6v14zm8-14v14h4V5h-4z"})}),Q=({className:e,size:r=24})=>t("svg",{className:e,width:r,height:r,viewBox:"0 0 24 24",fill:"currentColor",xmlns:"http://www.w3.org/2000/svg",children:t("path",{d:"M3 9v6h4l5 5V4L7 9H3zm13.5 3c0-1.77-1.02-3.29-2.5-4.03v8.05c1.48-.73 2.5-2.25 2.5-4.02zM14 3.23v2.06c2.89.86 5 3.54 5 6.71s-2.11 5.85-5 6.71v2.06c4.01-.91 7-4.49 7-8.77s-2.99-7.86-7-8.77z"})}),U=({className:e,size:r=24})=>t("svg",{className:e,width:r,height:r,viewBox:"0 0 24 24",fill:"currentColor",xmlns:"http://www.w3.org/2000/svg",children:t("path",{d:"M18.5 12c0-1.77-1.02-3.29-2.5-4.03v8.05c1.48-.73 2.5-2.25 2.5-4.02zM5 9v6h4l5 5V4L9 9H5z"})}),X=({className:e,size:r=24})=>t("svg",{className:e,width:r,height:r,viewBox:"0 0 24 24",fill:"currentColor",xmlns:"http://www.w3.org/2000/svg",children:t("path",{d:"M7 9v6h4l5 5V4l-5 5H7z"})}),j=({className:e,size:r=24})=>t("svg",{className:e,width:r,height:r,viewBox:"0 0 24 24",fill:"currentColor",xmlns:"http://www.w3.org/2000/svg",children:t("path",{d:"M16.5 12c0-1.77-1.02-3.29-2.5-4.03v2.21l2.45 2.45c.03-.2.05-.41.05-.63zm2.5 0c0 .94-.2 1.82-.54 2.64l1.51 1.51C20.63 14.91 21 13.5 21 12c0-4.28-2.99-7.86-7-8.77v2.06c2.89.86 5 3.54 5 6.71zM4.27 3L3 4.27 7.73 9H3v6h4l5 5v-6.73l4.25 4.25c-.67.52-1.42.93-2.25 1.18v2.06c1.38-.31 2.63-.95 3.69-1.81L19.73 21 21 19.73l-9-9L4.27 3zM12 4L9.91 6.09 12 8.18V4z"})}),K=({className:e,size:r=24})=>t("svg",{className:e,width:r,height:r,viewBox:"0 0 24 24",fill:"currentColor",xmlns:"http://www.w3.org/2000/svg",children:t("path",{d:"M7 14H5v5h5v-2H7v-3zm-2-4h2V7h3V5H5v5zm12 7h-3v2h5v-5h-2v3zM14 5v2h3v3h2V5h-5z"})}),G=({className:e,size:r=24})=>t("svg",{className:e,width:r,height:r,viewBox:"0 0 24 24",fill:"currentColor",xmlns:"http://www.w3.org/2000/svg",children:t("path",{d:"M5 16h3v3h2v-5H5v2zm3-8H5v2h5V5H8v3zm6 11h2v-3h3v-2h-5v5zm2-11V5h-2v5h5V8h-3z"})}),J=({className:e,size:r=24})=>t("svg",{className:e,width:r,height:r,viewBox:"0 0 24 24",fill:"currentColor",xmlns:"http://www.w3.org/2000/svg",children:t("path",{d:"M19 7h-8v6h8V7zm2-4H3c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h18c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 16H3V5h18v14z"})}),W=({className:e,size:r=24})=>t("svg",{className:e,width:r,height:r,viewBox:"0 0 24 24",fill:"currentColor",xmlns:"http://www.w3.org/2000/svg",children:t("path",{d:"M21 3H3c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h18c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 16H3V5h18v14zM5 7h8v6H5z"})}),Y=({className:e,size:r=24})=>t("svg",{className:e,width:r,height:r,viewBox:"0 0 24 24",fill:"currentColor",xmlns:"http://www.w3.org/2000/svg",children:t("path",{d:"M19.14 12.94c.04-.31.06-.63.06-.94 0-.31-.02-.63-.06-.94l2.03-1.58c.18-.14.23-.41.12-.61l-1.92-3.32c-.12-.22-.37-.29-.59-.22l-2.39.96c-.5-.38-1.03-.7-1.62-.94l-.36-2.54c-.04-.24-.24-.41-.48-.41h-3.84c-.24 0-.43.17-.47.41l-.36 2.54c-.59.24-1.13.57-1.62.94l-2.39-.96c-.22-.08-.47 0-.59.22L2.74 8.87c-.12.21-.08.47.12.61l2.03 1.58c-.04.31-.06.63-.06.94s.02.63.06.94l-2.03 1.58c-.18.14-.23.41-.12.61l1.92 3.32c.12.22.37.29.59.22l2.39-.96c.5.38 1.03.7 1.62.94l.36 2.54c.05.24.24.41.48.41h3.84c.24 0 .44-.17.47-.41l.36-2.54c.59-.24 1.13-.56 1.62-.94l2.39.96c.22.08.47 0 .59-.22l1.92-3.32c.12-.22.07-.47-.12-.61l-2.01-1.58zM12 15.6c-1.98 0-3.6-1.62-3.6-3.6s1.62-3.6 3.6-3.6 3.6 1.62 3.6 3.6-1.62 3.6-3.6 3.6z"})}),Z=({className:e,size:r=24})=>t("svg",{className:e,width:r,height:r,viewBox:"0 0 24 24",fill:"currentColor",xmlns:"http://www.w3.org/2000/svg",children:t("path",{d:"M19 4H5c-1.11 0-2 .9-2 2v12c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm-8 7H9.5v-.5h-2v3h2V13H11v1c0 .55-.45 1-1 1H7c-.55 0-1-.45-1-1v-4c0-.55.45-1 1-1h3c.55 0 1 .45 1 1v1zm7 0h-1.5v-.5h-2v3h2V13H18v1c0 .55-.45 1-1 1h-3c-.55 0-1-.45-1-1v-4c0-.55.45-1 1-1h3c.55 0 1 .45 1 1v1z"})}),ee=({className:e,size:r=24})=>t("svg",{className:e,width:r,height:r,viewBox:"0 0 24 24",fill:"currentColor",xmlns:"http://www.w3.org/2000/svg",children:t("path",{d:"M20.38 8.57l-1.23 1.85a8 8 0 0 1-.22 7.58H5.07A8 8 0 0 1 15.58 6.85l1.85-1.23A10 10 0 0 0 3.35 19a2 2 0 0 0 1.72 1h13.85a2 2 0 0 0 1.74-1 10 10 0 0 0-.27-10.44zm-9.79 6.84a2 2 0 0 0 2.83 0l5.66-8.49-8.49 5.66a2 2 0 0 0 0 2.83z"})}),te=({className:e,size:r=24})=>t("svg",{className:e,width:r,height:r,viewBox:"0 0 24 24",fill:"currentColor",xmlns:"http://www.w3.org/2000/svg",children:t("path",{d:"M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 16H5V5h14v14zM7.5 13h2v2H7.5zm4.5 0h2v2h-2zm4.5 0h2v2h-2zM7.5 9h2v2H7.5zm4.5 0h2v2h-2zm4.5 0h2v2h-2z"})}),re=({className:e,size:r=24})=>t("svg",{className:e,width:r,height:r,viewBox:"0 0 24 24",fill:"currentColor",xmlns:"http://www.w3.org/2000/svg",children:t("path",{d:"M4 18l8.5-6L4 6v12zm9-12v12l8.5-6L13 6z"})}),ne=({className:e,size:r=24})=>t("svg",{className:e,width:r,height:r,viewBox:"0 0 24 24",fill:"currentColor",xmlns:"http://www.w3.org/2000/svg",children:t("path",{d:"M11 18V6l-8.5 6 8.5 6zm.5-6l8.5 6V6l-8.5 6z"})}),ie=({className:e,size:r=24})=>t("svg",{className:e,width:r,height:r,viewBox:"0 0 24 24",fill:"currentColor",xmlns:"http://www.w3.org/2000/svg",children:t("path",{d:"M6 18l8.5-6L6 6v12zM16 6v12h2V6h-2z"})}),le=({className:e,size:r=24})=>t("svg",{className:e,width:r,height:r,viewBox:"0 0 24 24",fill:"currentColor",xmlns:"http://www.w3.org/2000/svg",children:t("path",{d:"M6 6h2v12H6zm3.5 6l8.5 6V6z"})}),ae=({className:e,size:r=24})=>t("svg",{className:e,width:r,height:r,viewBox:"0 0 24 24",fill:"currentColor",xmlns:"http://www.w3.org/2000/svg",children:t("path",{d:"M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-2h2v2zm0-4h-2V7h2v6z"})}),se=({className:e,size:r=24})=>t("svg",{className:e,width:r,height:r,viewBox:"0 0 24 24",fill:"currentColor",xmlns:"http://www.w3.org/2000/svg",children:t("path",{d:"M19 19H5V5h7V3H5c-1.11 0-2 .9-2 2v14c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2v-7h-2v7zM14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7z"})}),ce=({className:e,size:r=24})=>t("svg",{className:e,width:r,height:r,viewBox:"0 0 24 24",fill:"currentColor",xmlns:"http://www.w3.org/2000/svg",children:t("path",{d:"M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z"})}),oe=({volume:r,muted:n,onVolumeChange:i,onToggleMute:a})=>{const s=l(e=>{const t=parseFloat(e.target.value);i(t)},[i]);return e("div",{className:"plex-video-player__volume-container",children:[t("button",{className:"plex-video-player__btn",onClick:a,"aria-label":n?"Unmute":"Mute",title:n?"Unmute (M)":"Mute (M)",type:"button",children:t(n||0===r?j:r<.33?X:r<.66?U:Q,{})}),t("div",{className:"plex-video-player__volume-slider-container",children:t("input",{type:"range",className:"plex-video-player__volume-slider",min:0,max:1,step:.01,value:n?0:r,onChange:s,"aria-label":"Volume",style:{background:`linear-gradient(to right, var(--plex-primary) 0%, var(--plex-primary) ${100*(n?0:r)}%, var(--plex-progress-bg) ${100*(n?0:r)}%, var(--plex-progress-bg) 100%)`}})})]})},ue=({playbackRate:s,playbackSpeeds:c,onPlaybackRateChange:o,qualityEnabled:u,sources:d,currentQuality:p,onQualityChange:m,captionsEnabled:h,textTracks:v,currentTrack:g,onTrackChange:y})=>{const[w,f]=i(!1),[b,k]=i("main"),x=n(null);a(()=>{const e=e=>{x.current&&!x.current.contains(e.target)&&(f(!1),k("main"))};if(w)return document.addEventListener("mousedown",e),()=>document.removeEventListener("mousedown",e)},[w]);const P=l(()=>{f(e=>!e),k("main")},[]),_=l(e=>{o(e),k("main")},[o]),N=l(e=>{m?.(e),k("main")},[m]),E=l(e=>{y?.(e),k("main")},[y]);return e("div",{className:"plex-video-player__settings-container",ref:x,children:[t("button",{className:"plex-video-player__btn",onClick:P,"aria-label":"Settings","aria-expanded":w,title:"Settings",type:"button",children:t(Y,{})}),w&&e("div",{className:"plex-video-player__settings-menu "+(w?"plex-video-player__settings-menu--open":""),children:["main"===b&&e(r,{children:[t("div",{className:"plex-video-player__settings-title",children:"Settings"}),e("button",{className:"plex-video-player__settings-item",onClick:()=>k("speed"),type:"button",children:[t("span",{children:"Playback Speed"}),t("span",{children:1===s?"Normal":`${s}x`})]}),u&&d&&d.length>1&&e("button",{className:"plex-video-player__settings-item",onClick:()=>k("quality"),type:"button",children:[t("span",{children:"Quality"}),t("span",{children:p||"Auto"})]}),h&&v&&v.length>0&&e("button",{className:"plex-video-player__settings-item",onClick:()=>k("captions"),type:"button",children:[t("span",{children:"Captions"}),t("span",{children:g||"Off"})]})]}),"speed"===b&&e(r,{children:[t("button",{className:"plex-video-player__settings-title",onClick:()=>k("main"),style:{cursor:"pointer",border:"none",background:"transparent",width:"100%",textAlign:"left"},type:"button",children:"← Playback Speed"}),t("div",{className:"plex-video-player__speed-menu",children:c.map(e=>t("button",{className:"plex-video-player__speed-btn "+(s===e?"plex-video-player__speed-btn--active":""),onClick:()=>_(e),type:"button",children:1===e?"Normal":`${e}x`},e))})]}),"quality"===b&&e(r,{children:[t("button",{className:"plex-video-player__settings-title",onClick:()=>k("main"),style:{cursor:"pointer",border:"none",background:"transparent",width:"100%",textAlign:"left"},type:"button",children:"← Quality"}),t("div",{className:"plex-video-player__quality-menu",children:d?.map(r=>e("button",{className:"plex-video-player__settings-item "+(p===r.quality?"plex-video-player__settings-item--active":""),onClick:()=>N(r.quality||r.src),type:"button",children:[t("span",{children:r.label||r.quality||"Unknown"}),p===r.quality&&t(ce,{size:16})]},r.quality||r.src))})]}),"captions"===b&&e(r,{children:[t("button",{className:"plex-video-player__settings-title",onClick:()=>k("main"),style:{cursor:"pointer",border:"none",background:"transparent",width:"100%",textAlign:"left"},type:"button",children:"← Captions"}),e("div",{className:"plex-video-player__quality-menu",children:[e("button",{className:"plex-video-player__settings-item "+(g?"":"plex-video-player__settings-item--active"),onClick:()=>E(null),type:"button",children:[t("span",{children:"Off"}),!g&&t(ce,{size:16})]}),v?.map(r=>e("button",{className:"plex-video-player__settings-item "+(g===r.srclang?"plex-video-player__settings-item--active":""),onClick:()=>E(r.srclang),type:"button",children:[t("span",{children:r.label}),g===r.srclang&&t(ce,{size:16})]},r.srclang))]})]})]})]})},de=({ad:r,timeRemaining:n,canSkip:i,onSkip:l,onClick:a})=>{const s=i?"Skip Ad":`Skip in ${Math.ceil(n)}s`;return e("div",{className:"plex-video-player__ad-overlay",onClick:a,children:[e("div",{className:"plex-video-player__ad-info",children:[t("span",{className:"plex-video-player__ad-badge",children:"Ad"}),e("span",{children:[u(n)," remaining"]})]}),r.clickThrough&&e("button",{className:"plex-video-player__ad-learn-more",onClick:e=>{e.stopPropagation(),a()},type:"button",children:[t(se,{size:16}),"Learn More"]}),t("button",{className:"plex-video-player__ad-skip",onClick:e=>{e.stopPropagation(),i&&l()},disabled:!i,type:"button",children:s})]})},pe={1:"The video playback was aborted.",2:"A network error occurred while loading the video.",3:"The video format is not supported or cannot be decoded.",4:"The video source is not supported."},me=({error:r,onRetry:n})=>{const i=r?.code||0,l=pe[i]||"An unknown error occurred.";return e("div",{className:"plex-video-player__error",children:[t(ae,{className:"plex-video-player__error-icon",size:60}),t("div",{className:"plex-video-player__error-message",children:l}),i>0&&e("div",{className:"plex-video-player__error-code",children:["Error Code: ",i]}),t("button",{className:"plex-video-player__error-retry",onClick:n,type:"button",children:"Try Again"})]})},he=({visible:e})=>e?t("div",{className:"plex-video-player__loader",children:t("div",{className:"plex-video-player__loader-spinner"})}):null,ve=[.25,.5,.75,1,1.25,1.5,1.75,2],ge=3e3,ye=s((r,s)=>{const{src:d,poster:p,autoPlay:h=!1,muted:v=!1,loop:w=!1,preload:f="metadata",width:b="100%",height:x="auto",controls:P=!0,pip:_=!0,fullscreen:N=!0,playbackSpeed:E=!0,playbackSpeeds:z=ve,volume:C=!0,initialVolume:T=1,progressBar:S=!0,timeDisplay:F=!0,qualitySelector:V=!0,textTracks:L,vast:R,keyboard:q=!0,hotkeys:B,className:I="",style:Q,accentColor:U,theme:X="dark",controlsTimeout:j=ge,doubleClickFullscreen:Y=!0,clickToPlay:Z=!0,thumbnailPreview:ee,onPlay:te,onPause:re,onEnded:ne,onTimeUpdate:ie,onProgress:le,onVolumeChange:ae,onSeeking:se,onSeeked:ce,onRateChange:pe,onQualityChange:ye,onFullscreenChange:we,onPipChange:fe,onError:be,onReady:ke,onAdStart:xe,onAdEnd:Pe,onAdSkip:_e,onAdError:Ne}=r,Ee=n(null),ze=n(null),Ce=n(null),[,Me]=i(!1),[Te,Ae]=i(!!p&&!h),[Se,Fe]=i(!0),[Ve,Le]=i(null),[Re,qe]=i(null),{state:Be,play:He,pause:De,togglePlay:Ie,seek:$e,setVolume:Oe,toggleMute:Qe,setPlaybackRate:Ue,enterFullscreen:Xe,exitFullscreen:je,toggleFullscreen:Ke,enterPip:Ge,exitPip:Je,togglePip:We}=M({autoPlay:h,muted:v,loop:w,volume:T,playbackRate:1}),Ye=H({vastConfig:R,videoRef:Ee,onAdStart:xe,onAdEnd:Pe,onAdSkip:_e,onAdError:Ne}),Ze=c(()=>"string"==typeof d?[{src:d,type:k(d)}]:d.map(e=>({...e,type:e.type||k(e.src)})),[d]),et=c(()=>Ve&&Ze.find(e=>e.quality===Ve)||Ze[0],[Ze,Ve]),tt=l(()=>{Be.isPlaying&&!Ye.isAdPlaying&&Fe(!1)},[Be.isPlaying,Ye.isAdPlaying]),rt=l(()=>{Fe(!0),Ce.current&&clearTimeout(Ce.current),Ce.current=window.setTimeout(tt,j)},[tt,j]),nt=l(()=>{Z&&!Ye.isAdPlaying&&Ie(),rt()},[Z,Ye.isAdPlaying,Ie,rt]),it=l(()=>{Y&&!Ye.isAdPlaying&&Ke()},[Y,Ye.isAdPlaying,Ke]),lt=l(()=>{rt()},[rt]),at=l(()=>{Be.isPlaying&&tt()},[Be.isPlaying,tt]),st=l(e=>{$e(e),se?.(e)},[$e,se]),ct=l(e=>{const t=Ze.find(t=>t.quality===e);if(t&&Ee.current){const r=Ee.current.currentTime,n=!Ee.current.paused;Le(e),Ee.current.src=t.src,Ee.current.currentTime=r,n&&Ee.current.play(),ye?.(e)}},[Ze,ye]),ot=l(e=>{if(qe(e),Ee.current){const t=Ee.current.textTracks;for(let r=0;r<t.length;r++)t[r].mode=t[r].language===e?"showing":"hidden"}},[]),ut=l(()=>{Ee.current&&(Ee.current.load(),He())},[He]),dt=l(()=>{Ae(!1),He()},[He]);A({enabled:q,hotkeys:B,onPlay:Ie,onMute:Qe,onFullscreen:Ke,onPip:We,onSeek:e=>{if(Ee.current){const t=m(Ee.current.currentTime+e,0,Ee.current.duration);$e(t)}},onVolume:e=>{Oe(m(Be.volume+e,0,1))},containerRef:ze}),a(()=>{const e=Ee.current;if(!e)return;const t={loadedmetadata:()=>{Me(!0),ke?.()},play:()=>{Ae(!1),te?.()},pause:()=>re?.(),ended:()=>ne?.(),timeupdate:()=>{ie?.(e.currentTime),Ye.checkForAd(e.currentTime,e.duration)},progress:()=>{e.buffered.length>0&&le?.(e.buffered.end(e.buffered.length-1))},volumechange:()=>{ae?.(e.volume,e.muted)},seeking:()=>se?.(e.currentTime),seeked:()=>ce?.(e.currentTime),ratechange:()=>pe?.(e.playbackRate),error:()=>be?.(e.error),enterpictureinpicture:()=>fe?.(!0),leavepictureinpicture:()=>fe?.(!1)};return Object.entries(t).forEach(([t,r])=>{e.addEventListener(t,r)}),()=>{Object.entries(t).forEach(([t,r])=>{e.removeEventListener(t,r)})}},[te,re,ne,ie,le,ae,se,ce,pe,be,ke,fe,Ye]),a(()=>{const e=()=>{const e=!!document.fullscreenElement;we?.(e)};return document.addEventListener("fullscreenchange",e),()=>{document.removeEventListener("fullscreenchange",e)}},[we]),a(()=>()=>{Ce.current&&clearTimeout(Ce.current)},[]),o(s,()=>({play:async()=>{await He()},pause:()=>{De()},stop:()=>{De(),Ee.current&&(Ee.current.currentTime=0)},seek:e=>{$e(e)},setVolume:e=>{Oe(e)},mute:()=>{Ee.current&&(Ee.current.muted=!0)},unmute:()=>{Ee.current&&(Ee.current.muted=!1)},toggleMute:()=>{Qe()},enterFullscreen:async()=>{await Xe()},exitFullscreen:async()=>{await je()},toggleFullscreen:async()=>{await Ke()},enterPip:async()=>{await Ge()},exitPip:async()=>{await Je()},togglePip:async()=>{await We()},setPlaybackRate:e=>{Ue(e)},setQuality:e=>{ct(e)},getCurrentTime:()=>Ee.current?.currentTime||0,getDuration:()=>Ee.current?.duration||0,getVolume:()=>Ee.current?.volume||0,isMuted:()=>Ee.current?.muted||!1,isPlaying:()=>!Ee.current?.paused,isFullscreen:()=>Be.isFullscreen,isPip:()=>Be.isPip,getVideoElement:()=>Ee.current}));const pt=c(()=>{const e={width:b,height:x,...Q};return U&&(e["--plex-primary"]=U),e},[b,x,Q,U]),mt=["plex-video-player",`plex-video-player--theme-${X}`,Be.isFullscreen&&"plex-video-player--fullscreen",Se&&"plex-video-player--controls-visible",Ye.isAdPlaying&&"plex-video-player--ad-playing",I].filter(Boolean).join(" ");return e("div",{ref:ze,className:mt,style:pt,onMouseMove:lt,onMouseLeave:at,tabIndex:0,children:[e("video",{ref:Ee,className:"plex-video-player__video",src:et?.src,poster:Te?void 0:p,preload:f,loop:w,muted:v,playsInline:!0,onClick:nt,onDoubleClick:it,children:[Ze.map((e,r)=>t("source",{src:e.src,type:e.type},r)),L?.map((e,r)=>t("track",{src:e.src,kind:e.kind,srcLang:e.srclang,label:e.label,default:e.default},r))]}),Te&&p&&t("div",{className:"plex-video-player__poster",style:{backgroundImage:`url(${p})`},onClick:dt}),t(he,{visible:Be.isBuffering&&!Te}),Be.error&&t(me,{error:Be.error,onRetry:ut}),Ye.isAdPlaying&&Ye.currentAd&&t(de,{ad:Ye.currentAd,timeRemaining:Ye.adTimeRemaining,canSkip:Ye.canSkip,onSkip:Ye.skipAd,onClick:Ye.handleAdClick}),P&&!Te&&!Be.error&&e("div",{className:"plex-video-player__controls",children:[S&&t(D,{currentTime:Be.currentTime,duration:Be.duration,buffered:Be.buffered,onSeek:st,thumbnailPreview:ee,disabled:Ye.isAdPlaying}),e("div",{className:"plex-video-player__controls-row",children:[e("div",{className:"plex-video-player__controls-left",children:[t("button",{className:"plex-video-player__btn plex-video-player__btn--play",onClick:Ie,"aria-label":Be.isPlaying?"Pause":"Play",title:Be.isPlaying?"Pause (Space)":"Play (Space)",disabled:Ye.isAdPlaying,type:"button",children:Be.isPlaying?t(O,{}):t($,{})}),C&&t(oe,{volume:Be.volume,muted:Be.isMuted,onVolumeChange:Oe,onToggleMute:Qe}),F&&e("div",{className:"plex-video-player__time",children:[t("span",{children:u(Be.currentTime)}),t("span",{className:"plex-video-player__time-separator",children:"/"}),t("span",{children:u(Be.duration)})]})]}),e("div",{className:"plex-video-player__controls-right",children:[(E||V)&&t(ue,{playbackRate:Be.playbackRate,playbackSpeeds:z,onPlaybackRateChange:Ue,qualityEnabled:V,sources:Ze.length>1?Ze:void 0,currentQuality:Ve||void 0,onQualityChange:ct,captionsEnabled:!!L&&L.length>0,textTracks:L,currentTrack:Re||void 0,onTrackChange:ot}),_&&y()&&t("button",{className:"plex-video-player__btn",onClick:We,"aria-label":Be.isPip?"Exit Picture-in-Picture":"Picture-in-Picture",title:Be.isPip?"Exit PiP (P)":"Picture-in-Picture (P)",disabled:Ye.isAdPlaying,type:"button",children:Be.isPip?t(W,{}):t(J,{})}),N&&g()&&t("button",{className:"plex-video-player__btn",onClick:Ke,"aria-label":Be.isFullscreen?"Exit Fullscreen":"Fullscreen",title:Be.isFullscreen?"Exit Fullscreen (F)":"Fullscreen (F)",type:"button",children:Be.isFullscreen?t(G,{}):t(K,{})})]})]})]})]})});ye.displayName="PlexVideoPlayer";export{de as AdOverlay,Z as CaptionsIcon,ce as CheckIcon,me as ErrorDisplay,ae as ErrorIcon,se as ExternalLinkIcon,re as ForwardIcon,G as FullscreenExitIcon,K as FullscreenIcon,he as Loader,O as PauseIcon,W as PipExitIcon,J as PipIcon,$ as PlayIcon,ye as PlexVideoPlayer,D as ProgressBar,te as QualityIcon,ne as RewindIcon,Y as SettingsIcon,ue as SettingsMenu,ie as SkipNextIcon,le as SkipPrevIcon,ee as SpeedIcon,oe as VolumeControl,Q as VolumeHighIcon,X as VolumeLowIcon,U as VolumeMediumIcon,j as VolumeMuteIcon,z as canPlayType,m as clamp,B as convertToAdInfo,v as debounce,ye as default,k as detectVideoType,b as exitFullscreen,V as fetchVastAd,R as fireTrackingPixel,q as fireTrackingPixels,u as formatTime,P as generateId,E as getBufferedEnd,w as getFullscreenElement,g as isFullscreenSupported,x as isHlsNativelySupported,_ as isMobile,y as isPipSupported,N as isTouchDevice,d as parseTime,S as parseVastXml,p as percentage,f as requestFullscreen,L as selectBestMediaFile,h as throttle,A as useKeyboard,M as usePlayer,H as useVast};
|
|
2
|
+
//# sourceMappingURL=index.esm.js.map
|