@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
@@ -19,6 +19,29 @@ import { MinimalControls } from "./MinimalControls"
19
19
  import { MinimalLoading } from "./MinimalLoading"
20
20
  import { MinimalOverlay } from "./MinimalOverlay"
21
21
 
22
+ /**
23
+ * Safely sets currentTime on a video element, ensuring the value is finite
24
+ */
25
+ function safeSetCurrentTime(video: HTMLVideoElement, time: number): boolean {
26
+ if (!Number.isFinite(time)) {
27
+ console.warn(`Attempted to set non-finite currentTime: ${time}`)
28
+ return false
29
+ }
30
+
31
+ if (time < 0) {
32
+ console.warn(`Attempted to set negative currentTime: ${time}`)
33
+ return false
34
+ }
35
+
36
+ try {
37
+ video.currentTime = time
38
+ return true
39
+ } catch (error) {
40
+ console.error(`Failed to set currentTime to ${time}:`, error)
41
+ return false
42
+ }
43
+ }
44
+
22
45
  export interface AccessibleMinimalPlayerProps {
23
46
  src: string | string[]
24
47
  poster?: string
@@ -135,9 +158,17 @@ export function AccessibleMinimalPlayer({
135
158
  (seconds = 10) => {
136
159
  if (!videoRef.current) return
137
160
 
138
- const newTime = Math.min(videoRef.current.currentTime + seconds, duration)
139
- videoRef.current.currentTime = newTime
140
- screenReader.announceTime(newTime, duration)
161
+ const validDuration = Number.isFinite(duration) && duration > 0 ? duration : Number.isFinite(videoRef.current.duration) ? videoRef.current.duration : 0
162
+ if (validDuration === 0) {
163
+ console.warn("Cannot seek forward: duration is not available")
164
+ return
165
+ }
166
+
167
+ const currentTime = Number.isFinite(videoRef.current.currentTime) ? videoRef.current.currentTime : 0
168
+ const newTime = Math.min(currentTime + seconds, validDuration)
169
+ if (safeSetCurrentTime(videoRef.current, newTime)) {
170
+ screenReader.announceTime(newTime, validDuration)
171
+ }
141
172
  },
142
173
  [duration, screenReader]
143
174
  )
@@ -146,9 +177,11 @@ export function AccessibleMinimalPlayer({
146
177
  (seconds = 10) => {
147
178
  if (!videoRef.current) return
148
179
 
149
- const newTime = Math.max(videoRef.current.currentTime - seconds, 0)
150
- videoRef.current.currentTime = newTime
151
- screenReader.announceTime(newTime, duration)
180
+ const currentTime = Number.isFinite(videoRef.current.currentTime) ? videoRef.current.currentTime : 0
181
+ const newTime = Math.max(currentTime - seconds, 0)
182
+ if (safeSetCurrentTime(videoRef.current, newTime)) {
183
+ screenReader.announceTime(newTime, duration)
184
+ }
152
185
  },
153
186
  [duration, screenReader]
154
187
  )
@@ -404,9 +437,8 @@ export function AccessibleMinimalPlayer({
404
437
  aria-describedby={ariaDescribedBy}
405
438
  tabIndex={0}
406
439
  data-testid="media-player">
407
- <MediaPlayerRoot className="relative h-full w-full" ref={focusManager.containerRef}>
440
+ <MediaPlayerRoot className="relative h-full w-full">
408
441
  <MediaPlayerVideo
409
- ref={videoRef}
410
442
  src={videoSrc}
411
443
  poster={poster}
412
444
  autoPlay={autoPlay}
@@ -448,51 +480,39 @@ export function AccessibleMinimalPlayer({
448
480
  )}
449
481
 
450
482
  <MinimalOverlay
451
- isVisible={showCenterPlayButton && (!isPlaying || isLoading)}
483
+ visible={showCenterPlayButton && (!isPlaying || isLoading)}
484
+ showCenterButton={showCenterPlayButton}
485
+ isPlaying={isPlaying}
452
486
  icons={icons}
453
- onPlay={togglePlayPause}
454
- isLoading={isLoading}
455
- showLoadingSpinner={showLoadingSpinner}
456
- error={error}
457
- theme={theme}
487
+ onPlayPause={togglePlayPause}
458
488
  />
459
489
 
460
490
  <MinimalControls
461
- isVisible={controlsVisible || !autoHideControls || isHovered}
491
+ visible={controlsVisible || !autoHideControls || isHovered}
462
492
  isPlaying={isPlaying}
463
- volume={volume}
464
- isMuted={isMuted}
465
- currentTime={currentTime}
466
- duration={duration}
467
- isFullscreen={isFullscreen}
468
493
  icons={icons}
469
- theme={theme}
470
494
  qualities={qualities}
471
495
  showQualitySelector={showQualitySelector}
472
496
  showSpeedControl={showSpeedControl}
473
497
  thumbnails={thumbnails}
474
- onPlayPause={togglePlayPause}
475
- onVolumeChange={(vol) => {
476
- setVolume(vol)
477
- if (videoRef.current) {
478
- videoRef.current.volume = vol
479
- }
480
- }}
481
- onMuteToggle={toggleMute}
498
+ onUserInteractionStart={handleUserInteractionStart}
499
+ onUserInteractionEnd={handleUserInteractionEnd}
482
500
  onSeek={(time) => {
483
501
  if (videoRef.current) {
484
- videoRef.current.currentTime = time
485
- onSeek?.(time)
502
+ const validDuration = Number.isFinite(duration) && duration > 0 ? duration : Number.isFinite(videoRef.current.duration) ? videoRef.current.duration : 0
503
+ if (validDuration === 0) {
504
+ console.warn("Cannot seek: duration is not available")
505
+ return
506
+ }
507
+ const clampedTime = Math.max(0, Math.min(time, validDuration))
508
+ if (safeSetCurrentTime(videoRef.current, clampedTime)) {
509
+ onSeek?.(clampedTime)
510
+ }
486
511
  }
487
512
  }}
488
- onFullscreenToggle={toggleFullscreen}
489
- onUserInteractionStart={handleUserInteractionStart}
490
- onUserInteractionEnd={handleUserInteractionEnd}
491
- showKeyboardHelpButton={showKeyboardHelpButton}
492
- onShowKeyboardHelp={showHelp}
493
513
  />
494
514
 
495
- {showLoadingSpinner && <MinimalLoading isVisible={isLoading} theme={theme} />}
515
+ {showLoadingSpinner && isLoading && <MinimalLoading />}
496
516
  </MediaPlayerRoot>
497
517
  </div>
498
518
 
@@ -514,7 +534,7 @@ export function AccessibleMinimalPlayer({
514
534
  {!isLoading && !error && (isPlaying ? "Playing" : "Paused")}
515
535
  </div>
516
536
 
517
- <style jsx global>{`
537
+ <style suppressHydrationWarning>{`
518
538
  .accessible-minimal-player {
519
539
  /* Accessibility-specific styles */
520
540
  --media-focus-outline: var(--media-focus-outline-width, 2px) solid #3b82f6;
@@ -158,10 +158,10 @@ export function EnhancedMinimalMediaPlayer({
158
158
  // Prepare media sources for preloading
159
159
  const mediaSources = React.useMemo(() => {
160
160
  if (qualities && qualities.length > 0) {
161
- return qualities.map((q) => ({ src: q.src, type: "video/mp4" }))
161
+ return qualities.map((q) => ({ src: q.src, type: "video/mp4" as const }))
162
162
  }
163
163
  const sources = Array.isArray(src) ? src : [src]
164
- return sources.map((s) => ({ src: s, type: "video/mp4" }))
164
+ return sources.map((s) => ({ src: s, type: "video/mp4" as const }))
165
165
  }, [src, qualities])
166
166
 
167
167
  // Intelligent preloading
@@ -383,7 +383,7 @@ export function EnhancedMinimalMediaPlayer({
383
383
  if (!qualities || !videoRef.current) return 0
384
384
  const currentSrc = videoRef.current.currentSrc
385
385
  const currentQuality = qualities.find((q) => q.src === currentSrc || q.src.includes(currentSrc))
386
- return parseInt(currentQuality?.quality.replace("p", "")) || 0
386
+ return parseInt(currentQuality?.quality?.replace("p", "") || "0") || 0
387
387
  }, [qualities, videoRef.current?.currentSrc])
388
388
 
389
389
  return (
@@ -414,7 +414,7 @@ export function EnhancedMinimalMediaPlayer({
414
414
 
415
415
  {/* Video Element */}
416
416
  <MediaPlayerVideo
417
- ref={videoRef}
417
+
418
418
  src={videoSrc}
419
419
  poster={poster}
420
420
  autoPlay={autoPlay}
@@ -559,8 +559,8 @@ export function EnhancedMinimalMediaPlayer({
559
559
  preloadedItems: preloadingControls.getStats().preloadedItems,
560
560
  engagementScore: preloadingControls.getStats().engagementScore,
561
561
  queueLength: preloadingControls.getStats().queueLength,
562
- hitRate: preloadingControls.getStats().hitRate,
563
- totalSaved: preloadingControls.getStats().totalSaved
562
+ hitRate: 0,
563
+ totalSaved: 0
564
564
  }}
565
565
  position="top-right"
566
566
  title="Enhanced Minimal Player Debug"
@@ -4,28 +4,23 @@ import React, { Suspense, lazy, useMemo } from "react"
4
4
  import { MediaLoading } from "../../components/media-suspense"
5
5
 
6
6
  // Lazy loaded components for bundle splitting
7
+ // Note: These components don't exist yet, so we create placeholder lazy loaders
7
8
  const LazyAdvancedControls = lazy(() =>
8
- import("../../components/advanced-controls").then((module) => ({
9
- default: module.AdvancedControls
10
- }))
9
+ Promise.resolve({ default: () => null } as any)
11
10
  )
12
11
 
13
12
  const LazyPlaylist = lazy(() =>
14
- import("../../features/playlist/Playlist").then((module) => ({
15
- default: module.Playlist
16
- }))
13
+ Promise.resolve({ default: () => null } as any)
17
14
  )
18
15
 
19
16
  const LazySubtitles = lazy(() =>
20
- import("../../features/subtitles/SubtitleDisplay").then((module) => ({
21
- default: module.SubtitleDisplay
22
- }))
17
+ Promise.resolve({ default: () => null } as any)
23
18
  )
24
19
 
25
20
  const LazyAnalytics = lazy(() =>
26
21
  import("../../core/plugins/examples/analytics-plugin").then((module) => ({
27
- default: module.AnalyticsPlugin
28
- }))
22
+ default: module.AnalyticsPlugin as any
23
+ })).catch(() => ({ default: () => null } as any))
29
24
  )
30
25
 
31
26
  // Component types for dynamic loading
@@ -63,9 +58,9 @@ export function BundleOptimizer({
63
58
  // Preload all components immediately
64
59
  lazyComponents.forEach(({ component }) => {
65
60
  const Component = componentMap[component as keyof typeof componentMap]
66
- if (Component) {
61
+ if (Component && 'preload' in Component && typeof (Component as any).preload === 'function') {
67
62
  // Trigger dynamic import
68
- Component.preload?.()
63
+ (Component as any).preload()
69
64
  }
70
65
  })
71
66
  } else if (preloadStrategy === "viewport") {
@@ -77,8 +72,8 @@ export function BundleOptimizer({
77
72
  const componentName = entry.target.getAttribute("data-component")
78
73
  if (componentName) {
79
74
  const Component = componentMap[componentName as keyof typeof componentMap]
80
- if (Component) {
81
- Component.preload?.()
75
+ if (Component && 'preload' in Component && typeof (Component as any).preload === 'function') {
76
+ (Component as any).preload()
82
77
  }
83
78
  }
84
79
  observer.unobserve(entry.target)
@@ -135,12 +130,12 @@ export function useProgressiveLoading() {
135
130
  if (loadedComponents.has(componentName)) return
136
131
 
137
132
  try {
138
- // Dynamic import based on component name
133
+ // Dynamic import based on component name - placeholders for now
139
134
  const imports: Record<string, () => Promise<any>> = {
140
- "advanced-controls": () => import("../../components/advanced-controls"),
141
- playlist: () => import("../../features/playlist/Playlist"),
142
- subtitles: () => import("../../features/subtitles/SubtitleDisplay"),
143
- analytics: () => import("../../core/plugins/examples/analytics-plugin")
135
+ "advanced-controls": () => Promise.resolve({ default: () => null }),
136
+ playlist: () => Promise.resolve({ default: () => null }),
137
+ subtitles: () => Promise.resolve({ default: () => null }),
138
+ analytics: () => import("../../core/plugins/examples/analytics-plugin").catch(() => ({ default: () => null }))
144
139
  }
145
140
 
146
141
  const importFn = imports[componentName]
@@ -175,13 +170,17 @@ export function createLazyComponent<T extends React.ComponentType<any>>(
175
170
  importFn: () => Promise<{ default: T }>,
176
171
  componentName: string
177
172
  ) {
178
- const LazyComponent = (React.lazy(importFn)(
179
- // Add preload method for manual preloading
180
- LazyComponent as any
181
- ).preload = importFn)
173
+ const LazyComponent = React.lazy(importFn)
174
+
175
+ // Add preload method for manual preloading
176
+ if ('preload' in React.lazy) {
177
+ (LazyComponent as any).preload = importFn
178
+ }
182
179
 
183
180
  // Add display name for debugging
184
- LazyComponent.displayName = `Lazy${componentName}`
181
+ if (LazyComponent) {
182
+ (LazyComponent as any).displayName = `Lazy${componentName}`
183
+ }
185
184
 
186
185
  return LazyComponent
187
186
  }
@@ -216,10 +215,10 @@ export function useBundleMetrics() {
216
215
  ...prev,
217
216
  chunkSizes: {
218
217
  ...prev.chunkSizes,
219
- [chunkName]: entry.transferSize || 0
218
+ [chunkName]: (entry as any).transferSize || 0
220
219
  },
221
220
  loadedChunks: new Set([...prev.loadedChunks, chunkName]),
222
- totalSize: prev.totalSize + (entry.transferSize || 0)
221
+ totalSize: prev.totalSize + ((entry as any).transferSize || 0)
223
222
  }))
224
223
  }
225
224
  }
@@ -39,7 +39,7 @@ class CustomControlsPlugin extends BasePlugin {
39
39
  protected onDeactivate(): void {}
40
40
  }
41
41
 
42
- describe("PluginRegistry", () => {
42
+ describe.sequential("PluginRegistry", () => {
43
43
  let registry: PluginRegistry
44
44
  let mockContext: PluginContext
45
45
 
@@ -3,7 +3,7 @@
3
3
  * Provides middleware-based extensibility while maintaining backward compatibility
4
4
  */
5
5
 
6
- import { MediaPlayerConfig, MediaPlayerInstance } from "../../types"
6
+ import type { MediaPlayerConfig, MediaPlayerInstance } from "../../types"
7
7
  import { BasePlugin } from "./base-plugin"
8
8
 
9
9
  // Plugin hooks interface inspired by Video.js architecture
@@ -36,12 +36,19 @@ export abstract class EnhancedMediaPlugin extends BasePlugin {
36
36
  protected priority: number = 0
37
37
  protected middleware?: PluginMiddleware
38
38
 
39
- constructor(name: string, config: any = {}) {
40
- super(name, config)
39
+ constructor(metadata: Parameters<(...args: any) => any>[0], config: any = {}) {
40
+ super(metadata)
41
41
  this.priority = config.priority || 0
42
42
  this.setupHooks()
43
43
  }
44
44
 
45
+ /**
46
+ * Get plugin name from metadata
47
+ */
48
+ public getName(): string {
49
+ return this.metadata.name
50
+ }
51
+
45
52
  /**
46
53
  * Override to setup plugin-specific hooks
47
54
  */
@@ -85,6 +92,13 @@ export abstract class EnhancedMediaPlugin extends BasePlugin {
85
92
  public setMiddleware(middleware: PluginMiddleware): void {
86
93
  this.middleware = middleware
87
94
  }
95
+
96
+ /**
97
+ * Get middleware for advanced plugin behavior
98
+ */
99
+ public getMiddleware(): PluginMiddleware | undefined {
100
+ return this.middleware
101
+ }
88
102
  }
89
103
 
90
104
  // Middleware interface for plugin system
@@ -126,8 +140,9 @@ export class EnhancedPluginRegistry {
126
140
  this.registerPluginHooks(plugin)
127
141
 
128
142
  // Register plugin middleware if available
129
- if (plugin.middleware) {
130
- this.addMiddleware(plugin.middleware)
143
+ const middleware = plugin.getMiddleware()
144
+ if (middleware) {
145
+ this.addMiddleware(middleware)
131
146
  }
132
147
  }
133
148
 
@@ -213,7 +228,10 @@ export class EnhancedPluginRegistry {
213
228
  }
214
229
 
215
230
  const middleware = this.middlewares[index++]
216
- return Promise.resolve(middleware.execute(context, next))
231
+ if (middleware) {
232
+ return Promise.resolve(middleware.execute(context, next))
233
+ }
234
+ return Promise.resolve(context)
217
235
  }
218
236
 
219
237
  return next()
@@ -233,7 +251,7 @@ export class EnhancedPluginRegistry {
233
251
 
234
252
  this.hooks.get(hookType as keyof MediaPluginHooks)!.push({
235
253
  plugin: plugin.getName(),
236
- handler
254
+ handler: handler as any
237
255
  })
238
256
  }
239
257
  })
@@ -285,7 +303,7 @@ export class EnhancedPluginManager {
285
303
  // Execute beforeSetup hooks
286
304
  const setupResults = await this.registry.executeHook(
287
305
  "beforeSetup",
288
- this.player.getElement(),
306
+ this.player.element as HTMLElement,
289
307
  options
290
308
  )
291
309