@therealtinhtute/baroiplayer 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (244) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +384 -0
  3. package/dist/context/media.js +2 -0
  4. package/dist/context/media.js.map +1 -0
  5. package/dist/context/media.mjs +15 -0
  6. package/dist/context/media.mjs.map +1 -0
  7. package/dist/context/store.js +2 -0
  8. package/dist/context/store.js.map +1 -0
  9. package/dist/context/store.mjs +5 -0
  10. package/dist/context/store.mjs.map +1 -0
  11. package/dist/context-BMteAJDN.js +1140 -0
  12. package/dist/context-BMteAJDN.js.map +1 -0
  13. package/dist/context-DCJ_scmO.cjs +23 -0
  14. package/dist/context-DCJ_scmO.cjs.map +1 -0
  15. package/dist/core-events-B5W_j8yU.js +480 -0
  16. package/dist/core-events-B5W_j8yU.js.map +1 -0
  17. package/dist/core-events-CN0-Vhhq.cjs +2 -0
  18. package/dist/core-events-CN0-Vhhq.cjs.map +1 -0
  19. package/dist/core-plugins-Bq9KLany.js +696 -0
  20. package/dist/core-plugins-Bq9KLany.js.map +1 -0
  21. package/dist/core-plugins-CPDSHMiQ.cjs +2 -0
  22. package/dist/core-plugins-CPDSHMiQ.cjs.map +1 -0
  23. package/dist/core.js +37 -0
  24. package/dist/core.js.map +1 -0
  25. package/dist/core.mjs +1217 -0
  26. package/dist/core.mjs.map +1 -0
  27. package/dist/enhanced-components-DHv0MFvG.js +1073 -0
  28. package/dist/enhanced-components-DHv0MFvG.js.map +1 -0
  29. package/dist/enhanced-components-DTIObgB7.cjs +2 -0
  30. package/dist/enhanced-components-DTIObgB7.cjs.map +1 -0
  31. package/dist/hooks-C_r8wexn.cjs +4 -0
  32. package/dist/hooks-C_r8wexn.cjs.map +1 -0
  33. package/dist/hooks-Da2wVXlF.js +1630 -0
  34. package/dist/hooks-Da2wVXlF.js.map +1 -0
  35. package/dist/index.css +1 -0
  36. package/dist/index.js +112 -0
  37. package/dist/index.js.map +1 -0
  38. package/dist/index.mjs +6126 -0
  39. package/dist/index.mjs.map +1 -0
  40. package/dist/minimal-components-DSq5sQpy.js +2002 -0
  41. package/dist/minimal-components-DSq5sQpy.js.map +1 -0
  42. package/dist/minimal-components-DflWiihN.cjs +2 -0
  43. package/dist/minimal-components-DflWiihN.cjs.map +1 -0
  44. package/dist/minimal-components.css +1 -0
  45. package/dist/mona-loading.gif +0 -0
  46. package/dist/performance-monitor-CUW6f5ll.cjs +2 -0
  47. package/dist/performance-monitor-CUW6f5ll.cjs.map +1 -0
  48. package/dist/performance-monitor-rMW1RgV3.js +302 -0
  49. package/dist/performance-monitor-rMW1RgV3.js.map +1 -0
  50. package/dist/utils-BnscpYYd.js +2798 -0
  51. package/dist/utils-BnscpYYd.js.map +1 -0
  52. package/dist/utils-D_czS_0K.cjs +9 -0
  53. package/dist/utils-D_czS_0K.cjs.map +1 -0
  54. package/dist/utils.js +2 -0
  55. package/dist/utils.js.map +1 -0
  56. package/dist/utils.mjs +7 -0
  57. package/dist/utils.mjs.map +1 -0
  58. package/package.json +114 -0
  59. package/src/adapters/videojs-adapter.tsx +470 -0
  60. package/src/assets/mona-loading.gif +0 -0
  61. package/src/components/AnimateLoading.tsx +115 -0
  62. package/src/components/LoadingState.tsx +248 -0
  63. package/src/components/accessibility/keyboard-help-overlay.tsx +213 -0
  64. package/src/components/audio-player.tsx +85 -0
  65. package/src/components/basic/index.ts +37 -0
  66. package/src/components/enhanced/advanced-features.tsx +316 -0
  67. package/src/components/enhanced/debug-panel.tsx +422 -0
  68. package/src/components/enhanced/enhanced-controls.tsx +600 -0
  69. package/src/components/enhanced/enhanced-media-player.tsx +509 -0
  70. package/src/components/enhanced/error-handling.tsx +345 -0
  71. package/src/components/enhanced/index.ts +39 -0
  72. package/src/components/enhanced/media-player-enhanced.tsx +349 -0
  73. package/src/components/examples/basic-video-player.tsx +55 -0
  74. package/src/components/examples/custom-controls-example.tsx +63 -0
  75. package/src/components/examples/enhanced-controls-example.tsx +114 -0
  76. package/src/components/examples/enhanced-media-player-example.tsx +293 -0
  77. package/src/components/examples/loading-states-demo.tsx +142 -0
  78. package/src/components/examples/react-19-demo.tsx +381 -0
  79. package/src/components/examples/volume-orientation-demo.tsx +114 -0
  80. package/src/components/icon-demo.tsx +209 -0
  81. package/src/components/icons/index.tsx +166 -0
  82. package/src/components/icons/svg-icons/ArrowLeftIcon.tsx +18 -0
  83. package/src/components/icons/svg-icons/ArrowRightIcon.tsx +18 -0
  84. package/src/components/icons/svg-icons/AudioIcon.tsx +22 -0
  85. package/src/components/icons/svg-icons/BackwardIcon.tsx +12 -0
  86. package/src/components/icons/svg-icons/CameraIcon.tsx +19 -0
  87. package/src/components/icons/svg-icons/CheckIcon.tsx +19 -0
  88. package/src/components/icons/svg-icons/EnablePiPIcon.tsx +17 -0
  89. package/src/components/icons/svg-icons/ForwardIcon.tsx +12 -0
  90. package/src/components/icons/svg-icons/FullscreenEnterIcon.tsx +23 -0
  91. package/src/components/icons/svg-icons/FullscreenExitIcon.tsx +23 -0
  92. package/src/components/icons/svg-icons/LoadingIcon.tsx +32 -0
  93. package/src/components/icons/svg-icons/PauseIcon.tsx +23 -0
  94. package/src/components/icons/svg-icons/PlayIcon.tsx +21 -0
  95. package/src/components/icons/svg-icons/PlaybackSpeedIcon.tsx +23 -0
  96. package/src/components/icons/svg-icons/QualityIcon.tsx +21 -0
  97. package/src/components/icons/svg-icons/SettingsIcon.tsx +12 -0
  98. package/src/components/icons/svg-icons/SliderIcon.tsx +21 -0
  99. package/src/components/icons/svg-icons/SubtitleIcon.tsx +21 -0
  100. package/src/components/icons/svg-icons/SubtitleOffIcon.tsx +25 -0
  101. package/src/components/icons/svg-icons/UnPiPIcon.tsx +17 -0
  102. package/src/components/icons/svg-icons/VolumeMutedIcon.tsx +16 -0
  103. package/src/components/icons/svg-icons/VolumeOneIcon.tsx +22 -0
  104. package/src/components/icons/svg-icons/VolumeThreeIcon.tsx +16 -0
  105. package/src/components/icons/svg-icons/VolumeTwoIcon.tsx +16 -0
  106. package/src/components/legacy.tsx +93 -0
  107. package/src/components/media-metadata-display.tsx +100 -0
  108. package/src/components/media-player-audio.tsx +39 -0
  109. package/src/components/media-player-controls.tsx +226 -0
  110. package/src/components/media-player-error.tsx +163 -0
  111. package/src/components/media-player-fullscreen.tsx +109 -0
  112. package/src/components/media-player-hls.tsx +111 -0
  113. package/src/components/media-player-loading.tsx +76 -0
  114. package/src/components/media-player-play.tsx +75 -0
  115. package/src/components/media-player-root.tsx +340 -0
  116. package/src/components/media-player-seek.tsx +168 -0
  117. package/src/components/media-player-time.tsx +99 -0
  118. package/src/components/media-player-tooltip.tsx +58 -0
  119. package/src/components/media-player-video.tsx +39 -0
  120. package/src/components/media-player-volume.tsx +155 -0
  121. package/src/components/media-player.tsx +257 -0
  122. package/src/components/media-suspense.tsx +362 -0
  123. package/src/components/player-settings-form.tsx +163 -0
  124. package/src/components/react-player-wrapper.tsx +651 -0
  125. package/src/components/simple-video-player.tsx +60 -0
  126. package/src/components/ui/enhanced-seek-bar.tsx +235 -0
  127. package/src/components/ui/fullscreen-button.tsx +60 -0
  128. package/src/components/ui/media-resource-preloader.tsx +395 -0
  129. package/src/components/ui/play-button.tsx +107 -0
  130. package/src/components/ui/seek-bar.tsx +161 -0
  131. package/src/components/ui/time-display.tsx +40 -0
  132. package/src/components/ui/volume-control.tsx +143 -0
  133. package/src/components/unified/base-media-player.tsx +246 -0
  134. package/src/components/unified/media-player.tsx +75 -0
  135. package/src/components/variants/minimal/AccessibleMinimalPlayer.tsx +561 -0
  136. package/src/components/variants/minimal/EnhancedMinimalMediaPlayer.tsx +552 -0
  137. package/src/components/variants/minimal/MinimalControls.tsx +142 -0
  138. package/src/components/variants/minimal/MinimalLoading.tsx +32 -0
  139. package/src/components/variants/minimal/MinimalMediaPlayer.tsx +315 -0
  140. package/src/components/variants/minimal/MinimalOverlay.tsx +64 -0
  141. package/src/components/variants/minimal/MinimalPlayButton.tsx +65 -0
  142. package/src/components/variants/minimal/MinimalSeekBar.tsx +150 -0
  143. package/src/components/variants/minimal/MinimalVolumeControl.tsx +80 -0
  144. package/src/components/variants/minimal/SimpleMinimalPlayer.tsx +210 -0
  145. package/src/components/variants/minimal/components/CenterPlayButton.tsx +22 -0
  146. package/src/components/variants/minimal/components/ControlBar.tsx +146 -0
  147. package/src/components/variants/minimal/components/EnhancedSettingsMenu.tsx +361 -0
  148. package/src/components/variants/minimal/components/PlaybackControls.tsx +51 -0
  149. package/src/components/variants/minimal/components/RightControls.tsx +173 -0
  150. package/src/components/variants/minimal/components/SeekBar.tsx +131 -0
  151. package/src/components/variants/minimal/components/SettingsDropdown.tsx +70 -0
  152. package/src/components/variants/minimal/components/SubtitleDisplay.tsx +21 -0
  153. package/src/components/variants/minimal/components/TimeDisplay.tsx +18 -0
  154. package/src/components/variants/minimal/components/VideoElement.tsx +40 -0
  155. package/src/components/variants/minimal/components/VolumeControl.tsx +217 -0
  156. package/src/components/variants/minimal/index.ts +23 -0
  157. package/src/components/video-player.tsx +100 -0
  158. package/src/context/error-handling.ts +394 -0
  159. package/src/context/media-context.tsx +208 -0
  160. package/src/context/media-provider.tsx +265 -0
  161. package/src/context/media-store.ts +379 -0
  162. package/src/context/persistence.ts +453 -0
  163. package/src/core/events/event-system.ts +442 -0
  164. package/src/core/events/hooks.ts +302 -0
  165. package/src/core/events/index.ts +22 -0
  166. package/src/core/events/middleware.ts +269 -0
  167. package/src/core/index.ts +22 -0
  168. package/src/core/optimization/bundle-optimizer.tsx +308 -0
  169. package/src/core/performance/cicd.ts +96 -0
  170. package/src/core/performance/index.ts +38 -0
  171. package/src/core/performance/media-loading-optimizer.ts +538 -0
  172. package/src/core/performance/performance-budgets.ts +432 -0
  173. package/src/core/performance/performance-monitor.tsx +476 -0
  174. package/src/core/performance/performance-testing.ts +459 -0
  175. package/src/core/plugins/base-plugin.ts +224 -0
  176. package/src/core/plugins/enhanced-plugin-system.ts +411 -0
  177. package/src/core/plugins/examples/analytics-plugin.ts +242 -0
  178. package/src/core/plugins/index.ts +39 -0
  179. package/src/core/plugins/registry.ts +327 -0
  180. package/src/core/plugins/types.ts +159 -0
  181. package/src/core/plugins/utils.ts +316 -0
  182. package/src/core/progressive-loading.tsx +322 -0
  183. package/src/core/providers/enhanced-hls-provider.ts +339 -0
  184. package/src/core/providers/service-provider.tsx +168 -0
  185. package/src/core/utils.tsx +38 -0
  186. package/src/features/media-session/media-session-manager.ts +429 -0
  187. package/src/features/picture-in-picture/pip-manager.ts +429 -0
  188. package/src/features/playlist/playlist-manager.ts +606 -0
  189. package/src/features/subtitles/subtitle-manager.ts +441 -0
  190. package/src/features/subtitles/subtitle-parser.ts +280 -0
  191. package/src/features/thumbnails/thumbnail-generator.ts +519 -0
  192. package/src/hooks/accessibility/index.ts +27 -0
  193. package/src/hooks/accessibility/use-accessibility-preferences.ts +188 -0
  194. package/src/hooks/accessibility/use-focus-management.ts +176 -0
  195. package/src/hooks/accessibility/use-keyboard-shortcuts.ts +144 -0
  196. package/src/hooks/accessibility/use-screen-reader.ts +182 -0
  197. package/src/hooks/core/index.ts +47 -0
  198. package/src/hooks/legacy.ts +27 -0
  199. package/src/hooks/use-enhanced-media-player.ts +468 -0
  200. package/src/hooks/use-intelligent-preloading.ts +358 -0
  201. package/src/hooks/use-keyboard-controls.ts +115 -0
  202. package/src/hooks/use-media-actions.ts +175 -0
  203. package/src/hooks/use-media-metadata.ts +89 -0
  204. package/src/hooks/use-media-optimistic.ts +270 -0
  205. package/src/hooks/use-media-player.ts +312 -0
  206. package/src/hooks/use-media-state.ts +190 -0
  207. package/src/hooks/use-memory-optimization.ts +358 -0
  208. package/src/hooks/use-optimistic-controls.ts +92 -0
  209. package/src/hooks/use-optimized-media.ts +263 -0
  210. package/src/hooks/use-touch-gestures.ts +417 -0
  211. package/src/hooks/useEnhancedSubtitles.ts +258 -0
  212. package/src/hooks/useKeyboardShortcuts.ts +74 -0
  213. package/src/hooks/useMediaControls.ts +52 -0
  214. package/src/hooks/useScreenshot.ts +57 -0
  215. package/src/hooks/useSubtitles.ts +91 -0
  216. package/src/hooks/useVideoPlayer.ts +232 -0
  217. package/src/icons/components/FullscreenIcon.tsx +37 -0
  218. package/src/icons/components/SettingsIcon.tsx +38 -0
  219. package/src/icons/components/animated/LoadingIcon.tsx +43 -0
  220. package/src/icons/components/animated/PlayPauseIcon.tsx +50 -0
  221. package/src/icons/components/media/PauseIcon.tsx +39 -0
  222. package/src/icons/components/media/PlayIcon.tsx +38 -0
  223. package/src/icons/components/volume/VolumeIcon.tsx +39 -0
  224. package/src/icons/components/volume/VolumeOffIcon.tsx +39 -0
  225. package/src/icons/index.ts +56 -0
  226. package/src/icons/provider.tsx +64 -0
  227. package/src/icons/types.ts +40 -0
  228. package/src/index-enhanced.ts +35 -0
  229. package/src/index-legacy.ts +326 -0
  230. package/src/index.css +102 -0
  231. package/src/index.ts +216 -0
  232. package/src/lib/deprecation.ts +51 -0
  233. package/src/lib/format-time.ts +37 -0
  234. package/src/lib/index.ts +3 -0
  235. package/src/lib/utils.ts +6 -0
  236. package/src/styles/globals.css +462 -0
  237. package/src/styles/minimal-player.css +589 -0
  238. package/src/styles/pixel-art.css +46 -0
  239. package/src/styles/videojs.css +100 -0
  240. package/src/test/setup.ts +79 -0
  241. package/src/types/events.ts +47 -0
  242. package/src/types/formats.ts +95 -0
  243. package/src/types/index.ts +190 -0
  244. package/src/types/videojs-plugins.d.ts +14 -0
