@therealtinhtute/baroiplayer 1.1.0 → 1.2.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.
Files changed (78) hide show
  1. package/dist/core-plugins-By-4-kGs.cjs +2 -0
  2. package/dist/core-plugins-By-4-kGs.cjs.map +1 -0
  3. package/dist/{core-plugins-CROGvQUy.js → core-plugins-DSBWcTuX.js} +444 -309
  4. package/dist/core-plugins-DSBWcTuX.js.map +1 -0
  5. package/dist/core.js +2 -2
  6. package/dist/core.mjs +19 -19
  7. package/dist/{enhanced-components-DUw2KrmQ.js → enhanced-components-B_l3cist.js} +2 -2
  8. package/dist/{enhanced-components-DUw2KrmQ.js.map → enhanced-components-B_l3cist.js.map} +1 -1
  9. package/dist/{enhanced-components-DXBCHU0v.cjs → enhanced-components-BspFs9OO.cjs} +2 -2
  10. package/dist/{enhanced-components-DXBCHU0v.cjs.map → enhanced-components-BspFs9OO.cjs.map} +1 -1
  11. package/dist/hooks-BXwI8QG6.cjs +4 -0
  12. package/dist/hooks-BXwI8QG6.cjs.map +1 -0
  13. package/dist/hooks-Bpi-jDiO.js +1816 -0
  14. package/dist/hooks-Bpi-jDiO.js.map +1 -0
  15. package/dist/index.js +2 -2
  16. package/dist/index.js.map +1 -1
  17. package/dist/index.mjs +18 -20
  18. package/dist/index.mjs.map +1 -1
  19. package/dist/{minimal-components-DlRK7Qu_.js → minimal-components-C4z900Ro.js} +297 -298
  20. package/dist/minimal-components-C4z900Ro.js.map +1 -0
  21. package/dist/minimal-components-v4qBp6jv.cjs +2 -0
  22. package/dist/minimal-components-v4qBp6jv.cjs.map +1 -0
  23. package/package.json +1 -1
  24. package/src/components/accessibility/keyboard-help-overlay.tsx +2 -0
  25. package/src/components/enhanced/debug-panel.tsx +1 -1
  26. package/src/components/enhanced/enhanced-media-player.tsx +68 -25
  27. package/src/components/legacy.tsx +6 -6
  28. package/src/components/media-player-video.tsx +5 -12
  29. package/src/components/media-player.tsx +2 -2
  30. package/src/components/ui/enhanced-seek-bar.tsx +2 -2
  31. package/src/components/ui/media-resource-preloader.tsx +21 -23
  32. package/src/components/unified/base-media-player.tsx +22 -10
  33. package/src/components/variants/minimal/AccessibleMinimalPlayer.tsx +58 -38
  34. package/src/components/variants/minimal/EnhancedMinimalMediaPlayer.tsx +6 -6
  35. package/src/core/optimization/bundle-optimizer.tsx +26 -27
  36. package/src/core/plugins/__tests__/registry.test.ts +1 -1
  37. package/src/core/plugins/enhanced-plugin-system.ts +26 -8
  38. package/src/core/plugins/registry.ts +319 -92
  39. package/src/core/plugins/types.ts +21 -3
  40. package/src/core/providers/enhanced-hls-provider.ts +11 -40
  41. package/src/core/providers/lazy-provider.tsx +5 -42
  42. package/src/features/media-session/media-session-manager.ts +4 -26
  43. package/src/features/picture-in-picture/pip-manager.ts +2 -2
  44. package/src/features/playlist/playlist-manager.ts +26 -18
  45. package/src/features/subtitles/subtitle-manager.ts +1 -1
  46. package/src/features/subtitles/subtitle-parser.ts +19 -16
  47. package/src/features/thumbnails/thumbnail-generator.ts +12 -5
  48. package/src/hooks/__tests__/currentTime-fix.test.ts +149 -0
  49. package/src/hooks/accessibility/use-accessibility-preferences.ts +1 -1
  50. package/src/hooks/accessibility/use-focus-management.ts +2 -2
  51. package/src/hooks/accessibility/use-keyboard-shortcuts.ts +2 -2
  52. package/src/hooks/accessibility/use-screen-reader.ts +1 -1
  53. package/src/hooks/core/index.ts +1 -1
  54. package/src/hooks/use-buffer-health.ts +1 -1
  55. package/src/hooks/use-enhanced-media-player.ts +107 -9
  56. package/src/hooks/use-focus-trap.tsx +2 -2
  57. package/src/hooks/use-intelligent-preloading.ts +23 -3
  58. package/src/hooks/use-keyboard-shortcuts.ts +2 -2
  59. package/src/hooks/use-media-player.ts +74 -43
  60. package/src/hooks/use-memory-optimization.ts +15 -15
  61. package/src/hooks/use-smart-preload.ts +1 -2
  62. package/src/hooks/useKeyboardShortcuts.ts +34 -2
  63. package/src/hooks/useVideoPlayer.ts +50 -4
  64. package/src/index-legacy.ts +3 -5
  65. package/src/test/accessibility-utils.ts +7 -7
  66. package/src/test/media-mocks.ts +41 -28
  67. package/src/test/setup.ts +22 -4
  68. package/src/types/index.ts +13 -0
  69. package/dist/core-plugins-CROGvQUy.js.map +0 -1
  70. package/dist/core-plugins-DtsHBOtF.cjs +0 -2
  71. package/dist/core-plugins-DtsHBOtF.cjs.map +0 -1
  72. package/dist/hooks-BW-JjVgP.cjs +0 -4
  73. package/dist/hooks-BW-JjVgP.cjs.map +0 -1
  74. package/dist/hooks-CyX6De5M.js +0 -1693
  75. package/dist/hooks-CyX6De5M.js.map +0 -1
  76. package/dist/minimal-components-Bdcpxav5.cjs +0 -2
  77. package/dist/minimal-components-Bdcpxav5.cjs.map +0 -1
  78. package/dist/minimal-components-DlRK7Qu_.js.map +0 -1
