@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.
Files changed (142) hide show
  1. package/README.md +1 -2
  2. package/dist/context/media.js +1 -2
  3. package/dist/context/media.mjs +3 -15
  4. package/dist/context/store.js +1 -2
  5. package/dist/context/store.mjs +3 -5
  6. package/dist/context-Bzd5QCKM.cjs +7 -0
  7. package/dist/context-Bzd5QCKM.cjs.map +1 -0
  8. package/dist/context-DD_SH4tT.js +1053 -0
  9. package/dist/context-DD_SH4tT.js.map +1 -0
  10. package/dist/core-events-BRnLMB7s.cjs +2 -0
  11. package/dist/core-events-BRnLMB7s.cjs.map +1 -0
  12. package/dist/core-events-v_mKdj5p.js +417 -0
  13. package/dist/core-events-v_mKdj5p.js.map +1 -0
  14. package/dist/core-plugins-CMBv-yVh.cjs +2 -0
  15. package/dist/core-plugins-CMBv-yVh.cjs.map +1 -0
  16. package/dist/core-plugins-CqCxLkbd.js +754 -0
  17. package/dist/core-plugins-CqCxLkbd.js.map +1 -0
  18. package/dist/core.js +7 -29
  19. package/dist/core.js.map +1 -1
  20. package/dist/core.mjs +947 -1220
  21. package/dist/core.mjs.map +1 -1
  22. package/dist/enhanced-components-BwjBqSA1.js +2858 -0
  23. package/dist/enhanced-components-BwjBqSA1.js.map +1 -0
  24. package/dist/enhanced-components-C-ka-XLp.cjs +2 -0
  25. package/dist/enhanced-components-C-ka-XLp.cjs.map +1 -0
  26. package/dist/hooks-B-IIWPJr.js +1842 -0
  27. package/dist/hooks-B-IIWPJr.js.map +1 -0
  28. package/dist/hooks-DcHzmBwA.cjs +4 -0
  29. package/dist/hooks-DcHzmBwA.cjs.map +1 -0
  30. package/dist/index.js +27 -97
  31. package/dist/index.js.map +1 -1
  32. package/dist/index.mjs +5613 -6210
  33. package/dist/index.mjs.map +1 -1
  34. package/dist/minimal-components-BYsMQde5.js +1610 -0
  35. package/dist/minimal-components-BYsMQde5.js.map +1 -0
  36. package/dist/minimal-components-CsxQbDp7.cjs +2 -0
  37. package/dist/minimal-components-CsxQbDp7.cjs.map +1 -0
  38. package/dist/performance-monitor-DCSijLJg.js +312 -0
  39. package/dist/performance-monitor-DCSijLJg.js.map +1 -0
  40. package/dist/performance-monitor-wNFKmqMr.cjs +2 -0
  41. package/dist/performance-monitor-wNFKmqMr.cjs.map +1 -0
  42. package/dist/rolldown-runtime-BmowowmI.js +14 -0
  43. package/dist/rolldown-runtime-mLOUI7ql.cjs +1 -0
  44. package/dist/utils-D2gQ7c2R.js +64 -0
  45. package/dist/utils-D2gQ7c2R.js.map +1 -0
  46. package/dist/utils-DJaAwiZ9.cjs +2 -0
  47. package/dist/utils-DJaAwiZ9.cjs.map +1 -0
  48. package/dist/utils.js +1 -2
  49. package/dist/utils.mjs +3 -11
  50. package/package.json +28 -22
  51. package/src/components/AnimateLoading.tsx +14 -10
  52. package/src/components/accessibility/keyboard-help-overlay.tsx +2 -0
  53. package/src/components/enhanced/debug-panel.tsx +1 -1
  54. package/src/components/enhanced/enhanced-media-player.tsx +68 -25
  55. package/src/components/examples/loading-states-demo.tsx +1 -2
  56. package/src/components/examples/react-19-demo.tsx +6 -10
  57. package/src/components/examples/volume-orientation-demo.tsx +1 -2
  58. package/src/components/legacy.tsx +6 -6
  59. package/src/components/media-player-video.tsx +5 -12
  60. package/src/components/media-player.tsx +2 -2
  61. package/src/components/ui/blur-poster.tsx +18 -15
  62. package/src/components/ui/enhanced-seek-bar.tsx +2 -2
  63. package/src/components/ui/media-resource-preloader.tsx +21 -23
  64. package/src/components/unified/base-media-player.tsx +22 -10
  65. package/src/components/variants/minimal/AccessibleMinimalPlayer.tsx +58 -38
  66. package/src/components/variants/minimal/EnhancedMinimalMediaPlayer.tsx +6 -6
  67. package/src/components/variants/minimal/components/VideoElement.tsx +3 -1
  68. package/src/core/optimization/bundle-optimizer.tsx +26 -27
  69. package/src/core/performance/media-loading-optimizer.ts +2 -1
  70. package/src/core/plugins/__tests__/registry.test.ts +1 -1
  71. package/src/core/plugins/enhanced-plugin-system.ts +26 -8
  72. package/src/core/plugins/registry.ts +319 -92
  73. package/src/core/plugins/types.ts +21 -3
  74. package/src/core/providers/enhanced-hls-provider.ts +11 -40
  75. package/src/core/providers/lazy-provider.tsx +5 -42
  76. package/src/features/media-session/media-session-manager.ts +4 -26
  77. package/src/features/picture-in-picture/pip-manager.ts +2 -2
  78. package/src/features/playlist/playlist-manager.ts +26 -18
  79. package/src/features/subtitles/subtitle-manager.ts +1 -1
  80. package/src/features/subtitles/subtitle-parser.ts +19 -16
  81. package/src/features/thumbnails/thumbnail-generator.ts +12 -5
  82. package/src/hooks/__tests__/currentTime-fix.test.ts +149 -0
  83. package/src/hooks/accessibility/use-accessibility-preferences.ts +1 -1
  84. package/src/hooks/accessibility/use-focus-management.ts +2 -2
  85. package/src/hooks/accessibility/use-keyboard-shortcuts.ts +2 -2
  86. package/src/hooks/accessibility/use-screen-reader.ts +1 -1
  87. package/src/hooks/core/index.ts +1 -1
  88. package/src/hooks/use-buffer-health.ts +1 -1
  89. package/src/hooks/use-enhanced-media-player.ts +107 -9
  90. package/src/hooks/use-focus-trap.tsx +2 -2
  91. package/src/hooks/use-intelligent-preloading.ts +23 -3
  92. package/src/hooks/use-keyboard-shortcuts.ts +2 -2
  93. package/src/hooks/use-media-player.ts +74 -43
  94. package/src/hooks/use-memory-optimization.ts +15 -15
  95. package/src/hooks/use-smart-preload.ts +1 -2
  96. package/src/hooks/useKeyboardShortcuts.ts +34 -2
  97. package/src/hooks/useScreenshot.ts +60 -44
  98. package/src/hooks/useVideoPlayer.ts +50 -4
  99. package/src/index-legacy.ts +3 -5
  100. package/src/test/accessibility-utils.ts +7 -7
  101. package/src/test/media-mocks.ts +41 -28
  102. package/src/test/setup.ts +22 -4
  103. package/src/types/assets.d.ts +29 -0
  104. package/src/types/index.ts +13 -0
  105. package/dist/context/media.js.map +0 -1
  106. package/dist/context/media.mjs.map +0 -1
  107. package/dist/context/store.js.map +0 -1
  108. package/dist/context/store.mjs.map +0 -1
  109. package/dist/context-BYwDoSRX.js +0 -1172
  110. package/dist/context-BYwDoSRX.js.map +0 -1
  111. package/dist/context-eNS62_ac.cjs +0 -23
  112. package/dist/context-eNS62_ac.cjs.map +0 -1
  113. package/dist/core-events-B1iM2F46.cjs +0 -2
  114. package/dist/core-events-B1iM2F46.cjs.map +0 -1
  115. package/dist/core-events-DunPHKRE.js +0 -484
  116. package/dist/core-events-DunPHKRE.js.map +0 -1
  117. package/dist/core-plugins-CROGvQUy.js +0 -686
  118. package/dist/core-plugins-CROGvQUy.js.map +0 -1
  119. package/dist/core-plugins-DtsHBOtF.cjs +0 -2
  120. package/dist/core-plugins-DtsHBOtF.cjs.map +0 -1
  121. package/dist/enhanced-components-DUw2KrmQ.js +0 -1142
  122. package/dist/enhanced-components-DUw2KrmQ.js.map +0 -1
  123. package/dist/enhanced-components-DXBCHU0v.cjs +0 -2
  124. package/dist/enhanced-components-DXBCHU0v.cjs.map +0 -1
  125. package/dist/hooks-BW-JjVgP.cjs +0 -4
  126. package/dist/hooks-BW-JjVgP.cjs.map +0 -1
  127. package/dist/hooks-CyX6De5M.js +0 -1693
  128. package/dist/hooks-CyX6De5M.js.map +0 -1
  129. package/dist/minimal-components-Bdcpxav5.cjs +0 -2
  130. package/dist/minimal-components-Bdcpxav5.cjs.map +0 -1
  131. package/dist/minimal-components-DlRK7Qu_.js +0 -1927
  132. package/dist/minimal-components-DlRK7Qu_.js.map +0 -1
  133. package/dist/performance-monitor-C5k6FDXw.js +0 -301
  134. package/dist/performance-monitor-C5k6FDXw.js.map +0 -1
  135. package/dist/performance-monitor-lJmuUjQQ.cjs +0 -2
  136. package/dist/performance-monitor-lJmuUjQQ.cjs.map +0 -1
  137. package/dist/utils-CFOue_9K.cjs +0 -9
  138. package/dist/utils-CFOue_9K.cjs.map +0 -1
  139. package/dist/utils-Dzpl2ArK.js +0 -2844
  140. package/dist/utils-Dzpl2ArK.js.map +0 -1
  141. package/dist/utils.js.map +0 -1
  142. 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.0",
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
- "types": "./dist/index.d.ts",
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": "./dist/styles/globals.css",
15
- "./minimal-player.css": "./dist/styles/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": "./dist/index.d.ts",
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.3.0",
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": "^4.0.0",
54
- "@testing-library/jest-dom": "^6.6.3",
55
- "@testing-library/react": "^16.1.0",
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.1.4",
59
- "@types/react-dom": "^19.1.5",
60
- "@vitejs/plugin-react": "^4.3.4",
61
- "@vitest/coverage-v8": "^2.1.8",
62
- "babel-plugin-react-compiler": "^19.1.0-rc.3",
63
- "jsdom": "^25.0.1",
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": "^11.1.7",
68
- "tailwindcss": "^4.0.0",
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": "^6.1.1",
76
+ "typescript": "^5.9.3",
77
+ "vite": "^8.1.4",
72
78
  "vite-bundle-analyzer": "^0.13.1",