@@ -0,0 +1,470 @@
1
+ "use client"
2
+
3
+ import React, { useEffect, useRef, useState, useCallback } from "react"
4
+ import "../styles/videojs.css"
5
+ import type VideoJsPlayer from "video.js/dist/types/player"
6
+ import { MediaActionTypes, useMediaContext, useMediaDispatch } from "../context/media-context"
7
+ import { cn } from "../lib/utils"
8
+
9
+ // Lazy load Video.js and HLS.js for better performance
10
+ let videojs: any = null
11
+ let Hls: any = null
12
+
13
+ async function loadVideoJS() {
14
+ if (!videojs) {
15
+ const [videojsModule, hlsModule] = await Promise.all([
16
+ import("video.js"),
17
+ import("hls.js")
18
+ ])
19
+ videojs = videojsModule.default
20
+ Hls = hlsModule.default
21
+
22
+ // Import plugins after Video.js is loaded
23
+ await Promise.all([
24
+ // @ts-ignore
25
+ import("@videojs/http-streaming"),
26
+ // @ts-ignore
27
+ import("videojs-contrib-quality-levels"),
28
+ // @ts-ignore
29
+ import("videojs-hls-quality-selector")
30
+ ])
31
+ }
32
+ return { videojs, Hls }
33
+ }
34
+
35
+ export interface VideoSource {
36
+ src: string
37
+ type: string
38
+ label?: string
39
+ selected?: boolean
40
+ }
41
+
42
+ export interface TextTrack {
43
+ src: string
44
+ kind: "subtitles" | "captions" | "descriptions" | "chapters" | "metadata"
45
+ srclang: string
46
+ label: string
47
+ default?: boolean
48
+ }
49
+
50
+ export interface VideoJSAdapterProps {
51
+ sources: VideoSource[]
52
+ poster?: string
53
+ textTracks?: TextTrack[]
54
+ autoplay?: boolean
55
+ muted?: boolean
56
+ controls?: boolean
57
+ preload?: "auto" | "metadata" | "none"
58
+ fluid?: boolean
59
+ responsive?: boolean
60
+ playbackRates?: number[]
61
+ className?: string
62
+ onReady?: (player: VideoJsPlayer) => void
63
+ onError?: (error: any) => void
64
+ hlsConfig?: any // HLS.js config
65
+ enableQualitySelector?: boolean
66
+ enableSubtitles?: boolean
67
+ }
68
+
69
+ export function VideoJSAdapter({
70
+ sources,
71
+ poster,
72
+ textTracks = [],
73
+ autoplay = false,
74
+ muted = false,
75
+ controls = false,
76
+ preload = "metadata",
77
+ fluid = true,
78
+ responsive = true,
79
+ playbackRates = [0.5, 0.75, 1, 1.25, 1.5, 2],
80
+ className,
81
+ onReady,
82
+ onError,
83
+ hlsConfig = {},
84
+ enableQualitySelector = true,
85
+ enableSubtitles = true
86
+ }: VideoJSAdapterProps) {
87
+ const videoRef = useRef<HTMLVideoElement>(null)
88
+ const playerRef = useRef<VideoJsPlayer | null>(null)
89
+ const hlsRef = useRef<any | null>(null) // HLS.js instance
90
+ const [isLoading, setIsLoading] = useState(true)
91
+ const [error, setError] = useState<string | null>(null)
92
+
93
+ const context = useMediaContext()
94
+ const dispatch = useMediaDispatch()
95
+
96
+ // Initialize Video.js player
97
+ const initializePlayer = async () => {
98
+ if (!videoRef.current || playerRef.current) return
99
+
100
+ try {
101
+ setIsLoading(true)
102
+ const { videojs, Hls: HlsLib } = await loadVideoJS()
103
+
104
+ // Check if we need HLS.js (for non-Safari browsers)
105
+ const needsHlsJs = sources.some(s => s.type === "application/x-mpegURL") &&
106
+ !videoRef.current.canPlayType("application/vnd.apple.mpegurl")
107
+
108
+ // Video.js options
109
+ const options: any = {
110
+ autoplay,
111
+ muted,
112
+ controls,
113
+ preload,
114
+ fluid,
115
+ responsive,
116
+ playbackRates,
117
+ sources: needsHlsJs ? [] : sources, // Don't set sources if using HLS.js
118
+ poster,
119
+ html5: {
120
+ vhs: {
121
+ // Video.js HTTP Streaming options
122
+ overrideNative: !videoRef.current.canPlayType("application/vnd.apple.mpegurl"),
123
+ enableLowInitialPlaylist: true,
124
+ fastQualityChange: true,
125
+ handleManifestRedirects: true
126
+ }
127
+ }
128
+ }
129
+
130
+ // Create Video.js player
131
+ const player = videojs(videoRef.current, options, function() {
132
+ console.log("Video.js player ready")
133
+ }) as VideoJsPlayer
134
+
135
+ playerRef.current = player
136
+
137
+ // Add text tracks
138
+ textTracks.forEach(track => {
139
+ player.addRemoteTextTrack({
140
+ kind: track.kind,
141
+ src: track.src,
142
+ srclang: track.srclang,
143
+ label: track.label,
144
+ default: track.default
145
+ }, false)
146
+ })
147
+
148
+ // Setup HLS.js for non-Safari browsers if needed
149
+ if (needsHlsJs) {
150
+ const hlsSource = sources.find(s => s.type === "application/x-mpegURL")
151
+ if (hlsSource) {
152
+ const hls = new HlsLib({
153
+ debug: false,
154
+ enableWorker: true,
155
+ lowLatencyMode: false,
156
+ backBufferLength: 30,
157
+ maxBufferLength: 30,
158
+ maxMaxBufferLength: 600,
159
+ maxBufferSize: 60 * 1000 * 1000,
160
+ maxBufferHole: 0.1,
161
+ highBufferWatchdogPeriod: 2,
162
+ // ABR settings
163
+ abrEwmaFastLive: 3.0,
164
+ abrEwmaSlowLive: 9.0,
165
+ abrEwmaFastVoD: 3.0,
166
+ abrEwmaSlowVoD: 9.0,
167
+ abrEwmaDefaultEstimate: 500000,
168
+ abrBandWidthFactor: 0.95,
169
+ abrBandWidthUpFactor: 0.7,
170
+ abrMaxWithRealBitrate: false,
171
+ maxStarvationDelay: 4,
172
+ maxLoadingDelay: 4,
173
+ capLevelToPlayerSize: true,
174
+ capLevelOnFPSDrop: true,
175
+ // Subtitle settings
176
+ renderTextTracksNatively: false,
177
+ subtitlePreference: {
178
+ lang: navigator.language.split("-")[0],
179
+ forced: false
180
+ },
181
+ ...hlsConfig
182
+ })
183
+
184
+ hlsRef.current = hls
185
+
186
+ // Attach HLS to video element
187
+ hls.loadSource(hlsSource.src)
188
+ hls.attachMedia(videoRef.current)
189
+
190
+ // HLS.js event handlers
191
+ hls.on(HlsLib.Events.MANIFEST_PARSED, () => {
192
+ console.log("HLS manifest parsed", {
193
+ levels: hls.levels.map((l: any) => ({
194
+ height: l.height,
195
+ bitrate: l.bitrate,
196
+ codec: l.codecSet
197
+ }))
198
+ })
199
+
200
+ // Add quality levels to Video.js
201
+ if (enableQualitySelector && (player as any).qualityLevels) {
202
+ const qualityLevels = (player as any).qualityLevels()
203
+
204
+ hls.levels.forEach((level: any, index: number) => {
205
+ qualityLevels.addQualityLevel({
206
+ id: index.toString(),
207
+ label: `${level.height}p`,
208
+ width: level.width,
209
+ height: level.height,
210
+ bitrate: level.bitrate,
211
+ enabled: true
212
+ })
213
+ })
214
+ }
215
+ })
216
+
217
+ hls.on(HlsLib.Events.LEVEL_SWITCHED, (_event: any, data: any) => {
218
+ console.log(`HLS quality switched to level ${data.level}`)
219
+ dispatch({
220
+ type: MediaActionTypes.SET_QUALITY_LEVEL,
221
+ detail: data.level
222
+ })
223
+ })
224
+
225
+ hls.on(HlsLib.Events.ERROR, (_event: any, data: any) => {
226
+ if (data.fatal) {
227
+ switch (data.type) {
228
+ case HlsLib.ErrorTypes.NETWORK_ERROR:
229
+ console.error("Fatal network error encountered, try to recover")
230
+ hls.startLoad()
231
+ break
232
+ case HlsLib.ErrorTypes.MEDIA_ERROR:
233
+ console.error("Fatal media error encountered, try to recover")
234
+ hls.recoverMediaError()
235
+ break
236
+ default:
237
+ console.error("Fatal error, cannot recover")
238
+ setError("Streaming error occurred")
239
+ onError?.(data)
240
+ hls.destroy()
241
+ break
242
+ }
243
+ }
244
+ })
245
+
246
+ // Update bandwidth estimate in context
247
+ hls.on(HlsLib.Events.FRAG_LOADED, () => {
248
+ const estimate = hls.bandwidthEstimate
249
+ if (estimate) {
250
+ dispatch({
251
+ type: MediaActionTypes.SET_BANDWIDTH_ESTIMATE,
252
+ detail: estimate
253
+ })
254
+ }
255
+ })
256
+ }
257
+ }
258
+
259
+ // Enable quality selector plugin
260
+ if (enableQualitySelector && (player as any).qualityLevels) {
261
+ ;(player as any).hlsQualitySelector({
262
+ displayCurrentQuality: true
263
+ })
264
+ }
265
+
266
+ // Video.js event handlers to sync with MediaContext
267
+ player.on("play", () => {
268
+ dispatch({ type: MediaActionTypes.PLAY })
269
+ })
270
+
271
+ player.on("pause", () => {
272
+ dispatch({ type: MediaActionTypes.PAUSE })
273
+ })
274
+
275
+ player.on("timeupdate", () => {
276
+ dispatch({
277
+ type: MediaActionTypes.SET_CURRENT_TIME,
278
+ detail: player.currentTime() || 0
279
+ })
280
+ })
281
+
282
+ player.on("durationchange", () => {
283
+ dispatch({
284
+ type: MediaActionTypes.SET_DURATION,
285
+ detail: player.duration() || 0
286
+ })
287
+ })
288
+
289
+ player.on("volumechange", () => {
290
+ dispatch({
291
+ type: MediaActionTypes.SET_VOLUME,
292
+ detail: player.volume() || 0
293
+ })
294
+ dispatch({
295
+ type: player.muted() ? MediaActionTypes.MUTE : MediaActionTypes.UNMUTE
296
+ })
297
+ })
298
+
299
+ player.on("ended", () => {
300
+ dispatch({ type: MediaActionTypes.PAUSE })
301
+ })
302
+
303
+ player.on("error", (e: any) => {
304
+ const error = player.error()
305
+ setError(error?.message || "An error occurred")
306
+ dispatch({
307
+ type: MediaActionTypes.SET_ERROR,
308
+ detail: error?.message || "Playback error"
309
+ })
310
+ onError?.(error)
311
+ })
312
+
313
+ player.on("loadstart", () => {
314
+ dispatch({ type: MediaActionTypes.SET_LOADING, detail: true })
315
+ })
316
+
317
+ player.on("loadeddata", () => {
318
+ setIsLoading(false)
319
+ dispatch({ type: MediaActionTypes.SET_LOADING, detail: false })
320
+ })
321
+
322
+ player.on("waiting", () => {
323
+ dispatch({ type: MediaActionTypes.SET_BUFFERING, detail: true })
324
+ })
325
+
326
+ player.on("playing", () => {
327
+ dispatch({ type: MediaActionTypes.SET_BUFFERING, detail: false })
328
+ })
329
+
330
+ // Text track changes
331
+ player.on("texttrackchange", () => {
332
+ const tracks = player.textTracks() as any
333
+ const activeTracks: any[] = []
334
+
335
+ if (tracks && tracks.length) {
336
+ for (let i = 0; i < tracks.length; i++) {
337
+ const track = tracks[i]
338
+ if (track.mode === "showing") {
339
+ activeTracks.push({
340
+ id: track.id,
341
+ label: track.label,
342
+ language: track.language,
343
+ kind: track.kind
344
+ })
345
+ }
346
+ }
347
+ }
348
+
349
+ dispatch({
350
+ type: MediaActionTypes.SET_ACTIVE_TEXT_TRACKS,
351
+ detail: activeTracks
352
+ })
353
+ })
354
+
355
+ // Update media ref in context for gesture/keyboard controls
356
+ if (context.mediaRef) {
357
+ context.mediaRef.current = videoRef.current
358
+ }
359
+
360
+ onReady?.(player)
361
+ setIsLoading(false)
362
+
363
+ } catch (err) {
364
+ console.error("Failed to initialize Video.js player:", err)
365
+ setError("Failed to initialize player")
366
+ setIsLoading(false)
367
+ onError?.(err)
368
+ }
369
+ }
370
+
371
+ // Cleanup on unmount
372
+ useEffect(() => {
373
+ initializePlayer()
374
+
375
+ return () => {
376
+ if (hlsRef.current) {
377
+ hlsRef.current.destroy()
378
+ hlsRef.current = null
379
+ }
380
+
381
+ if (playerRef.current) {
382
+ playerRef.current.dispose()
383
+ playerRef.current = null
384
+ }
385
+ }
386
+ }, [initializePlayer])
387
+
388
+ // Expose player methods to MediaContext
389
+ useEffect(() => {
390
+ if (!playerRef.current || !context.mediaRef) return
391
+
392
+ // Create a proxy object that bridges Video.js methods to native HTMLVideoElement methods
393
+ const proxyMethods = {
394
+ play: () => playerRef.current?.play(),
395
+ pause: () => playerRef.current?.pause(),
396
+ seekTo: (time: number) => {
397
+ if (playerRef.current) {
398
+ playerRef.current.currentTime(time)
399
+ }
400
+ },
401
+ setVolume: (volume: number) => {
402
+ if (playerRef.current) {
403
+ playerRef.current.volume(volume)
404
+ }
405
+ },
406
+ setMuted: (muted: boolean) => {
407
+ if (playerRef.current) {
408
+ playerRef.current.muted(muted)
409
+ }
410
+ },
411
+ setQualityLevel: (level: number) => {
412
+ if (hlsRef.current) {
413
+ hlsRef.current.currentLevel = level
414
+ }
415
+ },
416
+ getQualityLevels: () => {
417
+ if (hlsRef.current) {
418
+ return hlsRef.current.levels
419
+ }
420
+ return []
421
+ },
422
+ getCurrentQualityLevel: () => {
423
+ if (hlsRef.current) {
424
+ return hlsRef.current.currentLevel
425
+ }
426
+ return -1
427
+ },
428
+ getBandwidthEstimate: () => {
429
+ if (hlsRef.current) {
430
+ return hlsRef.current.bandwidthEstimate
431
+ }
432
+ return 0
433
+ }
434
+ }
435
+
436
+ // Attach proxy methods to video element for external access
437
+ if (videoRef.current) {
438
+ Object.assign(videoRef.current, proxyMethods)
439
+ }
440
+ }, [context.mediaRef])
441
+
442
+ return (
443
+ <div className={cn("relative h-full w-full", className)}>
444
+ <video
445
+ ref={videoRef}
446
+ className="video-js vjs-default-skin vjs-big-play-centered h-full w-full"
447
+ playsInline
448
+ />
449
+
450
+ {isLoading && (
451
+ <div className="absolute inset-0 flex items-center justify-center bg-black/50">
452
+ <div className="text-white">
453
+ <div className="h-8 w-8 animate-spin rounded-full border-white border-b-2" />
454
+ </div>
455
+ </div>
456
+ )}
457
+
458
+ {error && (
459
+ <div className="absolute inset-0 flex items-center justify-center bg-black/80">
460
+ <div className="text-center text-white">
461
+ <p className="font-semibold text-lg">Error</p>
462
+ <p className="text-sm opacity-80">{error}</p>
463
+ </div>
464
+ </div>
465
+ )}
466
+ </div>
467
+ )
468
+ }
469
+
470
+ VideoJSAdapter.displayName = "VideoJSAdapter"
Binary file
@@ -0,0 +1,115 @@
1
+ "use client"
2
+
3
+ import React, { useState, useEffect } from "react"
4
+ import { cn } from "../lib/utils"
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
11
+ import monaGifSrc from "../assets/mona-loading.gif"
12
+
13
+ try {
14
+ monaLoadingGif = monaGifSrc
15
+ } catch {
16
+ // GIF import failed, will use fallback
17
+ monaLoadingGif = undefined
18
+ }
19
+
20
+ interface AnimateLoadingProps extends React.HTMLAttributes<HTMLDivElement> {
21
+ loadingText?: string
22
+ gifSrc?: string
23
+ showFallback?: boolean
24
+ }
25
+
26
+ export function AnimateLoading({
27
+ loadingText = "Loading...",
28
+ gifSrc,
29
+ showFallback = true,
30
+ className,
31
+ ...props
32
+ }: AnimateLoadingProps) {
33
+ const [imageError, setImageError] = useState(false)
34
+ const [imageLoaded, setImageLoaded] = useState(false)
35
+
36
+ // Try multiple paths for the GIF
37
+ const gifPaths = [
38
+ gifSrc,
39
+ monaLoadingGif,
40
+ "/mona-loading.gif",
41
+ "./mona-loading.gif",
42
+ "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" // 1x1 transparent gif as last resort
43
+ ].filter(Boolean) as string[]
44
+
45
+ const [currentGifIndex, setCurrentGifIndex] = useState(0)
46
+ const currentGif = gifPaths[currentGifIndex]
47
+
48
+ const handleImageError = () => {
49
+ if (currentGifIndex < gifPaths.length - 1) {
50
+ setCurrentGifIndex(currentGifIndex + 1)
51
+ } else {
52
+ setImageError(true)
53
+ }
54
+ }
55
+
56
+ // Enhanced fallback CSS animation
57
+ const FallbackAnimation = () => (
58
+ <div className="relative h-12 w-12">
59
+ {/* Outer spinning gradient ring */}
60
+ <div className="absolute inset-0 animate-spin rounded-full bg-gradient-to-r from-cyan-400 via-blue-500 to-cyan-400 opacity-75" />
61
+
62
+ {/* Inner background */}
63
+ <div className="absolute inset-1 rounded-full bg-black/90" />
64
+
65
+ {/* Center spinning element */}
66
+ <div className="absolute inset-0 flex items-center justify-center">
67
+ <div className="relative">
68
+ <div className="h-8 w-8 rounded-full border-2 border-white/20" />
69
+ <div className="absolute inset-0 h-8 w-8 animate-spin rounded-full border-2 border-transparent border-t-cyan-400" />
70
+
71
+ {/* Pulsing dot in center */}
72
+ <div className="absolute inset-0 flex items-center justify-center">
73
+ <div className="h-2 w-2 animate-pulse rounded-full bg-cyan-400" />
74
+ </div>
75
+ </div>
76
+ </div>
77
+ </div>
78
+ )
79
+
80
+ return (
81
+ <div
82
+ className={cn(
83
+ "flex flex-col items-center justify-center space-y-2 rounded py-2 text-white",
84
+ className
85
+ )}
86
+ {...props}
87
+ >
88
+ {/* Show fallback while image is loading or if error */}
89
+ {(!imageLoaded || imageError) && showFallback && (
90
+ <FallbackAnimation />
91
+ )}
92
+
93
+ {/* Try to load the actual GIF (hidden if not loaded) */}
94
+ {!imageError && currentGif && (
95
+ <img
96
+ alt="loading"
97
+ height={50}
98
+ width={50}
99
+ src={currentGif}
100
+ onError={handleImageError}
101
+ onLoad={() => setImageLoaded(true)}
102
+ className={cn(
103
+ "object-contain",
104
+ !imageLoaded && "hidden"
105
+ )}
106
+ style={{ display: imageLoaded ? 'block' : 'none' }}
107
+ />
108
+ )}
109
+
110
+ <span className="mt-2 text-sm tracking-wider">
111
+ {loadingText}
112
+ </span>
113
+ </div>
114
+ )
115
+ }