@@ -44,4 +44,4 @@ export type { VideoState, VideoControls } from "../useVideoPlayer"
44
44
 
45
45
  // Specialized hooks
46
46
  export { useMediaMetadata } from "../use-media-metadata"
47
- export { useOptimisticControls } from "../use-optimistic-controls"
47
+ export { useOptimisticMediaState } from "../use-optimistic-controls"
@@ -120,7 +120,7 @@ export function useBufferHealth(
120
120
  }
121
121
 
122
122
  if ("audioTracks" in video) {
123
- const tracks = (video as HTMLVideoElement).audioTracks
123
+ const tracks = (video as any).audioTracks
124
124
  if (tracks && tracks.length > 0) {
125
125
  audioLevel = tracks[0]?.volume || null
126
126
  }
@@ -34,6 +34,32 @@ interface UseEnhancedMediaPlayerOptions {
34
34
  timeUpdateThrottleMs?: number
35
35
  enableSeekPreview?: boolean
36
36
  preloadStrategy?: "eager" | "lazy" | "progressive"
37
+ // Event handlers
38
+ onPlay?: () => void
39
+ onPause?: () => void
40
+ }
41
+
42
+ /**
43
+ * Safely sets currentTime on a media element, ensuring the value is finite
44
+ */
45
+ function safeSetCurrentTime(media: HTMLVideoElement | HTMLAudioElement, time: number): boolean {
46
+ if (!Number.isFinite(time)) {
47
+ console.warn(`Attempted to set non-finite currentTime: ${time}`)
48
+ return false
49
+ }
50
+
51
+ if (time < 0) {
52
+ console.warn(`Attempted to set negative currentTime: ${time}`)
53
+ return false
54
+ }
55
+
56
+ try {
57
+ media.currentTime = time
58
+ return true
59
+ } catch (error) {
60
+ console.error(`Failed to set currentTime to ${time}:`, error)
61
+ return false
62
+ }
37
63
  }
38
64
 
39
65
  // Performance-optimized media player hook with React 19 features
@@ -51,7 +77,7 @@ export function useEnhancedMediaPlayer(options: UseEnhancedMediaPlayerOptions) {
51
77
  const lastFrameTime = React.useRef<number>(0)
52
78
 
53
79
  // Throttled time update
54
- const timeUpdateTimeoutRef = useRef<NodeJS.Timeout>()
80
+ const timeUpdateTimeoutRef = useRef<ReturnType<typeof setTimeout> | undefined>(undefined)
55
81
  const lastTimeUpdate = useRef<number>(0)
56
82
 
57
83
  // Store integration
@@ -331,14 +357,25 @@ export function useEnhancedMediaPlayer(options: UseEnhancedMediaPlayerOptions) {
331
357
  const media = mediaRef.current
332
358
  if (!media) return
333
359
 
334
- const clampedTime = Math.max(0, Math.min(time, duration || 0))
360
+ const validDuration = Number.isFinite(duration) && duration > 0 ? duration : Number.isFinite(media.duration) ? media.duration : 0
361
+ if (validDuration === 0) {
362
+ console.warn("Cannot seek: duration is not available")
363
+ return
364
+ }
365
+
366
+ const clampedTime = Math.max(0, Math.min(time, validDuration))
367
+
368
+ if (!Number.isFinite(clampedTime)) {
369
+ console.warn(`Computed non-finite seek time: ${clampedTime}`)
370
+ return
371
+ }
335
372
 
336
373
  if (isEnhanced && actions) {
337
374
  startSeekTransition(() => {
338
375
  actions.seek(clampedTime, true) // Optimistic
339
376
  })
340
377
  } else {
341
- media.currentTime = clampedTime
378
+ safeSetCurrentTime(media, clampedTime)
342
379
  }
343
380
  }
344
381
 
@@ -396,18 +433,79 @@ export function useEnhancedMediaPlayer(options: UseEnhancedMediaPlayerOptions) {
396
433
  }
397
434
  }
398
435
 
436
+ const mute = () => {
437
+ const media = mediaRef.current
438
+ if (!media) return
439
+
440
+ if (isEnhanced && actions) {
441
+ actions.mute(true)
442
+ } else {
443
+ media.muted = true
444
+ }
445
+ }
446
+
447
+ const unmute = () => {
448
+ const media = mediaRef.current
449
+ if (!media) return
450
+
451
+ if (isEnhanced && actions) {
452
+ actions.unmute(true)
453
+ } else {
454
+ media.muted = false
455
+ }
456
+ }
457
+
458
+ const toggleFullscreen = async () => {
459
+ if (isFullscreen) {
460
+ await exitFullscreen()
461
+ } else {
462
+ await enterFullscreen()
463
+ }
464
+ }
465
+
466
+ const setCurrentTime = (time: number) => {
467
+ const media = mediaRef.current
468
+ if (!media) return
469
+
470
+ const validDuration =
471
+ Number.isFinite(duration) && duration > 0
472
+ ? duration
473
+ : Number.isFinite(media.duration)
474
+ ? media.duration
475
+ : 0
476
+
477
+ if (validDuration === 0) {
478
+ console.warn("Cannot set currentTime: duration is not available")
479
+ return
480
+ }
481
+
482
+ const clampedTime = Math.max(0, Math.min(time, validDuration))
483
+
484
+ if (!Number.isFinite(clampedTime)) {
485
+ console.warn(`Computed non-finite currentTime: ${clampedTime}`)
486
+ return
487
+ }
488
+
489
+ safeSetCurrentTime(media, clampedTime)
490
+ }
491
+
399
492
  return {
400
493
  play,
401
494
  pause,
402
495
  toggle,
403
496
  seek,
497
+ setCurrentTime,
404
498
  setVolume,
499
+ mute,
500
+ unmute,
405
501
  toggleMute,
406
502
  enterFullscreen,
407
- exitFullscreen
503
+ exitFullscreen,
504
+ toggleFullscreen
408
505
  }
409
506
  }, [
410
507
  isPlaying,
508
+ isFullscreen,
411
509
  duration,
412
510
  isEnhanced,
413
511
  actions,
@@ -421,13 +519,13 @@ export function useEnhancedMediaPlayer(options: UseEnhancedMediaPlayerOptions) {
421
519
  () => ({
422
520
  isPlaying: optimisticState?.isPlaying ?? isPlaying,
423
521
  currentTime: optimisticState?.currentTime ?? currentTime,
424
- duration: optimisticState?.duration ?? duration,
522
+ duration: optimisticState?.actualState?.duration ?? duration,
425
523
  volume: optimisticState?.volume ?? optimisticVolume,
426
524
  isMuted: optimisticState?.isMuted ?? isMuted,
427
525
  isLoading: optimisticState?.isBuffering ?? isLoading,
428
526
  isBuffering: optimisticState?.isBuffering ?? isBuffering,
429
- error: optimisticState?.error ?? error,
430
- buffered: optimisticState?.buffered ?? buffered,
527
+ error: optimisticState?.actualState?.error ?? error,
528
+ buffered: optimisticState?.actualState?.buffered ?? buffered,
431
529
  isFullscreen: optimisticState?.isFullscreen ?? isFullscreen,
432
530
  progress: duration > 0 ? currentTime / duration : 0,
433
531
  isTransitioning: isPending || isSeeking,
@@ -467,8 +565,8 @@ export function useEnhancedMediaPlayer(options: UseEnhancedMediaPlayerOptions) {
467
565
  isTransitioning: isPending || isSeeking,
468
566
 
469
567
  // Enhanced features
470
- actions: optimisticState?.actions,
471
- actionState: optimisticState?.actionState,
568
+ actions: optimisticState,
569
+ actionState: optimisticState,
472
570
 
473
571
  // Optimistic features
474
572
  optimisticState,
@@ -98,14 +98,14 @@ export function useFocusTrap(
98
98
  const focusableElements = getFocusableElements()
99
99
  const firstElement = focusableElements[0]
100
100
  firstElement?.focus()
101
- lastFocusedElementRef.current = firstElement
101
+ lastFocusedElementRef.current = firstElement || null
102
102
  }, [getFocusableElements])
103
103
 
104
104
  const focusLastElement = useCallback(() => {
105
105
  const focusableElements = getFocusableElements()
106
106
  const lastElement = focusableElements[focusableElements.length - 1]
107
107
  lastElement?.focus()
108
- lastFocusedElementRef.current = lastElement
108
+ lastFocusedElementRef.current = lastElement || null
109
109
  }, [getFocusableElements])
110
110
 
111
111
  return {
@@ -6,6 +6,16 @@
6
6
  import { useCallback, useEffect, useMemo, useRef } from "react"
7
7
  import { MediaSource } from "../types"
8
8
 
9
+ interface NetworkInformation {
10
+ type?: string
11
+ downlink?: number
12
+ effectiveType?: "slow-2g" | "2g" | "3g" | "4g"
13
+ saveData?: boolean
14
+ rtt?: number
15
+ addEventListener?: (type: string, listener: EventListener) => void
16
+ removeEventListener?: (type: string, listener: EventListener) => void
17
+ }
18
+
9
19
  interface IntelligentPreloadingOptions {
10
20
  /** Enable user behavior tracking */
11
21
  trackUserBehavior?: boolean
@@ -158,8 +168,10 @@ export const useIntelligentPreloading = (
158
168
 
159
169
  // Additional heuristics
160
170
  if (shouldPreload) {
171
+ const sourceType = source.type as string
172
+
161
173
  // Prioritize HLS and adaptive streaming
162
- if (source.type === "hls" || source.type === "m3u8") {
174
+ if (sourceType === "hls" || sourceType === "m3u8" || sourceType === "application/x-mpegURL") {
163
175
  priority = priority === "low" ? "medium" : "high"
164
176
  }
165
177
 
@@ -171,7 +183,7 @@ export const useIntelligentPreloading = (
171
183
  }
172
184
 
173
185
  // Consider content type (video gets higher priority than audio)
174
- if (source.type === "mp4" || source.type === "webm") {
186
+ if (sourceType === "mp4" || sourceType === "webm" || sourceType === "video/mp4" || sourceType === "video/webm") {
175
187
  priority = priority === "low" ? "medium" : priority
176
188
  }
177
189
  }
@@ -189,19 +201,27 @@ export const useIntelligentPreloading = (
189
201
  link.rel = "preload"
190
202
  link.href = source.src
191
203
 
204
+ const sourceType = source.type as string
205
+
192
206
  // Determine the correct 'as' attribute
193
- switch (source.type) {
207
+ switch (sourceType) {
194
208
  case "hls":
195
209
  case "m3u8":
210
+ case "application/x-mpegURL":
196
211
  link.as = "fetch"
197
212
  break
198
213
  case "mp4":
199
214
  case "webm":
215
+ case "video/mp4":
216
+ case "video/webm":
200
217
  link.as = "video"
201
218
  break
202
219
  case "mp3":
203
220
  case "wav":
204
221
  case "ogg":
222
+ case "audio/mpeg":
223
+ case "audio/wav":
224
+ case "audio/ogg":
205
225
  link.as = "audio"
206
226
  break
207
227
  default:
@@ -63,10 +63,10 @@ export function useKeyboardShortcuts(
63
63
  useEffect(() => {
64
64
  const target = opts.capture ? window.document.body : window
65
65
 
66
- target.addEventListener("keydown", handleKeyDown)
66
+ target.addEventListener("keydown", handleKeyDown as EventListener)
67
67
 
68
68
  return () => {
69
- target.removeEventListener("keydown", handleKeyDown)
69
+ target.removeEventListener("keydown", handleKeyDown as EventListener)
70
70
  }
71
71
  }, [handleKeyDown, opts.capture])
72
72
 
@@ -27,6 +27,29 @@ interface UseMediaPlayerOptions {
27
27
  onEnded?: () => void
28
28
  }
29
29
 
30
+ /**
31
+ * Safely sets currentTime on a media element, ensuring the value is finite
32
+ */
33
+ function safeSetCurrentTime(media: HTMLVideoElement | HTMLAudioElement, time: number): boolean {
34
+ if (!Number.isFinite(time)) {
35
+ console.warn(`Attempted to set non-finite currentTime: ${time}`)
36
+ return false
37
+ }
38
+
39
+ if (time < 0) {
40
+ console.warn(`Attempted to set negative currentTime: ${time}`)
41
+ return false
42
+ }
43
+
44
+ try {
45
+ media.currentTime = time
46
+ return true
47
+ } catch (error) {
48
+ console.error(`Failed to set currentTime to ${time}:`, error)
49
+ return false
50
+ }
51
+ }
52
+
30
53
  export function useMediaPlayer(options: UseMediaPlayerOptions) {
31
54
  const mediaRef = useRef<HTMLVideoElement | HTMLAudioElement>(null)
32
55
  const containerRef = useRef<HTMLDivElement>(null)
@@ -73,53 +96,53 @@ export function useMediaPlayer(options: UseMediaPlayerOptions) {
73
96
  }, [options, setVolume, setMuted])
74
97
 
75
98
  // Event handlers
76
- const handlePlay = () => {
99
+ const handlePlay = useCallback(() => {
77
100
  setPlaying(true)
78
101
  options.onPlay?.()
79
- }
102
+ }, [setPlaying, options])
80
103
 
81
- const handlePause = () => {
104
+ const handlePause = useCallback(() => {
82
105
  setPlaying(false)
83
106
  options.onPause?.()
84
- }
107
+ }, [setPlaying, options])
85
108
 
86
- const handleTimeUpdate = () => {
109
+ const handleTimeUpdate = useCallback(() => {
87
110
  const media = mediaRef.current
88
111
  if (!media) return
89
112
 
90
113
  setCurrentTime(media.currentTime)
91
114
  options.onTimeUpdate?.(media.currentTime)
92
- }
115
+ }, [setCurrentTime, options])
93
116
 
94
- const handleDurationChange = () => {
117
+ const handleDurationChange = useCallback(() => {
95
118
  const media = mediaRef.current
96
119
  if (!media) return
97
120
 
98
121
  setDuration(media.duration)
99
122
  options.onDurationChange?.(media.duration)
100
- }
123
+ }, [setDuration, options])
101
124
 
102
- const handleVolumeChange = () => {
125
+ const handleVolumeChange = useCallback(() => {
103
126
  const media = mediaRef.current
104
127
  if (!media) return
105
128
 
106
129
  setVolume(media.volume)
107
130
  setMuted(media.muted)
108
131
  options.onVolumeChange?.(media.volume)
109
- }
132
+ }, [setVolume, setMuted, options])
110
133
 
111
- const handleLoadStart = () => {
134
+ const handleLoadStart = useCallback(() => {
112
135
  setLoading(true)
113
136
  setError(null)
114
137
  options.onLoadStart?.()
115
- }
138
+ }, [setLoading, setError, options])
116
139
 
117
- const handleLoadedData = () => {
140
+ const handleLoadedData = useCallback(() => {
118
141
  setLoading(false)
119
142
  options.onLoadedData?.()
120
- }
143
+ }, [setLoading, options])
121
144
 
122
- const handleError = () => {
145
+ const handleError = useCallback(() => {
123
146
  const media = mediaRef.current
124
147
  if (!media) return
125
148
 
@@ -127,19 +150,19 @@ export function useMediaPlayer(options: UseMediaPlayerOptions) {
127
150
  setError(errorMessage)
128
151
  setLoading(false)
129
152
  options.onError?.(errorMessage)
130
- }
153
+ }, [setError, setLoading, options])
131
154
 
132
- const handleEnded = () => {
155
+ const handleEnded = useCallback(() => {
133
156
  setPlaying(false)
134
157
  options.onEnded?.()
135
- }
158
+ }, [setPlaying, options])
136
159
 
137
- const handleProgress = () => {
160
+ const handleProgress = useCallback(() => {
138
161
  const media = mediaRef.current
139
162
  if (!media) return
140
163
 
141
164
  setBuffered(media.buffered)
142
- }
165
+ }, [setBuffered])
143
166
 
144
167
  // Attach event listeners
145
168
  useEffect(() => {
@@ -183,7 +206,7 @@ export function useMediaPlayer(options: UseMediaPlayerOptions) {
183
206
  ])
184
207
 
185
208
  // Control functions
186
- const play = async () => {
209
+ const play = useCallback(async () => {
187
210
  const media = mediaRef.current
188
211
  if (!media) return
189
212
 
@@ -193,60 +216,67 @@ export function useMediaPlayer(options: UseMediaPlayerOptions) {
193
216
  console.error("Failed to play media:", error)
194
217
  setError("Failed to play media")
195
218
  }
196
- }
219
+ }, [setError])
197
220
 
198
- const pause = () => {
221
+ const pause = useCallback(() => {
199
222
  const media = mediaRef.current
200
223
  if (!media) return
201
224
 
202
225
  media.pause()
203
- }
226
+ }, [])
204
227
 
205
- const toggle = () => {
228
+ const toggle = useCallback(() => {
206
229
  if (state.isPlaying) {
207
230
  pause()
208
231
  } else {
209
232
  play()
210
233
  }
211
- }
234
+ }, [state.isPlaying, pause, play])
212
235
 
213
- const seek = (time: number) => {
236
+ const seek = useCallback((time: number) => {
214
237
  const media = mediaRef.current
215
238
  if (!media) return
216
239
 
217
- media.currentTime = Math.max(0, Math.min(time, media.duration || 0))
218
- }
240
+ const duration = Number.isFinite(media.duration) ? media.duration : 0
241
+ if (duration === 0) {
242
+ console.warn("Cannot seek: duration is not available")
243
+ return
244
+ }
219
245
 
220
- const setVolumeControl = (volume: number) => {
246
+ const clampedTime = Math.max(0, Math.min(time, duration))
247
+ safeSetCurrentTime(media, clampedTime)
248
+ }, [])
249
+
250
+ const setVolumeControl = useCallback((volume: number) => {
221
251
  const media = mediaRef.current
222
252
  if (!media) return
223
253
 
224
254
  const clampedVolume = Math.max(0, Math.min(1, volume))
225
255
  media.volume = clampedVolume
226
- }
256
+ }, [])
227
257
 
228
- const mute = () => {
258
+ const mute = useCallback(() => {
229
259
  const media = mediaRef.current
230
260
  if (!media) return
231
261
 
232
262
  media.muted = true
233
- }
263
+ }, [])
234
264
 
235
- const unmute = () => {
265
+ const unmute = useCallback(() => {
236
266
  const media = mediaRef.current
237
267
  if (!media) return
238
268
 
239
269
  media.muted = false
240
- }
270
+ }, [])
241
271
 
242
- const toggleMute = () => {
272
+ const toggleMute = useCallback(() => {
243
273
  const media = mediaRef.current
244
274
  if (!media) return
245
275
 
246
276
  media.muted = !media.muted
247
- }
277
+ }, [])
248
278
 
249
- const enterFullscreen = async () => {
279
+ const enterFullscreen = useCallback(async () => {
250
280
  const container = containerRef.current
251
281
  if (!container || !document.fullscreenEnabled) return
252
282
 
@@ -256,9 +286,9 @@ export function useMediaPlayer(options: UseMediaPlayerOptions) {
256
286
  } catch (error) {
257
287
  console.error("Failed to enter fullscreen:", error)
258
288
  }
259
- }
289
+ }, [setFullscreen])
260
290
 
261
- const exitFullscreen = async () => {
291
+ const exitFullscreen = useCallback(async () => {
262
292
  if (!document.fullscreenElement) return
263
293
 
264
294
  try {
@@ -267,15 +297,15 @@ export function useMediaPlayer(options: UseMediaPlayerOptions) {
267
297
  } catch (error) {
268
298
  console.error("Failed to exit fullscreen:", error)
269
299
  }
270
- }
300
+ }, [setFullscreen])
271
301
 
272
- const toggleFullscreen = () => {
302
+ const toggleFullscreen = useCallback(() => {
273
303
  if (document.fullscreenElement) {
274
304
  exitFullscreen()
275
305
  } else {
276
306
  enterFullscreen()
277
307
  }
278
- }
308
+ }, [exitFullscreen, enterFullscreen])
279
309
 
280
310
  // Listen for fullscreen changes
281
311
  useEffect(() => {
@@ -294,6 +324,7 @@ export function useMediaPlayer(options: UseMediaPlayerOptions) {
294
324
  pause,
295
325
  toggle,
296
326
  seek,
327
+ setCurrentTime: seek,
297
328
  setVolume: setVolumeControl,
298
329
  mute,
299
330
  unmute,
@@ -18,12 +18,12 @@ export function useMemoryOptimization(
18
18
  monitorMemory = true
19
19
  } = options
20
20
 
21
- const cleanupTimeoutRef = useRef<NodeJS.Timeout>()
21
+ const cleanupTimeoutRef = useRef<ReturnType<typeof setTimeout> | undefined>(undefined)
22
22
  const observersRef = useRef<Set<MutationObserver>>(new Set())
23
23
  const eventListenersRef = useRef<Map<Element, Array<{ event: string; handler: EventListener }>>>(
24
24
  new Map()
25
25
  )
26
- const intervalRefs = useRef<Set<NodeJS.Timeout>>(new Set())
26
+ const intervalRefs = useRef<Set<ReturnType<typeof setInterval>>>(new Set())
27
27
  const animationFrameRefs = useRef<Set<number>>(new Set())
28
28
 
29
29
  // Get current memory usage
@@ -85,11 +85,11 @@ export function useMemoryOptimization(
85
85
 
86
86
  // Safe interval management (must be declared before use)
87
87
  const useInterval = useCallback((callback: () => void, delay: number) => {
88
- const timeoutId = setInterval(callback, delay)
88
+ const timeoutId = globalThis.setInterval(callback, delay)
89
89
  intervalRefs.current.add(timeoutId)
90
90
 
91
91
  return () => {
92
- clearInterval(timeoutId)
92
+ globalThis.clearInterval(timeoutId)
93
93
  intervalRefs.current.delete(timeoutId)
94
94
  }
95
95
  }, [])
@@ -153,23 +153,23 @@ export function useMemoryOptimization(
153
153
  )
154
154
 
155
155
  // Safe timeout management
156
- const setTimeout = useCallback((callback: () => void, delay: number) => {
157
- const timeoutId = setTimeout(callback, delay)
156
+ const useSafeTimeout = useCallback((callback: () => void, delay: number) => {
157
+ const timeoutId = globalThis.setTimeout(callback, delay)
158
158
  intervalRefs.current.add(timeoutId)
159
159
 
160
160
  return () => {
161
- clearTimeout(timeoutId)
161
+ globalThis.clearTimeout(timeoutId)
162
162
  intervalRefs.current.delete(timeoutId)
163
163
  }
164
164
  }, [])
165
165
 
166
166
  // Safe animation frame management
167
- const requestAnimationFrame = useCallback((callback: FrameRequestCallback) => {
168
- const frameId = requestAnimationFrame(callback)
167
+ const useSafeAnimationFrame = useCallback((callback: FrameRequestCallback) => {
168
+ const frameId = globalThis.requestAnimationFrame(callback)
169
169
  animationFrameRefs.current.add(frameId)
170
170
 
171
171
  return () => {
172
- cancelAnimationFrame(frameId)
172
+ globalThis.cancelAnimationFrame(frameId)
173
173
  animationFrameRefs.current.delete(frameId)
174
174
  }
175
175
  }, [])
@@ -201,9 +201,9 @@ export function useMemoryOptimization(
201
201
 
202
202
  // Safe API functions
203
203
  addEventListener,
204
- setInterval,
205
- setTimeout,
206
- requestAnimationFrame,
204
+ setInterval: useInterval,
205
+ setTimeout: useSafeTimeout,
206
+ requestAnimationFrame: useSafeAnimationFrame,
207
207
  addObserver,
208
208
 
209
209
  // Utilities
@@ -230,7 +230,7 @@ export function useMediaResourceCleanup(
230
230
 
231
231
  // Stop media playback
232
232
  if (!media.paused) {
233
- _media.pause()
233
+ media.pause()
234
234
  }
235
235
 
236
236
  // Clear source
@@ -286,7 +286,7 @@ export function usePerformanceMonitoring() {
286
286
 
287
287
  const frameCountRef = useRef(0)
288
288
  const lastFrameTimeRef = useRef(performance.now())
289
- const longTaskObserverRef = useRef<PerformanceObserver>()
289
+ const longTaskObserverRef = useRef<PerformanceObserver | undefined>(undefined)
290
290
 
291
291
  // FPS monitoring
292
292
  useEffect(() => {
@@ -56,8 +56,7 @@ export function useSmartPreload(options: UseSmartPreloadOptions = {}): UseSmartP
56
56
  const preloadPromise = (async () => {
57
57
  try {
58
58
  const response = await fetch(src, {
59
- method: "HEAD",
60
- cache: "preload"
59
+ method: "HEAD"
61
60
  })
62
61
 
63
62
  if (!response.ok) {