73
- "vitest": "^2.1.8"
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": ">=18.0.0"
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
- // Try to import the GIF using direct import for better compatibility
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
- try {
14
- monaLoadingGif = monaGifSrc
15
- } catch {
16
- // GIF import failed, will use fallback
17
- monaLoadingGif = undefined
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, MediaSource } from "../../types"
24
+ import { MediaPlayerProps, type MediaMimeType } from "../../types"
25
25
  import { MediaResourcePreloader } from "../ui/media-resource-preloader"
26
26
 
27
- interface EnhancedMediaPlayerProps extends MediaPlayerProps {
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
- ...props
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((): MediaSource[] => {
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 hlsConfig = { ...ENHANCED_HLS_DEFAULTS, ...hlsConfig }
153
- const provider = new EnhancedHLSProvider(hlsConfig)
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
- hlsConfig: hlsConfig,
159
- onError: handleMediaError
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
- onError?.(error)
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
- handleMediaError(new Error(`HLS Error: ${data.details}`))
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
- video.addEventListener("loadstart", onLoadStart)
352
- video.addEventListener("canplay", onCanPlay)
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].type === "hls" && enableEnhancedHLS) {
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
- video.removeEventListener("loadstart", onLoadStart)
375
- video.removeEventListener("canplay", onCanPlay)
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
- {...props}>
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://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4"
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://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4"
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://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4",
279
- title: "Big Buck Bunny",
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://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ElephantsDream.mp4",
285
- title: "Elephants Dream",
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={currentVideo.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://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4"
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} ref={ref} />
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} ref={ref} />
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} ref={ref} />
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} ref={ref} />
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} ref={ref} />
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} ref={ref} />
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 [combinedRef, setCombinedRef] = React.useState<React.RefCallback<HTMLVideoElement> | null>(
33
- () => {
34
- // This runs once during initialization to create the combined ref
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 intensityClasses = {
13
- low: "blur-sm",
14
- medium: "blur-md",
15
- high: "blur-lg"
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(