@therealtinhtute/baroiplayer 1.1.0 → 1.2.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/README.md +1 -2
- package/dist/context/media.js +1 -2
- package/dist/context/media.mjs +3 -15
- package/dist/context/store.js +1 -2
- package/dist/context/store.mjs +3 -5
- package/dist/context-Bzd5QCKM.cjs +7 -0
- package/dist/context-Bzd5QCKM.cjs.map +1 -0
- package/dist/context-DD_SH4tT.js +1053 -0
- package/dist/context-DD_SH4tT.js.map +1 -0
- package/dist/core-events-BRnLMB7s.cjs +2 -0
- package/dist/core-events-BRnLMB7s.cjs.map +1 -0
- package/dist/core-events-v_mKdj5p.js +417 -0
- package/dist/core-events-v_mKdj5p.js.map +1 -0
- package/dist/core-plugins-CMBv-yVh.cjs +2 -0
- package/dist/core-plugins-CMBv-yVh.cjs.map +1 -0
- package/dist/core-plugins-CqCxLkbd.js +754 -0
- package/dist/core-plugins-CqCxLkbd.js.map +1 -0
- package/dist/core.js +7 -29
- package/dist/core.js.map +1 -1
- package/dist/core.mjs +947 -1220
- package/dist/core.mjs.map +1 -1
- package/dist/enhanced-components-BwjBqSA1.js +2858 -0
- package/dist/enhanced-components-BwjBqSA1.js.map +1 -0
- package/dist/enhanced-components-C-ka-XLp.cjs +2 -0
- package/dist/enhanced-components-C-ka-XLp.cjs.map +1 -0
- package/dist/hooks-B-IIWPJr.js +1842 -0
- package/dist/hooks-B-IIWPJr.js.map +1 -0
- package/dist/hooks-DcHzmBwA.cjs +4 -0
- package/dist/hooks-DcHzmBwA.cjs.map +1 -0
- package/dist/index.js +27 -97
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +5613 -6210
- package/dist/index.mjs.map +1 -1
- package/dist/minimal-components-BYsMQde5.js +1610 -0
- package/dist/minimal-components-BYsMQde5.js.map +1 -0
- package/dist/minimal-components-CsxQbDp7.cjs +2 -0
- package/dist/minimal-components-CsxQbDp7.cjs.map +1 -0
- package/dist/performance-monitor-DCSijLJg.js +312 -0
- package/dist/performance-monitor-DCSijLJg.js.map +1 -0
- package/dist/performance-monitor-wNFKmqMr.cjs +2 -0
- package/dist/performance-monitor-wNFKmqMr.cjs.map +1 -0
- package/dist/rolldown-runtime-BmowowmI.js +14 -0
- package/dist/rolldown-runtime-mLOUI7ql.cjs +1 -0
- package/dist/utils-D2gQ7c2R.js +64 -0
- package/dist/utils-D2gQ7c2R.js.map +1 -0
- package/dist/utils-DJaAwiZ9.cjs +2 -0
- package/dist/utils-DJaAwiZ9.cjs.map +1 -0
- package/dist/utils.js +1 -2
- package/dist/utils.mjs +3 -11
- package/package.json +28 -22
- package/src/components/AnimateLoading.tsx +14 -10
- package/src/components/accessibility/keyboard-help-overlay.tsx +2 -0
- package/src/components/enhanced/debug-panel.tsx +1 -1
- package/src/components/enhanced/enhanced-media-player.tsx +68 -25
- package/src/components/examples/loading-states-demo.tsx +1 -2
- package/src/components/examples/react-19-demo.tsx +6 -10
- package/src/components/examples/volume-orientation-demo.tsx +1 -2
- package/src/components/legacy.tsx +6 -6
- package/src/components/media-player-video.tsx +5 -12
- package/src/components/media-player.tsx +2 -2
- package/src/components/ui/blur-poster.tsx +18 -15
- package/src/components/ui/enhanced-seek-bar.tsx +2 -2
- package/src/components/ui/media-resource-preloader.tsx +21 -23
- package/src/components/unified/base-media-player.tsx +22 -10
- package/src/components/variants/minimal/AccessibleMinimalPlayer.tsx +58 -38
- package/src/components/variants/minimal/EnhancedMinimalMediaPlayer.tsx +6 -6
- package/src/components/variants/minimal/components/VideoElement.tsx +3 -1
- package/src/core/optimization/bundle-optimizer.tsx +26 -27
- package/src/core/performance/media-loading-optimizer.ts +2 -1
- package/src/core/plugins/__tests__/registry.test.ts +1 -1
- package/src/core/plugins/enhanced-plugin-system.ts +26 -8
- package/src/core/plugins/registry.ts +319 -92
- package/src/core/plugins/types.ts +21 -3
- package/src/core/providers/enhanced-hls-provider.ts +11 -40
- package/src/core/providers/lazy-provider.tsx +5 -42
- package/src/features/media-session/media-session-manager.ts +4 -26
- package/src/features/picture-in-picture/pip-manager.ts +2 -2
- package/src/features/playlist/playlist-manager.ts +26 -18
- package/src/features/subtitles/subtitle-manager.ts +1 -1
- package/src/features/subtitles/subtitle-parser.ts +19 -16
- package/src/features/thumbnails/thumbnail-generator.ts +12 -5
- package/src/hooks/__tests__/currentTime-fix.test.ts +149 -0
- package/src/hooks/accessibility/use-accessibility-preferences.ts +1 -1
- package/src/hooks/accessibility/use-focus-management.ts +2 -2
- package/src/hooks/accessibility/use-keyboard-shortcuts.ts +2 -2
- package/src/hooks/accessibility/use-screen-reader.ts +1 -1
- package/src/hooks/core/index.ts +1 -1
- package/src/hooks/use-buffer-health.ts +1 -1
- package/src/hooks/use-enhanced-media-player.ts +107 -9
- package/src/hooks/use-focus-trap.tsx +2 -2
- package/src/hooks/use-intelligent-preloading.ts +23 -3
- package/src/hooks/use-keyboard-shortcuts.ts +2 -2
- package/src/hooks/use-media-player.ts +74 -43
- package/src/hooks/use-memory-optimization.ts +15 -15
- package/src/hooks/use-smart-preload.ts +1 -2
- package/src/hooks/useKeyboardShortcuts.ts +34 -2
- package/src/hooks/useScreenshot.ts +60 -44
- package/src/hooks/useVideoPlayer.ts +50 -4
- package/src/index-legacy.ts +3 -5
- package/src/test/accessibility-utils.ts +7 -7
- package/src/test/media-mocks.ts +41 -28
- package/src/test/setup.ts +22 -4
- package/src/types/assets.d.ts +29 -0
- package/src/types/index.ts +13 -0
- package/dist/context/media.js.map +0 -1
- package/dist/context/media.mjs.map +0 -1
- package/dist/context/store.js.map +0 -1
- package/dist/context/store.mjs.map +0 -1
- package/dist/context-BYwDoSRX.js +0 -1172
- package/dist/context-BYwDoSRX.js.map +0 -1
- package/dist/context-eNS62_ac.cjs +0 -23
- package/dist/context-eNS62_ac.cjs.map +0 -1
- package/dist/core-events-B1iM2F46.cjs +0 -2
- package/dist/core-events-B1iM2F46.cjs.map +0 -1
- package/dist/core-events-DunPHKRE.js +0 -484
- package/dist/core-events-DunPHKRE.js.map +0 -1
- package/dist/core-plugins-CROGvQUy.js +0 -686
- package/dist/core-plugins-CROGvQUy.js.map +0 -1
- package/dist/core-plugins-DtsHBOtF.cjs +0 -2
- package/dist/core-plugins-DtsHBOtF.cjs.map +0 -1
- package/dist/enhanced-components-DUw2KrmQ.js +0 -1142
- package/dist/enhanced-components-DUw2KrmQ.js.map +0 -1
- package/dist/enhanced-components-DXBCHU0v.cjs +0 -2
- package/dist/enhanced-components-DXBCHU0v.cjs.map +0 -1
- package/dist/hooks-BW-JjVgP.cjs +0 -4
- package/dist/hooks-BW-JjVgP.cjs.map +0 -1
- package/dist/hooks-CyX6De5M.js +0 -1693
- package/dist/hooks-CyX6De5M.js.map +0 -1
- package/dist/minimal-components-Bdcpxav5.cjs +0 -2
- package/dist/minimal-components-Bdcpxav5.cjs.map +0 -1
- package/dist/minimal-components-DlRK7Qu_.js +0 -1927
- package/dist/minimal-components-DlRK7Qu_.js.map +0 -1
- package/dist/performance-monitor-C5k6FDXw.js +0 -301
- package/dist/performance-monitor-C5k6FDXw.js.map +0 -1
- package/dist/performance-monitor-lJmuUjQQ.cjs +0 -2
- package/dist/performance-monitor-lJmuUjQQ.cjs.map +0 -1
- package/dist/utils-CFOue_9K.cjs +0 -9
- package/dist/utils-CFOue_9K.cjs.map +0 -1
- package/dist/utils-Dzpl2ArK.js +0 -2844
- package/dist/utils-Dzpl2ArK.js.map +0 -1
- package/dist/utils.js.map +0 -1
- package/dist/utils.mjs.map +0 -1
package/package.json
CHANGED
|
@@ -1,18 +1,23 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@therealtinhtute/baroiplayer",
|
|
3
|
-
"version": "1.1
|
|
3
|
+
"version": "1.2.1",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "A customizable media player component library for React",
|
|
7
7
|
"sideEffects": false,
|
|
8
8
|
"exports": {
|
|
9
9
|
".": {
|
|
10
|
-
"
|
|
10
|
+
"development": {
|
|
11
|
+
"types": "./src/index.ts",
|
|
12
|
+
"default": "./src/index.ts"
|
|
13
|
+
},
|
|
14
|
+
"types": "./src/index.ts",
|
|
11
15
|
"import": "./dist/index.mjs",
|
|
12
|
-
"require": "./dist/index.js"
|
|
16
|
+
"require": "./dist/index.js",
|
|
17
|
+
"default": "./dist/index.mjs"
|
|
13
18
|
},
|
|
14
|
-
"./globals.css": "./
|
|
15
|
-
"./minimal-player.css": "./
|
|
19
|
+
"./globals.css": "./src/styles/globals.css",
|
|
20
|
+
"./minimal-player.css": "./src/styles/minimal-player.css",
|
|
16
21
|
"./dist/*": "./dist/*",
|
|
17
22
|
"./lib/*": "./src/lib/*.ts",
|
|
18
23
|
"./components/*": "./src/components/*.tsx",
|
|
@@ -21,7 +26,7 @@
|
|
|
21
26
|
},
|
|
22
27
|
"main": "./dist/index.js",
|
|
23
28
|
"module": "./dist/index.mjs",
|
|
24
|
-
"types": "./
|
|
29
|
+
"types": "./src/index.ts",
|
|
25
30
|
"files": ["dist", "src", "README.md", "LICENSE"],
|
|
26
31
|
"scripts": {
|
|
27
32
|
"build": "vite build",
|
|
@@ -45,32 +50,33 @@
|
|
|
45
50
|
"@radix-ui/react-tooltip": "^1.2.7",
|
|
46
51
|
"clsx": "^2.1.1",
|
|
47
52
|
"react-player": "^3.3.1",
|
|
48
|
-
"tailwind-merge": "^3.
|
|
53
|
+
"tailwind-merge": "^3.6.0",
|
|
49
54
|
"web-vitals": "^4.2.4"
|
|
50
55
|
},
|
|
51
56
|
"devDependencies": {
|
|
52
57
|
"@rp/typescript-config": "workspace:*",
|
|
53
|
-
"@tailwindcss/postcss": "
|
|
54
|
-
"@testing-library/jest-dom": "^6.
|
|
55
|
-
"@testing-library/react": "^16.
|
|
58
|
+
"@tailwindcss/postcss": "4.3.2",
|
|
59
|
+
"@testing-library/jest-dom": "^6.9.1",
|
|
60
|
+
"@testing-library/react": "^16.3.2",
|
|
56
61
|
"@testing-library/user-event": "^14.5.2",
|
|
57
62
|
"@types/node": "^20",
|
|
58
|
-
"@types/react": "^19.
|
|
59
|
-
"@types/react-dom": "^19.
|
|
60
|
-
"@vitejs/plugin-react": "^
|
|
61
|
-
"@vitest/coverage-v8": "^
|
|
62
|
-
"babel-plugin-react-compiler": "^
|
|
63
|
-
"jsdom": "^
|
|
63
|
+
"@types/react": "^19.2.17",
|
|
64
|
+
"@types/react-dom": "^19.2.3",
|
|
65
|
+
"@vitejs/plugin-react": "^6.0.3",
|
|
66
|
+
"@vitest/coverage-v8": "^4.1.10",
|
|
67
|
+
"babel-plugin-react-compiler": "^1.0.0",
|
|
68
|
+
"jsdom": "^29.1.1",
|
|
64
69
|
"lightningcss": "^1.30.1",
|
|
65
70
|
"rollup-plugin-analyzer": "^4.0.0",
|
|
66
71
|
"rollup-plugin-bundle-analyzer": "^1.1.0",
|
|
67
|
-
"size-limit": "^
|
|
68
|
-
"
|
|
72
|
+
"@size-limit/file": "^12.1.0",
|
|
73
|
+
"size-limit": "^12.1.0",
|
|
74
|
+
"tailwindcss": "4.3.2",
|
|
69
75
|
"ts-morph": "^27.0.0",
|
|
70
|
-
"typescript": "^5",
|
|
71
|
-
"vite": "^
|
|
76
|
+
"typescript": "^5.9.3",
|
|
77
|
+
"vite": "^8.1.4",
|
|
72
78
|
"vite-bundle-analyzer": "^0.13.1",
|
|
73
|
-
"vitest": "^
|
|
79
|
+
"vitest": "^4.1.10"
|
|
74
80
|
},
|
|
75
81
|
"peerDependencies": {
|
|
76
82
|
"react": ">=18.0.0",
|
|
@@ -105,6 +111,6 @@
|
|
|
105
111
|
"url": "https://github.com/ryanphanrp/baroiplayer/issues"
|
|
106
112
|
},
|
|
107
113
|
"engines": {
|
|
108
|
-
"node": ">=
|
|
114
|
+
"node": ">=20.19.0"
|
|
109
115
|
}
|
|
110
116
|
}
|
|
@@ -3,20 +3,24 @@
|
|
|
3
3
|
import React, { useState, useEffect } from "react"
|
|
4
4
|
import { cn } from "../lib/utils"
|
|
5
5
|
|
|
6
|
-
//
|
|
7
|
-
let monaLoadingGif: string | undefined
|
|
8
|
-
|
|
9
|
-
// Import the GIF statically for bundling - this will be handled by Vite
|
|
10
|
-
// @ts-ignore - Vite handles this import
|
|
6
|
+
// Import the GIF statically for bundling (Vite / webpack may yield string or { src })
|
|
11
7
|
import monaGifSrc from "../assets/mona-loading.gif"
|
|
12
8
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
9
|
+
function resolveAssetUrl(src: unknown): string | undefined {
|
|
10
|
+
if (typeof src === "string" && src.length > 0) return src
|
|
11
|
+
if (
|
|
12
|
+
src &&
|
|
13
|
+
typeof src === "object" &&
|
|
14
|
+
"src" in src &&
|
|
15
|
+
typeof (src as { src: unknown }).src === "string"
|
|
16
|
+
) {
|
|
17
|
+
return (src as { src: string }).src
|
|
18
|
+
}
|
|
19
|
+
return undefined
|
|
18
20
|
}
|
|
19
21
|
|
|
22
|
+
const monaLoadingGif = resolveAssetUrl(monaGifSrc)
|
|
23
|
+
|
|
20
24
|
interface AnimateLoadingProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
21
25
|
loadingText?: string
|
|
22
26
|
gifSrc?: string
|
|
@@ -55,6 +55,8 @@ export function KeyboardHelpOverlay({
|
|
|
55
55
|
const firstElement = focusableElements[0]
|
|
56
56
|
const lastElement = focusableElements[focusableElements.length - 1]
|
|
57
57
|
|
|
58
|
+
if (!firstElement || !lastElement) return
|
|
59
|
+
|
|
58
60
|
// Focus the close button initially
|
|
59
61
|
const closeButton = overlay.querySelector("[data-close-button]") as HTMLElement
|
|
60
62
|
closeButton?.focus()
|
|
@@ -275,7 +275,7 @@ export const DebugPanel: React.FC<DebugPanelProps> = ({
|
|
|
275
275
|
</div>
|
|
276
276
|
</div>
|
|
277
277
|
{pluginInfo.loadedPlugins &&
|
|
278
|
-
pluginInfo.loadedPlugins.length > pluginInfo.activePlugins?.length && (
|
|
278
|
+
pluginInfo.loadedPlugins.length > (pluginInfo.activePlugins?.length || 0) && (
|
|
279
279
|
<div className="mt-2">
|
|
280
280
|
<span className="text-yellow-400">Loaded (inactive):</span>
|
|
281
281
|
<div className="ml-2 space-y-1 font-mono text-xs">
|
|
@@ -21,10 +21,25 @@ import {
|
|
|
21
21
|
} from "../../core/providers/enhanced-hls-provider"
|
|
22
22
|
import { useIntelligentPreloading } from "../../hooks/use-intelligent-preloading"
|
|
23
23
|
import { cn } from "../../lib/utils"
|
|
24
|
-
import { MediaPlayerProps,
|
|
24
|
+
import { MediaPlayerProps, type MediaMimeType } from "../../types"
|
|
25
25
|
import { MediaResourcePreloader } from "../ui/media-resource-preloader"
|
|
26
26
|
|
|
27
|
-
|
|
27
|
+
// Extend MediaMimeType to include HLS and define extended MediaSource
|
|
28
|
+
type ExtendedMediaMimeType = MediaMimeType | "hls"
|
|
29
|
+
|
|
30
|
+
interface ExtendedMediaSource {
|
|
31
|
+
src: string
|
|
32
|
+
type?: ExtendedMediaMimeType
|
|
33
|
+
media?: string
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
interface EnhancedMediaPlayerProps extends Omit<MediaPlayerProps, 'src' | 'onTimeUpdate' | 'onError'> {
|
|
37
|
+
src?: string
|
|
38
|
+
sources?: ExtendedMediaSource[]
|
|
39
|
+
controls?: boolean
|
|
40
|
+
onCanPlay?: () => void
|
|
41
|
+
onTimeUpdate?: () => void
|
|
42
|
+
onError?: (error: MediaError) => void
|
|
28
43
|
/** Enable enhanced HLS optimization */
|
|
29
44
|
enableEnhancedHLS?: boolean
|
|
30
45
|
/** Enable intelligent preloading */
|
|
@@ -84,7 +99,18 @@ export const EnhancedMediaPlayer: React.FC<EnhancedMediaPlayerProps> = ({
|
|
|
84
99
|
onTimeUpdate,
|
|
85
100
|
onLoadStart,
|
|
86
101
|
onCanPlay,
|
|
87
|
-
|
|
102
|
+
onDurationChange,
|
|
103
|
+
onVolumeChange,
|
|
104
|
+
onLoadedData,
|
|
105
|
+
volume,
|
|
106
|
+
preload,
|
|
107
|
+
crossOrigin,
|
|
108
|
+
playbackRates,
|
|
109
|
+
keyboardControls,
|
|
110
|
+
showControls,
|
|
111
|
+
customControls,
|
|
112
|
+
children,
|
|
113
|
+
...restProps
|
|
88
114
|
}) => {
|
|
89
115
|
const videoRef = useRef<HTMLVideoElement>(null)
|
|
90
116
|
const containerRef = useRef<HTMLDivElement>(null)
|
|
@@ -125,20 +151,20 @@ export const EnhancedMediaPlayer: React.FC<EnhancedMediaPlayerProps> = ({
|
|
|
125
151
|
const deferredVolume = useDeferredValue(mediaState.volume)
|
|
126
152
|
|
|
127
153
|
// Prepare media sources
|
|
128
|
-
const mediaSources = useMemo(():
|
|
154
|
+
const mediaSources = useMemo((): ExtendedMediaSource[] => {
|
|
129
155
|
if (sources) {
|
|
130
156
|
return sources
|
|
131
157
|
}
|
|
132
158
|
if (src) {
|
|
133
159
|
const extension = src.split(".").pop()?.toLowerCase()
|
|
134
160
|
const type = getMediaTypeFromExtension(extension)
|
|
135
|
-
return [{ src, type }]
|
|
161
|
+
return [{ src, type: type as ExtendedMediaMimeType }]
|
|
136
162
|
}
|
|
137
163
|
return []
|
|
138
164
|
}, [src, sources])
|
|
139
165
|
|
|
140
166
|
// Intelligent preloading
|
|
141
|
-
const preloadingControls = useIntelligentPreloading(mediaSources, {
|
|
167
|
+
const preloadingControls = useIntelligentPreloading(mediaSources as any, {
|
|
142
168
|
trackUserBehavior: enableIntelligentPreloading,
|
|
143
169
|
adaptToNetwork: true,
|
|
144
170
|
strategy: preloadingStrategy,
|
|
@@ -147,18 +173,23 @@ export const EnhancedMediaPlayer: React.FC<EnhancedMediaPlayerProps> = ({
|
|
|
147
173
|
|
|
148
174
|
// Initialize HLS provider
|
|
149
175
|
const initializeHLS = useCallback(async () => {
|
|
150
|
-
if (!videoRef.current || !enableEnhancedHLS) return
|
|
176
|
+
if (!videoRef.current || !enableEnhancedHLS || !mediaSources[0]?.src) return
|
|
151
177
|
|
|
152
|
-
const
|
|
153
|
-
const provider = new EnhancedHLSProvider(
|
|
178
|
+
const mergedHlsConfig = { ...ENHANCED_HLS_DEFAULTS, ...hlsConfig }
|
|
179
|
+
const provider = new EnhancedHLSProvider(mergedHlsConfig)
|
|
154
180
|
hlsProviderRef.current = provider
|
|
155
181
|
|
|
156
182
|
try {
|
|
157
183
|
const hls = provider.initialize(videoRef.current, mediaSources[0].src, {
|
|
158
|
-
|
|
159
|
-
|
|
184
|
+
autoPlay,
|
|
185
|
+
muted,
|
|
186
|
+
loop,
|
|
187
|
+
preload,
|
|
188
|
+
crossOrigin
|
|
160
189
|
})
|
|
161
190
|
|
|
191
|
+
// Import Hls dynamically to access Events
|
|
192
|
+
const Hls = (await import('hls.js')).default
|
|
162
193
|
// Setup HLS event listeners
|
|
163
194
|
hls.on(Hls.Events.LEVEL_SWITCHED, handleQualityChange)
|
|
164
195
|
hls.on(Hls.Events.FRAG_BUFFERED, handleFragmentBuffered)
|
|
@@ -167,7 +198,7 @@ export const EnhancedMediaPlayer: React.FC<EnhancedMediaPlayerProps> = ({
|
|
|
167
198
|
console.error("HLS initialization failed:", error)
|
|
168
199
|
fallbackToNativePlayback()
|
|
169
200
|
}
|
|
170
|
-
}, [enableEnhancedHLS, hlsConfig, mediaSources])
|
|
201
|
+
}, [enableEnhancedHLS, hlsConfig, mediaSources, autoPlay, muted, loop, preload, crossOrigin])
|
|
171
202
|
|
|
172
203
|
// Fallback to native playback
|
|
173
204
|
const fallbackToNativePlayback = useCallback(() => {
|
|
@@ -175,7 +206,7 @@ export const EnhancedMediaPlayer: React.FC<EnhancedMediaPlayerProps> = ({
|
|
|
175
206
|
|
|
176
207
|
const source = document.createElement("source")
|
|
177
208
|
source.src = mediaSources[0].src
|
|
178
|
-
source.type = getMediaTypeFromUrl(mediaSources[0].src)
|
|
209
|
+
source.type = getMediaTypeFromUrl(mediaSources[0].src) || "video/mp4"
|
|
179
210
|
|
|
180
211
|
videoRef.current.appendChild(source)
|
|
181
212
|
videoRef.current.load()
|
|
@@ -265,11 +296,13 @@ export const EnhancedMediaPlayer: React.FC<EnhancedMediaPlayerProps> = ({
|
|
|
265
296
|
}, [])
|
|
266
297
|
|
|
267
298
|
const handleMediaError = useCallback(
|
|
268
|
-
(error: MediaError) => {
|
|
299
|
+
(error: MediaError | Error) => {
|
|
269
300
|
startTransition(() => {
|
|
270
|
-
setMediaState((prev) => ({ ...prev, error }))
|
|
301
|
+
setMediaState((prev) => ({ ...prev, error: error as MediaError }))
|
|
271
302
|
})
|
|
272
|
-
|
|
303
|
+
// Convert to string for the callback
|
|
304
|
+
const errorMessage = error instanceof Error ? error.message : `Media error code: ${error.code}`
|
|
305
|
+
onError?.(error as MediaError)
|
|
273
306
|
},
|
|
274
307
|
[onError]
|
|
275
308
|
)
|
|
@@ -278,7 +311,9 @@ export const EnhancedMediaPlayer: React.FC<EnhancedMediaPlayerProps> = ({
|
|
|
278
311
|
(event: any, data: any) => {
|
|
279
312
|
if (data.fatal) {
|
|
280
313
|
console.error("Fatal HLS error:", data)
|
|
281
|
-
|
|
314
|
+
const mediaError = new Error(`HLS Error: ${data.details}`) as any
|
|
315
|
+
mediaError.code = 4 // MEDIA_ERR_SRC_NOT_SUPPORTED
|
|
316
|
+
handleMediaError(mediaError)
|
|
282
317
|
}
|
|
283
318
|
},
|
|
284
319
|
[handleMediaError]
|
|
@@ -348,11 +383,15 @@ export const EnhancedMediaPlayer: React.FC<EnhancedMediaPlayerProps> = ({
|
|
|
348
383
|
video.addEventListener("timeupdate", handleTimeUpdate)
|
|
349
384
|
video.addEventListener("loadedmetadata", handleLoadedMetadata)
|
|
350
385
|
video.addEventListener("volumechange", handleVolumeChange)
|
|
351
|
-
|
|
352
|
-
|
|
386
|
+
if (onLoadStart) {
|
|
387
|
+
video.addEventListener("loadstart", onLoadStart)
|
|
388
|
+
}
|
|
389
|
+
if (onCanPlay) {
|
|
390
|
+
video.addEventListener("canplay", onCanPlay)
|
|
391
|
+
}
|
|
353
392
|
|
|
354
393
|
// Initialize HLS if needed
|
|
355
|
-
if (mediaSources[0]
|
|
394
|
+
if (mediaSources[0]?.type === "hls" && enableEnhancedHLS) {
|
|
356
395
|
initializeHLS()
|
|
357
396
|
} else {
|
|
358
397
|
fallbackToNativePlayback()
|
|
@@ -371,8 +410,12 @@ export const EnhancedMediaPlayer: React.FC<EnhancedMediaPlayerProps> = ({
|
|
|
371
410
|
video.removeEventListener("timeupdate", handleTimeUpdate)
|
|
372
411
|
video.removeEventListener("loadedmetadata", handleLoadedMetadata)
|
|
373
412
|
video.removeEventListener("volumechange", handleVolumeChange)
|
|
374
|
-
|
|
375
|
-
|
|
413
|
+
if (onLoadStart) {
|
|
414
|
+
video.removeEventListener("loadstart", onLoadStart)
|
|
415
|
+
}
|
|
416
|
+
if (onCanPlay) {
|
|
417
|
+
video.removeEventListener("canplay", onCanPlay)
|
|
418
|
+
}
|
|
376
419
|
|
|
377
420
|
hlsProviderRef.current?.destroy()
|
|
378
421
|
pluginManagerRef.current?.dispose()
|
|
@@ -439,15 +482,15 @@ export const EnhancedMediaPlayer: React.FC<EnhancedMediaPlayerProps> = ({
|
|
|
439
482
|
<div
|
|
440
483
|
ref={containerRef}
|
|
441
484
|
className={cn("relative aspect-video overflow-hidden rounded-lg bg-black", className)}
|
|
442
|
-
{...
|
|
485
|
+
{...restProps}>
|
|
443
486
|
{/* Media Resource Preloader */}
|
|
444
487
|
{enableIntelligentPreloading && (
|
|
445
488
|
<MediaResourcePreloader
|
|
446
|
-
sources={mediaSources}
|
|
489
|
+
sources={mediaSources as any}
|
|
447
490
|
enableIntelligentPreloading={true}
|
|
448
491
|
adaptToNetworkConditions={true}
|
|
449
492
|
priority="auto"
|
|
450
|
-
strategy={preloadingStrategy}
|
|
493
|
+
strategy={preloadingStrategy === "balanced" ? "adaptive" : preloadingStrategy}
|
|
451
494
|
/>
|
|
452
495
|
)}
|
|
453
496
|
|
|
@@ -82,8 +82,7 @@ export function LoadingStatesDemo() {
|
|
|
82
82
|
|
|
83
83
|
<div className="aspect-video overflow-hidden rounded-lg bg-black">
|
|
84
84
|
<SimpleMinimalPlayer
|
|
85
|
-
src="https://
|
|
86
|
-
poster="https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/images/BigBuckBunny.jpg"
|
|
85
|
+
src="https://interactive-examples.mdn.mozilla.net/media/cc0-videos/flower.mp4"
|
|
87
86
|
crossOrigin="anonymous"
|
|
88
87
|
showLoadingState={true}
|
|
89
88
|
loadingComponent={showCustomLoading ? CustomLoadingComponent : undefined}
|
|
@@ -270,21 +270,17 @@ function PerformanceDebugPanel({
|
|
|
270
270
|
// Example usage component
|
|
271
271
|
export function React19DemoExample() {
|
|
272
272
|
const [currentSrc, setCurrentSrc] = React.useState(
|
|
273
|
-
"https://
|
|
273
|
+
"https://interactive-examples.mdn.mozilla.net/media/cc0-videos/flower.mp4"
|
|
274
274
|
)
|
|
275
275
|
|
|
276
276
|
const videoSources = [
|
|
277
277
|
{
|
|
278
|
-
src: "https://
|
|
279
|
-
title: "
|
|
280
|
-
poster:
|
|
281
|
-
"https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/images/BigBuckBunny.jpg"
|
|
278
|
+
src: "https://interactive-examples.mdn.mozilla.net/media/cc0-videos/flower.mp4",
|
|
279
|
+
title: "Flower (MDN)"
|
|
282
280
|
},
|
|
283
281
|
{
|
|
284
|
-
src: "https://
|
|
285
|
-
title: "
|
|
286
|
-
poster:
|
|
287
|
-
"https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/images/ElephantsDream.jpg"
|
|
282
|
+
src: "https://www.w3schools.com/html/mov_bbb.mp4",
|
|
283
|
+
title: "Big Buck Bunny"
|
|
288
284
|
}
|
|
289
285
|
]
|
|
290
286
|
|
|
@@ -320,7 +316,7 @@ export function React19DemoExample() {
|
|
|
320
316
|
{/* Enhanced Media Player */}
|
|
321
317
|
<React19MediaPlayerDemo
|
|
322
318
|
src={currentSrc}
|
|
323
|
-
poster={
|
|
319
|
+
poster={undefined}
|
|
324
320
|
mediaId={`demo-${currentSrc}`}
|
|
325
321
|
className="w-full"
|
|
326
322
|
/>
|
|
@@ -41,8 +41,7 @@ export function VolumeOrientationDemo() {
|
|
|
41
41
|
{/* Demo Player */}
|
|
42
42
|
<div className="aspect-video overflow-hidden rounded-lg bg-black">
|
|
43
43
|
<SimpleMinimalPlayer
|
|
44
|
-
src="https://
|
|
45
|
-
poster="https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/images/BigBuckBunny.jpg"
|
|
44
|
+
src="https://interactive-examples.mdn.mozilla.net/media/cc0-videos/flower.mp4"
|
|
46
45
|
crossOrigin="anonymous"
|
|
47
46
|
className="h-full w-full"
|
|
48
47
|
volumeOrientation={orientation}
|
|
@@ -30,7 +30,7 @@ ModernMediaPlayer.displayName = "DeprecatedModernMediaPlayer"
|
|
|
30
30
|
|
|
31
31
|
export const SimpleVideoPlayer = React.forwardRef<any, any>((props, ref) => {
|
|
32
32
|
showDeprecationWarning("SimpleVideoPlayer", "VideoPlayer (the primary video player component)")
|
|
33
|
-
return <SimpleVideoPlayerBase {...props
|
|
33
|
+
return <SimpleVideoPlayerBase {...(props as any)} />
|
|
34
34
|
})
|
|
35
35
|
|
|
36
36
|
SimpleVideoPlayer.displayName = "DeprecatedSimpleVideoPlayer"
|
|
@@ -41,7 +41,7 @@ export const LegacyPlayButton = React.forwardRef<any, any>((props, ref) => {
|
|
|
41
41
|
"PlayButton",
|
|
42
42
|
"Use MediaPlayer compound components or MediaPlayer (all-in-one)"
|
|
43
43
|
)
|
|
44
|
-
return <PlayButton {...props
|
|
44
|
+
return <PlayButton {...(props as any)} />
|
|
45
45
|
})
|
|
46
46
|
|
|
47
47
|
LegacyPlayButton.displayName = "DeprecatedPlayButton"
|
|
@@ -51,7 +51,7 @@ export const LegacySeekBar = React.forwardRef<any, any>((props, ref) => {
|
|
|
51
51
|
"SeekBar",
|
|
52
52
|
"Use MediaPlayer compound components or MediaPlayer (all-in-one)"
|
|
53
53
|
)
|
|
54
|
-
return <SeekBar {...props
|
|
54
|
+
return <SeekBar {...(props as any)} />
|
|
55
55
|
})
|
|
56
56
|
|
|
57
57
|
LegacySeekBar.displayName = "DeprecatedSeekBar"
|
|
@@ -61,7 +61,7 @@ export const LegacyTimeDisplay = React.forwardRef<any, any>((props, ref) => {
|
|
|
61
61
|
"TimeDisplay",
|
|
62
62
|
"Use MediaPlayer compound components or MediaPlayer (all-in-one)"
|
|
63
63
|
)
|
|
64
|
-
return <TimeDisplay {...props
|
|
64
|
+
return <TimeDisplay {...(props as any)} />
|
|
65
65
|
})
|
|
66
66
|
|
|
67
67
|
LegacyTimeDisplay.displayName = "DeprecatedTimeDisplay"
|
|
@@ -71,7 +71,7 @@ export const LegacyVolumeControl = React.forwardRef<any, any>((props, ref) => {
|
|
|
71
71
|
"VolumeControl",
|
|
72
72
|
"Use MediaPlayer compound components or MediaPlayer (all-in-one)"
|
|
73
73
|
)
|
|
74
|
-
return <VolumeControl {...props
|
|
74
|
+
return <VolumeControl {...(props as any)} />
|
|
75
75
|
})
|
|
76
76
|
|
|
77
77
|
LegacyVolumeControl.displayName = "DeprecatedVolumeControl"
|
|
@@ -81,7 +81,7 @@ export const LegacyFullscreenButton = React.forwardRef<any, any>((props, ref) =>
|
|
|
81
81
|
"FullscreenButton",
|
|
82
82
|
"Use MediaPlayer compound components or MediaPlayer (all-in-one)"
|
|
83
83
|
)
|
|
84
|
-
return <FullscreenButton {...props
|
|
84
|
+
return <FullscreenButton {...(props as any)} />
|
|
85
85
|
})
|
|
86
86
|
|
|
87
87
|
LegacyFullscreenButton.displayName = "DeprecatedFullscreenButton"
|
|
@@ -5,12 +5,12 @@ import { useMediaContext } from "../context/media-context"
|
|
|
5
5
|
|
|
6
6
|
// Utility function to compose refs
|
|
7
7
|
function composeRefs<T>(...refs: Array<React.Ref<T> | undefined>) {
|
|
8
|
-
return (node: T) => {
|
|
8
|
+
return (node: T | null) => {
|
|
9
9
|
refs.forEach((ref) => {
|
|
10
10
|
if (typeof ref === "function") {
|
|
11
11
|
ref(node)
|
|
12
12
|
} else if (ref) {
|
|
13
|
-
;(ref as React.MutableRefObject<T>).current = node
|
|
13
|
+
;(ref as React.MutableRefObject<T | null>).current = node
|
|
14
14
|
}
|
|
15
15
|
})
|
|
16
16
|
}
|
|
@@ -29,18 +29,11 @@ export function MediaPlayerVideo({
|
|
|
29
29
|
const context = useMediaContext()
|
|
30
30
|
|
|
31
31
|
// Create a ref that will be composed with the context mediaRef when mounted
|
|
32
|
-
const
|
|
33
|
-
() =>
|
|
34
|
-
|
|
35
|
-
return composeRefs(context.mediaRef)
|
|
36
|
-
}
|
|
32
|
+
const combinedRef = React.useMemo(
|
|
33
|
+
() => composeRefs(context.mediaRef),
|
|
34
|
+
[context.mediaRef]
|
|
37
35
|
)
|
|
38
36
|
|
|
39
|
-
// Update combined ref when context changes (but this won't trigger render)
|
|
40
|
-
React.useEffect(() => {
|
|
41
|
-
setCombinedRef(() => composeRefs(context.mediaRef))
|
|
42
|
-
}, [context.mediaRef])
|
|
43
|
-
|
|
44
37
|
if (asChild) {
|
|
45
38
|
// When asChild is true, clone the child element and add our props
|
|
46
39
|
const child = React.Children.only(children) as React.ReactElement
|
|
@@ -191,7 +191,7 @@ export const MediaPlayer = forwardRef<HTMLDivElement, MediaPlayerProps>(
|
|
|
191
191
|
playsInline
|
|
192
192
|
x-webkit-airplay="allow"
|
|
193
193
|
style={{ willChange: "transform" }}>
|
|
194
|
-
{renderSources
|
|
194
|
+
{renderSources}
|
|
195
195
|
Your browser does not support the video element.
|
|
196
196
|
</video>
|
|
197
197
|
) : (
|
|
@@ -201,7 +201,7 @@ export const MediaPlayer = forwardRef<HTMLDivElement, MediaPlayerProps>(
|
|
|
201
201
|
// Performance optimizations
|
|
202
202
|
preload={preloadStrategy === "eager" ? "auto" : "metadata"}
|
|
203
203
|
>
|
|
204
|
-
{renderSources
|
|
204
|
+
{renderSources}
|
|
205
205
|
Your browser does not support the audio element.
|
|
206
206
|
</audio>
|
|
207
207
|
)}
|
|
@@ -9,11 +9,11 @@ interface BlurPosterProps {
|
|
|
9
9
|
transitionDuration?: number
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
const
|
|
13
|
-
low: "
|
|
14
|
-
medium: "
|
|
15
|
-
high: "
|
|
16
|
-
}
|
|
12
|
+
const intensityBlurPx = {
|
|
13
|
+
low: "8px",
|
|
14
|
+
medium: "16px",
|
|
15
|
+
high: "32px"
|
|
16
|
+
} as const
|
|
17
17
|
|
|
18
18
|
const BlurPosterComponent = memo(function BlurPoster({
|
|
19
19
|
src,
|
|
@@ -24,15 +24,16 @@ const BlurPosterComponent = memo(function BlurPoster({
|
|
|
24
24
|
}: BlurPosterProps) {
|
|
25
25
|
return (
|
|
26
26
|
<div
|
|
27
|
-
className={cn(
|
|
28
|
-
"relative overflow-hidden bg-black/20",
|
|
29
|
-
"before:absolute before:inset-0 before:bg-center before:bg-cover before:opacity-100",
|
|
30
|
-
`before:[background-image:url('${src}')]`,
|
|
31
|
-
`before:[filter:blur(${intensity === "low" ? "8px" : intensity === "medium" ? "16px" : "32px"})]`,
|
|
32
|
-
className
|
|
33
|
-
)}
|
|
27
|
+
className={cn("relative overflow-hidden bg-black/20", className)}
|
|
34
28
|
aria-label={alt}
|
|
35
29
|
role="img">
|
|
30
|
+
<div
|
|
31
|
+
className="absolute inset-0 bg-center bg-cover opacity-100"
|
|
32
|
+
style={{
|
|
33
|
+
backgroundImage: `url('${src}')`,
|
|
34
|
+
filter: `blur(${intensityBlurPx[intensity]})`
|
|
35
|
+
}}
|
|
36
|
+
/>
|
|
36
37
|
<div
|
|
37
38
|
className="absolute inset-0 bg-gradient-to-t from-black/60 via-transparent to-black/20"
|
|
38
39
|
style={{ transitionDuration: `${transitionDuration}ms` }}
|
|
@@ -75,10 +76,12 @@ export const BlurPosterWithPlaceholder = memo(function BlurPosterWithPlaceholder
|
|
|
75
76
|
<div
|
|
76
77
|
className={cn(
|
|
77
78
|
"absolute inset-0 bg-center bg-cover opacity-0 transition-opacity duration-500",
|
|
78
|
-
!isLoading && "opacity-100"
|
|
79
|
-
`[background-image:url('${src}')]`,
|
|
80
|
-
`[filter:blur(${intensity === "low" ? "8px" : intensity === "medium" ? "16px" : "32px"})]`
|
|
79
|
+
!isLoading && "opacity-100"
|
|
81
80
|
)}
|
|
81
|
+
style={{
|
|
82
|
+
backgroundImage: `url('${src}')`,
|
|
83
|
+
filter: `blur(${intensityBlurPx[intensity]})`
|
|
84
|
+
}}
|
|
82
85
|
aria-hidden="true"
|
|
83
86
|
/>
|
|
84
87
|
</>
|
|
@@ -28,9 +28,9 @@ export const EnhancedSeekBar = forwardRef<HTMLDivElement, SeekBarProps>(
|
|
|
28
28
|
const deferredPreviewTime = useDeferredValue(previewTime)
|
|
29
29
|
|
|
30
30
|
// Throttling refs
|
|
31
|
-
const seekTimeoutRef = useRef<NodeJS.Timeout>()
|
|
31
|
+
const seekTimeoutRef = useRef<NodeJS.Timeout | undefined>(undefined)
|
|
32
32
|
const lastSeekTime = useRef<number>(0)
|
|
33
|
-
const rafId = useRef<number>()
|
|
33
|
+
const rafId = useRef<number | undefined>(undefined)
|
|
34
34
|
|
|
35
35
|
// Memoize expensive calculations
|
|
36
36
|
const progress = useMemo(
|