@therealtinhtute/baroiplayer 1.2.0 → 2.0.0-beta.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 (305) hide show
  1. package/README.md +91 -73
  2. package/dist/components/icons/index.d.ts +36 -0
  3. package/dist/components/icons/index.d.ts.map +1 -0
  4. package/dist/components/icons/svg-icons/ArrowLeftIcon.d.ts +4 -0
  5. package/dist/components/icons/svg-icons/ArrowLeftIcon.d.ts.map +1 -0
  6. package/dist/components/icons/svg-icons/ArrowRightIcon.d.ts +5 -0
  7. package/dist/components/icons/svg-icons/ArrowRightIcon.d.ts.map +1 -0
  8. package/dist/components/icons/svg-icons/AudioIcon.d.ts +5 -0
  9. package/dist/components/icons/svg-icons/AudioIcon.d.ts.map +1 -0
  10. package/dist/components/icons/svg-icons/BackwardIcon.d.ts +5 -0
  11. package/dist/components/icons/svg-icons/BackwardIcon.d.ts.map +1 -0
  12. package/dist/components/icons/svg-icons/CameraIcon.d.ts +5 -0
  13. package/dist/components/icons/svg-icons/CameraIcon.d.ts.map +1 -0
  14. package/dist/components/icons/svg-icons/CheckIcon.d.ts +5 -0
  15. package/dist/components/icons/svg-icons/CheckIcon.d.ts.map +1 -0
  16. package/dist/components/icons/svg-icons/EnablePiPIcon.d.ts +4 -0
  17. package/dist/components/icons/svg-icons/EnablePiPIcon.d.ts.map +1 -0
  18. package/dist/components/icons/svg-icons/ForwardIcon.d.ts +5 -0
  19. package/dist/components/icons/svg-icons/ForwardIcon.d.ts.map +1 -0
  20. package/dist/components/icons/svg-icons/FullscreenEnterIcon.d.ts +5 -0
  21. package/dist/components/icons/svg-icons/FullscreenEnterIcon.d.ts.map +1 -0
  22. package/dist/components/icons/svg-icons/FullscreenExitIcon.d.ts +5 -0
  23. package/dist/components/icons/svg-icons/FullscreenExitIcon.d.ts.map +1 -0
  24. package/dist/components/icons/svg-icons/LoadingIcon.d.ts +5 -0
  25. package/dist/components/icons/svg-icons/LoadingIcon.d.ts.map +1 -0
  26. package/dist/components/icons/svg-icons/PauseIcon.d.ts +5 -0
  27. package/dist/components/icons/svg-icons/PauseIcon.d.ts.map +1 -0
  28. package/dist/components/icons/svg-icons/PlayIcon.d.ts +5 -0
  29. package/dist/components/icons/svg-icons/PlayIcon.d.ts.map +1 -0
  30. package/dist/components/icons/svg-icons/PlaybackSpeedIcon.d.ts +5 -0
  31. package/dist/components/icons/svg-icons/PlaybackSpeedIcon.d.ts.map +1 -0
  32. package/dist/components/icons/svg-icons/QualityIcon.d.ts +5 -0
  33. package/dist/components/icons/svg-icons/QualityIcon.d.ts.map +1 -0
  34. package/dist/components/icons/svg-icons/SettingsIcon.d.ts +5 -0
  35. package/dist/components/icons/svg-icons/SettingsIcon.d.ts.map +1 -0
  36. package/dist/components/icons/svg-icons/SliderIcon.d.ts +5 -0
  37. package/dist/components/icons/svg-icons/SliderIcon.d.ts.map +1 -0
  38. package/dist/components/icons/svg-icons/SubtitleIcon.d.ts +5 -0
  39. package/dist/components/icons/svg-icons/SubtitleIcon.d.ts.map +1 -0
  40. package/dist/components/icons/svg-icons/SubtitleOffIcon.d.ts +5 -0
  41. package/dist/components/icons/svg-icons/SubtitleOffIcon.d.ts.map +1 -0
  42. package/dist/components/icons/svg-icons/UnPiPIcon.d.ts +4 -0
  43. package/dist/components/icons/svg-icons/UnPiPIcon.d.ts.map +1 -0
  44. package/dist/components/icons/svg-icons/VolumeMutedIcon.d.ts +5 -0
  45. package/dist/components/icons/svg-icons/VolumeMutedIcon.d.ts.map +1 -0
  46. package/dist/components/icons/svg-icons/VolumeOneIcon.d.ts +5 -0
  47. package/dist/components/icons/svg-icons/VolumeOneIcon.d.ts.map +1 -0
  48. package/dist/components/icons/svg-icons/VolumeThreeIcon.d.ts +5 -0
  49. package/dist/components/icons/svg-icons/VolumeThreeIcon.d.ts.map +1 -0
  50. package/dist/components/icons/svg-icons/VolumeTwoIcon.d.ts +5 -0
  51. package/dist/components/icons/svg-icons/VolumeTwoIcon.d.ts.map +1 -0
  52. package/dist/components/variants/gradient/gradient-audio.d.ts +13 -0
  53. package/dist/components/variants/gradient/gradient-audio.d.ts.map +1 -0
  54. package/dist/components/variants/gradient/gradient-controls.d.ts +4 -0
  55. package/dist/components/variants/gradient/gradient-controls.d.ts.map +1 -0
  56. package/dist/components/variants/gradient/gradient-glyphs.d.ts +30 -0
  57. package/dist/components/variants/gradient/gradient-glyphs.d.ts.map +1 -0
  58. package/dist/components/variants/gradient/gradient-video.d.ts +16 -0
  59. package/dist/components/variants/gradient/gradient-video.d.ts.map +1 -0
  60. package/dist/index.css +1 -1
  61. package/dist/index.d.ts +5 -0
  62. package/dist/index.d.ts.map +1 -0
  63. package/dist/index.js +2 -112
  64. package/dist/index.js.map +1 -1
  65. package/dist/index.mjs +786 -6271
  66. package/dist/index.mjs.map +1 -1
  67. package/dist/package.json +3 -0
  68. package/package.json +110 -108
  69. package/src/components/variants/gradient/gradient-audio.tsx +47 -0
  70. package/src/components/variants/gradient/gradient-controls.tsx +254 -0
  71. package/src/components/variants/gradient/gradient-glyphs.test.tsx +69 -0
  72. package/src/components/variants/gradient/gradient-glyphs.tsx +129 -0
  73. package/src/components/variants/gradient/gradient-skin.css +211 -0
  74. package/src/components/variants/gradient/gradient-video.tsx +70 -0
  75. package/src/index.ts +5 -198
  76. package/src/test/accessibility-utils.ts +0 -59
  77. package/dist/context/media.js +0 -2
  78. package/dist/context/media.js.map +0 -1
  79. package/dist/context/media.mjs +0 -15
  80. package/dist/context/media.mjs.map +0 -1
  81. package/dist/context/store.js +0 -2
  82. package/dist/context/store.js.map +0 -1
  83. package/dist/context/store.mjs +0 -5
  84. package/dist/context/store.mjs.map +0 -1
  85. package/dist/context-BYwDoSRX.js +0 -1172
  86. package/dist/context-BYwDoSRX.js.map +0 -1
  87. package/dist/context-eNS62_ac.cjs +0 -23
  88. package/dist/context-eNS62_ac.cjs.map +0 -1
  89. package/dist/core-events-B1iM2F46.cjs +0 -2
  90. package/dist/core-events-B1iM2F46.cjs.map +0 -1
  91. package/dist/core-events-DunPHKRE.js +0 -484
  92. package/dist/core-events-DunPHKRE.js.map +0 -1
  93. package/dist/core-plugins-By-4-kGs.cjs +0 -2
  94. package/dist/core-plugins-By-4-kGs.cjs.map +0 -1
  95. package/dist/core-plugins-DSBWcTuX.js +0 -821
  96. package/dist/core-plugins-DSBWcTuX.js.map +0 -1
  97. package/dist/core.js +0 -37
  98. package/dist/core.js.map +0 -1
  99. package/dist/core.mjs +0 -1233
  100. package/dist/core.mjs.map +0 -1
  101. package/dist/enhanced-components-B_l3cist.js +0 -1142
  102. package/dist/enhanced-components-B_l3cist.js.map +0 -1
  103. package/dist/enhanced-components-BspFs9OO.cjs +0 -2
  104. package/dist/enhanced-components-BspFs9OO.cjs.map +0 -1
  105. package/dist/hooks-BXwI8QG6.cjs +0 -4
  106. package/dist/hooks-BXwI8QG6.cjs.map +0 -1
  107. package/dist/hooks-Bpi-jDiO.js +0 -1816
  108. package/dist/hooks-Bpi-jDiO.js.map +0 -1
  109. package/dist/minimal-components-C4z900Ro.js +0 -1926
  110. package/dist/minimal-components-C4z900Ro.js.map +0 -1
  111. package/dist/minimal-components-v4qBp6jv.cjs +0 -2
  112. package/dist/minimal-components-v4qBp6jv.cjs.map +0 -1
  113. package/dist/minimal-components.css +0 -1
  114. package/dist/mona-loading.gif +0 -0
  115. package/dist/performance-monitor-C5k6FDXw.js +0 -301
  116. package/dist/performance-monitor-C5k6FDXw.js.map +0 -1
  117. package/dist/performance-monitor-lJmuUjQQ.cjs +0 -2
  118. package/dist/performance-monitor-lJmuUjQQ.cjs.map +0 -1
  119. package/dist/utils-CFOue_9K.cjs +0 -9
  120. package/dist/utils-CFOue_9K.cjs.map +0 -1
  121. package/dist/utils-Dzpl2ArK.js +0 -2844
  122. package/dist/utils-Dzpl2ArK.js.map +0 -1
  123. package/dist/utils.js +0 -2
  124. package/dist/utils.js.map +0 -1
  125. package/dist/utils.mjs +0 -11
  126. package/dist/utils.mjs.map +0 -1
  127. package/src/adapters/videojs-adapter.tsx +0 -470
  128. package/src/assets/mona-loading.gif +0 -0
  129. package/src/components/AnimateLoading.tsx +0 -107
  130. package/src/components/LoadingState.tsx +0 -239
  131. package/src/components/__tests__/media-player-accessibility.test.tsx +0 -510
  132. package/src/components/__tests__/media-player-react19.test.tsx +0 -457
  133. package/src/components/accessibility/keyboard-help-overlay.tsx +0 -207
  134. package/src/components/audio-player.tsx +0 -85
  135. package/src/components/basic/index.ts +0 -37
  136. package/src/components/enhanced/advanced-features.tsx +0 -311
  137. package/src/components/enhanced/debug-panel.tsx +0 -448
  138. package/src/components/enhanced/enhanced-controls.tsx +0 -597
  139. package/src/components/enhanced/enhanced-media-player.tsx +0 -566
  140. package/src/components/enhanced/error-handling.tsx +0 -338
  141. package/src/components/enhanced/index.ts +0 -39
  142. package/src/components/enhanced/media-player-enhanced.tsx +0 -383
  143. package/src/components/examples/basic-video-player.tsx +0 -50
  144. package/src/components/examples/custom-controls-example.tsx +0 -63
  145. package/src/components/examples/enhanced-controls-example.tsx +0 -100
  146. package/src/components/examples/enhanced-media-player-example.tsx +0 -302
  147. package/src/components/examples/loading-states-demo.tsx +0 -142
  148. package/src/components/examples/react-19-demo.tsx +0 -353
  149. package/src/components/examples/volume-orientation-demo.tsx +0 -106
  150. package/src/components/icon-demo.tsx +0 -196
  151. package/src/components/legacy.tsx +0 -87
  152. package/src/components/media-metadata-display.tsx +0 -99
  153. package/src/components/media-player-audio.tsx +0 -39
  154. package/src/components/media-player-controls.tsx +0 -223
  155. package/src/components/media-player-error.tsx +0 -163
  156. package/src/components/media-player-fullscreen.tsx +0 -109
  157. package/src/components/media-player-hls.tsx +0 -111
  158. package/src/components/media-player-loading.tsx +0 -76
  159. package/src/components/media-player-play.tsx +0 -75
  160. package/src/components/media-player-root.tsx +0 -338
  161. package/src/components/media-player-seek.tsx +0 -168
  162. package/src/components/media-player-time.tsx +0 -99
  163. package/src/components/media-player-tooltip.tsx +0 -58
  164. package/src/components/media-player-video.tsx +0 -54
  165. package/src/components/media-player-volume.tsx +0 -155
  166. package/src/components/media-player.tsx +0 -292
  167. package/src/components/media-suspense.tsx +0 -344
  168. package/src/components/player-settings-form.tsx +0 -158
  169. package/src/components/react-player-wrapper.tsx +0 -651
  170. package/src/components/simple-video-player.tsx +0 -62
  171. package/src/components/ui/blur-poster.tsx +0 -93
  172. package/src/components/ui/empty-state.tsx +0 -91
  173. package/src/components/ui/enhanced-seek-bar.tsx +0 -254
  174. package/src/components/ui/error-state.tsx +0 -124
  175. package/src/components/ui/fullscreen-button.tsx +0 -81
  176. package/src/components/ui/loading-skeleton.tsx +0 -107
  177. package/src/components/ui/media-resource-preloader.tsx +0 -458
  178. package/src/components/ui/offline-message.tsx +0 -98
  179. package/src/components/ui/play-button.tsx +0 -99
  180. package/src/components/ui/seek-bar.tsx +0 -191
  181. package/src/components/ui/time-display.tsx +0 -61
  182. package/src/components/ui/volume-control.tsx +0 -190
  183. package/src/components/unified/base-media-player.tsx +0 -252
  184. package/src/components/unified/media-player.tsx +0 -75
  185. package/src/components/variants/minimal/AccessibleMinimalPlayer.tsx +0 -587
  186. package/src/components/variants/minimal/EnhancedMinimalMediaPlayer.tsx +0 -573
  187. package/src/components/variants/minimal/MinimalControls.tsx +0 -124
  188. package/src/components/variants/minimal/MinimalLoading.tsx +0 -28
  189. package/src/components/variants/minimal/MinimalMediaPlayer.tsx +0 -318
  190. package/src/components/variants/minimal/MinimalOverlay.tsx +0 -58
  191. package/src/components/variants/minimal/MinimalPlayButton.tsx +0 -56
  192. package/src/components/variants/minimal/MinimalSeekBar.tsx +0 -140
  193. package/src/components/variants/minimal/MinimalVolumeControl.tsx +0 -75
  194. package/src/components/variants/minimal/SimpleMinimalPlayer.tsx +0 -206
  195. package/src/components/variants/minimal/components/CenterPlayButton.tsx +0 -22
  196. package/src/components/variants/minimal/components/ControlBar.tsx +0 -142
  197. package/src/components/variants/minimal/components/EnhancedSettingsMenu.tsx +0 -412
  198. package/src/components/variants/minimal/components/PlaybackControls.tsx +0 -46
  199. package/src/components/variants/minimal/components/RightControls.tsx +0 -164
  200. package/src/components/variants/minimal/components/SeekBar.tsx +0 -127
  201. package/src/components/variants/minimal/components/SettingsDropdown.tsx +0 -68
  202. package/src/components/variants/minimal/components/SubtitleDisplay.tsx +0 -21
  203. package/src/components/variants/minimal/components/TimeDisplay.tsx +0 -15
  204. package/src/components/variants/minimal/components/VideoElement.tsx +0 -33
  205. package/src/components/variants/minimal/components/VolumeControl.tsx +0 -220
  206. package/src/components/variants/minimal/index.ts +0 -23
  207. package/src/components/video-player.tsx +0 -100
  208. package/src/context/__tests__/media-store.test.ts +0 -509
  209. package/src/context/error-handling.ts +0 -398
  210. package/src/context/media-context.tsx +0 -254
  211. package/src/context/media-provider.tsx +0 -264
  212. package/src/context/media-store.ts +0 -387
  213. package/src/context/persistence.ts +0 -462
  214. package/src/core/events/event-system.ts +0 -448
  215. package/src/core/events/hooks.ts +0 -325
  216. package/src/core/events/index.ts +0 -22
  217. package/src/core/events/middleware.ts +0 -286
  218. package/src/core/index.ts +0 -22
  219. package/src/core/optimization/bundle-optimizer.tsx +0 -312
  220. package/src/core/performance/cicd.ts +0 -98
  221. package/src/core/performance/index.ts +0 -38
  222. package/src/core/performance/media-loading-optimizer.ts +0 -544
  223. package/src/core/performance/performance-budgets.ts +0 -437
  224. package/src/core/performance/performance-monitor.tsx +0 -488
  225. package/src/core/performance/performance-testing.ts +0 -461
  226. package/src/core/plugins/__tests__/registry.test.ts +0 -595
  227. package/src/core/plugins/base-plugin.ts +0 -225
  228. package/src/core/plugins/enhanced-plugin-system.ts +0 -435
  229. package/src/core/plugins/examples/analytics-plugin.ts +0 -241
  230. package/src/core/plugins/index.ts +0 -39
  231. package/src/core/plugins/registry.ts +0 -546
  232. package/src/core/plugins/types.ts +0 -169
  233. package/src/core/plugins/utils.ts +0 -317
  234. package/src/core/progressive-loading.tsx +0 -331
  235. package/src/core/providers/enhanced-hls-provider.ts +0 -307
  236. package/src/core/providers/lazy-provider.tsx +0 -14
  237. package/src/core/providers/service-provider.tsx +0 -164
  238. package/src/core/utils.tsx +0 -34
  239. package/src/features/media-session/media-session-manager.ts +0 -411
  240. package/src/features/picture-in-picture/pip-manager.ts +0 -428
  241. package/src/features/playlist/playlist-manager.ts +0 -626
  242. package/src/features/subtitles/subtitle-manager.ts +0 -435
  243. package/src/features/subtitles/subtitle-parser.ts +0 -284
  244. package/src/features/thumbnails/thumbnail-generator.ts +0 -528
  245. package/src/hooks/__tests__/currentTime-fix.test.ts +0 -149
  246. package/src/hooks/__tests__/use-media-player.test.ts +0 -712
  247. package/src/hooks/__tests__/use-media-state.test.tsx +0 -316
  248. package/src/hooks/accessibility/index.ts +0 -27
  249. package/src/hooks/accessibility/use-accessibility-preferences.ts +0 -190
  250. package/src/hooks/accessibility/use-focus-management.ts +0 -183
  251. package/src/hooks/accessibility/use-keyboard-shortcuts.ts +0 -152
  252. package/src/hooks/accessibility/use-screen-reader.ts +0 -212
  253. package/src/hooks/core/index.ts +0 -47
  254. package/src/hooks/legacy.ts +0 -21
  255. package/src/hooks/use-buffer-health.ts +0 -218
  256. package/src/hooks/use-enhanced-media-player.ts +0 -575
  257. package/src/hooks/use-focus-trap.tsx +0 -162
  258. package/src/hooks/use-intelligent-preloading.ts +0 -387
  259. package/src/hooks/use-keyboard-controls.ts +0 -115
  260. package/src/hooks/use-keyboard-shortcuts.ts +0 -255
  261. package/src/hooks/use-media-actions.ts +0 -184
  262. package/src/hooks/use-media-metadata.ts +0 -91
  263. package/src/hooks/use-media-optimistic.ts +0 -264
  264. package/src/hooks/use-media-player.ts +0 -343
  265. package/src/hooks/use-media-state.ts +0 -194
  266. package/src/hooks/use-memory-optimization.ts +0 -360
  267. package/src/hooks/use-network-status.ts +0 -124
  268. package/src/hooks/use-optimistic-controls.ts +0 -95
  269. package/src/hooks/use-optimized-media.ts +0 -283
  270. package/src/hooks/use-smart-preload.ts +0 -229
  271. package/src/hooks/use-touch-gestures.ts +0 -461
  272. package/src/hooks/useEnhancedSubtitles.ts +0 -258
  273. package/src/hooks/useKeyboardShortcuts.ts +0 -106
  274. package/src/hooks/useMediaControls.ts +0 -52
  275. package/src/hooks/useScreenshot.ts +0 -61
  276. package/src/hooks/useSubtitles.ts +0 -91
  277. package/src/hooks/useVideoPlayer.ts +0 -278
  278. package/src/icons/components/FullscreenIcon.tsx +0 -36
  279. package/src/icons/components/SettingsIcon.tsx +0 -37
  280. package/src/icons/components/animated/LoadingIcon.tsx +0 -27
  281. package/src/icons/components/animated/PlayPauseIcon.tsx +0 -42
  282. package/src/icons/components/media/PauseIcon.tsx +0 -38
  283. package/src/icons/components/media/PlayIcon.tsx +0 -37
  284. package/src/icons/components/volume/VolumeIcon.tsx +0 -38
  285. package/src/icons/components/volume/VolumeOffIcon.tsx +0 -38
  286. package/src/icons/index.ts +0 -56
  287. package/src/icons/provider.tsx +0 -58
  288. package/src/icons/types.ts +0 -60
  289. package/src/index-enhanced.ts +0 -35
  290. package/src/index-legacy.ts +0 -324
  291. package/src/index.css +0 -102
  292. package/src/lib/__tests__/async-utils.test.ts +0 -291
  293. package/src/lib/async-utils.ts +0 -194
  294. package/src/lib/deprecation.ts +0 -51
  295. package/src/lib/format-time.ts +0 -37
  296. package/src/lib/index.ts +0 -4
  297. package/src/lib/utils.ts +0 -6
  298. package/src/styles/globals.css +0 -462
  299. package/src/styles/minimal-player.css +0 -621
  300. package/src/styles/pixel-art.css +0 -41
  301. package/src/styles/videojs.css +0 -100
  302. package/src/types/events.ts +0 -47
  303. package/src/types/formats.ts +0 -79
  304. package/src/types/index.ts +0 -203
  305. package/src/types/videojs-plugins.d.ts +0 -14
@@ -1,264 +0,0 @@
1
- "use client"
2
-
3
- import React, { useOptimistic } from "react"
4
- import type { MediaState } from "../context/media-context"
5
- import type { EnhancedMediaStore, OptimisticMediaAction } from "../context/media-store"
6
-
7
- // Optimistic update reducer function
8
- function optimisticMediaReducer(state: MediaState, action: OptimisticMediaAction): MediaState {
9
- if (!action.optimistic) return state
10
-
11
- // Apply immediate optimistic updates for better UX
12
- switch (action.type) {
13
- case "PLAY":
14
- return { ...state, isPlaying: true, error: null }
15
-
16
- case "PAUSE":
17
- return { ...state, isPlaying: false }
18
-
19
- case "SEEK":
20
- return { ...state, currentTime: action.detail }
21
-
22
- case "SET_VOLUME":
23
- return { ...state, volume: Math.max(0, Math.min(1, action.detail)) }
24
-
25
- case "MUTE":
26
- return { ...state, isMuted: true }
27
-
28
- case "UNMUTE":
29
- return { ...state, isMuted: false }
30
-
31
- case "TOGGLE_MUTE":
32
- return { ...state, isMuted: !state.isMuted }
33
-
34
- case "ENTER_FULLSCREEN":
35
- return { ...state, isFullscreen: true }
36
-
37
- case "EXIT_FULLSCREEN":
38
- return { ...state, isFullscreen: false }
39
-
40
- case "SET_PLAYBACK_RATE":
41
- return { ...state, playbackRate: action.detail }
42
-
43
- case "TOGGLE_SUBTITLES":
44
- return { ...state, subtitlesShowing: !state.subtitlesShowing }
45
-
46
- case "PREVIEW_TIME":
47
- return { ...state, previewTime: action.detail }
48
-
49
- case "CLEAR_PREVIEW":
50
- return {
51
- ...state,
52
- previewTime: null,
53
- previewImage: null,
54
- previewCoords: null
55
- }
56
-
57
- default:
58
- return state
59
- }
60
- }
61
-
62
- // Media optimistic state hook using React 19's useOptimistic
63
- export function useMediaOptimistic(store: EnhancedMediaStore) {
64
- const actualState = React.useSyncExternalStore(store.subscribe, store.getState, store.getState)
65
-
66
- const [optimisticState, addOptimistic] = useOptimistic(actualState, optimisticMediaReducer)
67
-
68
- // Enhanced dispatch that uses optimistic updates
69
- const dispatchOptimistic = React.useCallback(
70
- async (action: OptimisticMediaAction) => {
71
- // Add optimistic update immediately
72
- if (action.optimistic) {
73
- addOptimistic(action)
74
- }
75
-
76
- // Dispatch the actual action
77
- await store.dispatchOptimistic(action)
78
- },
79
- [store, addOptimistic]
80
- )
81
-
82
- // Quick actions with optimistic updates
83
- const optimisticActions = React.useMemo(
84
- () => ({
85
- // Play/Pause with immediate feedback
86
- togglePlayPause: async () => {
87
- const action: OptimisticMediaAction = {
88
- type: optimisticState.isPlaying ? "PAUSE" : "PLAY",
89
- optimistic: true,
90
- timestamp: Date.now()
91
- }
92
- await dispatchOptimistic(action)
93
- },
94
-
95
- // Volume control with immediate slider feedback
96
- setVolumeOptimistic: async (volume: number) => {
97
- const action: OptimisticMediaAction = {
98
- type: "SET_VOLUME",
99
- detail: Math.max(0, Math.min(1, volume)),
100
- optimistic: true,
101
- timestamp: Date.now()
102
- }
103
- await dispatchOptimistic(action)
104
- },
105
-
106
- // Seek with immediate scrub feedback
107
- seekOptimistic: async (time: number) => {
108
- const action: OptimisticMediaAction = {
109
- type: "SEEK",
110
- detail: time,
111
- optimistic: true,
112
- timestamp: Date.now()
113
- }
114
- await dispatchOptimistic(action)
115
- },
116
-
117
- // Mute toggle with immediate icon change
118
- toggleMuteOptimistic: async () => {
119
- const action: OptimisticMediaAction = {
120
- type: "TOGGLE_MUTE",
121
- optimistic: true,
122
- timestamp: Date.now()
123
- }
124
- await dispatchOptimistic(action)
125
- },
126
-
127
- // Fullscreen toggle with immediate layout change
128
- toggleFullscreenOptimistic: async () => {
129
- const action: OptimisticMediaAction = {
130
- type: optimisticState.isFullscreen ? "EXIT_FULLSCREEN" : "ENTER_FULLSCREEN",
131
- optimistic: true,
132
- timestamp: Date.now()
133
- }
134
- await dispatchOptimistic(action)
135
- },
136
-
137
- // Playback rate with immediate feedback
138
- setPlaybackRateOptimistic: async (rate: number) => {
139
- const action: OptimisticMediaAction = {
140
- type: "SET_PLAYBACK_RATE",
141
- detail: rate,
142
- optimistic: true,
143
- timestamp: Date.now()
144
- }
145
- await dispatchOptimistic(action)
146
- },
147
-
148
- // Subtitle toggle with immediate visibility change
149
- toggleSubtitlesOptimistic: async () => {
150
- const action: OptimisticMediaAction = {
151
- type: "TOGGLE_SUBTITLES",
152
- optimistic: true,
153
- timestamp: Date.now()
154
- }
155
- await dispatchOptimistic(action)
156
- }
157
- }),
158
- [optimisticState, dispatchOptimistic]
159
- )
160
-
161
- // State selectors that return optimistic values
162
- const selectors = React.useMemo(
163
- () => ({
164
- isPlaying: optimisticState.isPlaying,
165
- currentTime: optimisticState.currentTime,
166
- volume: optimisticState.volume,
167
- isMuted: optimisticState.isMuted,
168
- isFullscreen: optimisticState.isFullscreen,
169
- playbackRate: optimisticState.playbackRate,
170
- subtitlesShowing: optimisticState.subtitlesShowing,
171
- previewTime: optimisticState.previewTime,
172
-
173
- // Computed values
174
- progress:
175
- optimisticState.duration > 0 ? optimisticState.currentTime / optimisticState.duration : 0,
176
-
177
- formattedTime: formatTime(optimisticState.currentTime),
178
- formattedDuration: formatTime(optimisticState.duration),
179
-
180
- // State checks
181
- canPlay: !optimisticState.isLoading && !optimisticState.error,
182
- hasError: !!optimisticState.error,
183
- isBuffering: optimisticState.isLoading
184
- }),
185
- [optimisticState]
186
- )
187
-
188
- return {
189
- state: optimisticState,
190
- actualState,
191
- ...selectors,
192
- ...optimisticActions,
193
- dispatchOptimistic
194
- }
195
- }
196
-
197
- // Time formatting utility
198
- function formatTime(seconds: number): string {
199
- if (!seconds || !isFinite(seconds)) return "0:00"
200
-
201
- const mins = Math.floor(seconds / 60)
202
- const secs = Math.floor(seconds % 60)
203
- const hours = Math.floor(mins / 60)
204
-
205
- if (hours > 0) {
206
- return `${hours}:${String(mins % 60).padStart(2, "0")}:${String(secs).padStart(2, "0")}`
207
- }
208
-
209
- return `${mins}:${String(secs).padStart(2, "0")}`
210
- }
211
-
212
- // Hook for components that need to detect optimistic updates
213
- export function useOptimisticUpdates(store: EnhancedMediaStore) {
214
- const [hasOptimisticUpdates, setHasOptimisticUpdates] = React.useState(false)
215
-
216
- React.useEffect(() => {
217
- const unsubscribe = store.subscribe(() => {
218
- const actualState = store.getState()
219
- const optimisticState = store.getOptimisticState()
220
-
221
- const hasUpdates = JSON.stringify(actualState) !== JSON.stringify(optimisticState)
222
- setHasOptimisticUpdates(hasUpdates)
223
- })
224
-
225
- return unsubscribe
226
- }, [store])
227
-
228
- return hasOptimisticUpdates
229
- }
230
-
231
- // Hook to get both actual and optimistic state for debugging
232
- export function useMediaStateComparison(store: EnhancedMediaStore) {
233
- const actualState = React.useSyncExternalStore(store.subscribe, store.getState, store.getState)
234
-
235
- const optimisticState = React.useSyncExternalStore(
236
- store.subscribe,
237
- store.getOptimisticState,
238
- store.getOptimisticState
239
- )
240
-
241
- const differences = React.useMemo(() => {
242
- const diffs: any[] = []
243
-
244
- const stateKeys = Object.keys(actualState) as Array<keyof MediaState>
245
- stateKeys.forEach((key) => {
246
- if (JSON.stringify(actualState[key]) !== JSON.stringify(optimisticState[key])) {
247
- diffs.push({
248
- key: key as string,
249
- actual: actualState[key],
250
- optimistic: optimisticState[key]
251
- })
252
- }
253
- })
254
-
255
- return diffs
256
- }, [actualState, optimisticState])
257
-
258
- return {
259
- actualState,
260
- optimisticState,
261
- differences,
262
- hasOptimisticUpdates: differences.length > 0
263
- }
264
- }
@@ -1,343 +0,0 @@
1
- "use client"
2
-
3
- import { useCallback, useEffect, useRef } from "react"
4
- import type { MediaPlayerControls } from "../types/index"
5
- import { useMediaState } from "./use-media-state"
6
-
7
- interface UseMediaPlayerOptions {
8
- src: string | string[]
9
- autoPlay?: boolean
10
- loop?: boolean
11
- muted?: boolean
12
- volume?: number
13
- preload?: "none" | "metadata" | "auto"
14
- crossOrigin?: "anonymous" | "use-credentials"
15
- playbackRates?: number[]
16
- keyboardControls?: boolean
17
- showControls?: boolean
18
- customControls?: boolean
19
- onPlay?: () => void
20
- onPause?: () => void
21
- onTimeUpdate?: (currentTime: number) => void
22
- onDurationChange?: (duration: number) => void
23
- onVolumeChange?: (volume: number) => void
24
- onError?: (error: string) => void
25
- onLoadStart?: () => void
26
- onLoadedData?: () => void
27
- onEnded?: () => void
28
- }
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
-
53
- export function useMediaPlayer(options: UseMediaPlayerOptions) {
54
- const mediaRef = useRef<HTMLVideoElement | HTMLAudioElement>(null)
55
- const containerRef = useRef<HTMLDivElement>(null)
56
-
57
- const {
58
- state,
59
- setPlaying,
60
- setCurrentTime,
61
- setDuration,
62
- setVolume,
63
- setMuted,
64
- setLoading,
65
- setFullscreen,
66
- setError,
67
- setBuffered
68
- } = useMediaState()
69
-
70
- // Initialize media element with options
71
- useEffect(() => {
72
- const media = mediaRef.current
73
- if (!media) return
74
-
75
- // Set initial properties
76
- if (options.volume !== undefined) {
77
- media.volume = options.volume
78
- setVolume(options.volume)
79
- }
80
- if (options.muted !== undefined) {
81
- media.muted = options.muted
82
- setMuted(options.muted)
83
- }
84
- if (options.autoPlay !== undefined) {
85
- media.autoplay = options.autoPlay
86
- }
87
- if (options.loop !== undefined) {
88
- media.loop = options.loop
89
- }
90
- if (options.preload !== undefined) {
91
- media.preload = options.preload
92
- }
93
- if (options.crossOrigin !== undefined) {
94
- media.crossOrigin = options.crossOrigin
95
- }
96
- }, [options, setVolume, setMuted])
97
-
98
- // Event handlers
99
- const handlePlay = useCallback(() => {
100
- setPlaying(true)
101
- options.onPlay?.()
102
- }, [setPlaying, options])
103
-
104
- const handlePause = useCallback(() => {
105
- setPlaying(false)
106
- options.onPause?.()
107
- }, [setPlaying, options])
108
-
109
- const handleTimeUpdate = useCallback(() => {
110
- const media = mediaRef.current
111
- if (!media) return
112
-
113
- setCurrentTime(media.currentTime)
114
- options.onTimeUpdate?.(media.currentTime)
115
- }, [setCurrentTime, options])
116
-
117
- const handleDurationChange = useCallback(() => {
118
- const media = mediaRef.current
119
- if (!media) return
120
-
121
- setDuration(media.duration)
122
- options.onDurationChange?.(media.duration)
123
- }, [setDuration, options])
124
-
125
- const handleVolumeChange = useCallback(() => {
126
- const media = mediaRef.current
127
- if (!media) return
128
-
129
- setVolume(media.volume)
130
- setMuted(media.muted)
131
- options.onVolumeChange?.(media.volume)
132
- }, [setVolume, setMuted, options])
133
-
134
- const handleLoadStart = useCallback(() => {
135
- setLoading(true)
136
- setError(null)
137
- options.onLoadStart?.()
138
- }, [setLoading, setError, options])
139
-
140
- const handleLoadedData = useCallback(() => {
141
- setLoading(false)
142
- options.onLoadedData?.()
143
- }, [setLoading, options])
144
-
145
- const handleError = useCallback(() => {
146
- const media = mediaRef.current
147
- if (!media) return
148
-
149
- const errorMessage = media.error?.message || "An error occurred while loading the media"
150
- setError(errorMessage)
151
- setLoading(false)
152
- options.onError?.(errorMessage)
153
- }, [setError, setLoading, options])
154
-
155
- const handleEnded = useCallback(() => {
156
- setPlaying(false)
157
- options.onEnded?.()
158
- }, [setPlaying, options])
159
-
160
- const handleProgress = useCallback(() => {
161
- const media = mediaRef.current
162
- if (!media) return
163
-
164
- setBuffered(media.buffered)
165
- }, [setBuffered])
166
-
167
- // Attach event listeners
168
- useEffect(() => {
169
- const media = mediaRef.current
170
- if (!media) return
171
-
172
- media.addEventListener("play", handlePlay)
173
- media.addEventListener("pause", handlePause)
174
- media.addEventListener("timeupdate", handleTimeUpdate)
175
- media.addEventListener("durationchange", handleDurationChange)
176
- media.addEventListener("volumechange", handleVolumeChange)
177
- media.addEventListener("loadstart", handleLoadStart)
178
- media.addEventListener("loadeddata", handleLoadedData)
179
- media.addEventListener("error", handleError)
180
- media.addEventListener("ended", handleEnded)
181
- media.addEventListener("progress", handleProgress)
182
-
183
- return () => {
184
- media.removeEventListener("play", handlePlay)
185
- media.removeEventListener("pause", handlePause)
186
- media.removeEventListener("timeupdate", handleTimeUpdate)
187
- media.removeEventListener("durationchange", handleDurationChange)
188
- media.removeEventListener("volumechange", handleVolumeChange)
189
- media.removeEventListener("loadstart", handleLoadStart)
190
- media.removeEventListener("loadeddata", handleLoadedData)
191
- media.removeEventListener("error", handleError)
192
- media.removeEventListener("ended", handleEnded)
193
- media.removeEventListener("progress", handleProgress)
194
- }
195
- }, [
196
- handlePlay,
197
- handlePause,
198
- handleTimeUpdate,
199
- handleDurationChange,
200
- handleVolumeChange,
201
- handleLoadStart,
202
- handleLoadedData,
203
- handleError,
204
- handleEnded,
205
- handleProgress
206
- ])
207
-
208
- // Control functions
209
- const play = useCallback(async () => {
210
- const media = mediaRef.current
211
- if (!media) return
212
-
213
- try {
214
- await media.play()
215
- } catch (error) {
216
- console.error("Failed to play media:", error)
217
- setError("Failed to play media")
218
- }
219
- }, [setError])
220
-
221
- const pause = useCallback(() => {
222
- const media = mediaRef.current
223
- if (!media) return
224
-
225
- media.pause()
226
- }, [])
227
-
228
- const toggle = useCallback(() => {
229
- if (state.isPlaying) {
230
- pause()
231
- } else {
232
- play()
233
- }
234
- }, [state.isPlaying, pause, play])
235
-
236
- const seek = useCallback((time: number) => {
237
- const media = mediaRef.current
238
- if (!media) return
239
-
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
- }
245
-
246
- const clampedTime = Math.max(0, Math.min(time, duration))
247
- safeSetCurrentTime(media, clampedTime)
248
- }, [])
249
-
250
- const setVolumeControl = useCallback((volume: number) => {
251
- const media = mediaRef.current
252
- if (!media) return
253
-
254
- const clampedVolume = Math.max(0, Math.min(1, volume))
255
- media.volume = clampedVolume
256
- }, [])
257
-
258
- const mute = useCallback(() => {
259
- const media = mediaRef.current
260
- if (!media) return
261
-
262
- media.muted = true
263
- }, [])
264
-
265
- const unmute = useCallback(() => {
266
- const media = mediaRef.current
267
- if (!media) return
268
-
269
- media.muted = false
270
- }, [])
271
-
272
- const toggleMute = useCallback(() => {
273
- const media = mediaRef.current
274
- if (!media) return
275
-
276
- media.muted = !media.muted
277
- }, [])
278
-
279
- const enterFullscreen = useCallback(async () => {
280
- const container = containerRef.current
281
- if (!container || !document.fullscreenEnabled) return
282
-
283
- try {
284
- await container.requestFullscreen()
285
- setFullscreen(true)
286
- } catch (error) {
287
- console.error("Failed to enter fullscreen:", error)
288
- }
289
- }, [setFullscreen])
290
-
291
- const exitFullscreen = useCallback(async () => {
292
- if (!document.fullscreenElement) return
293
-
294
- try {
295
- await document.exitFullscreen()
296
- setFullscreen(false)
297
- } catch (error) {
298
- console.error("Failed to exit fullscreen:", error)
299
- }
300
- }, [setFullscreen])
301
-
302
- const toggleFullscreen = useCallback(() => {
303
- if (document.fullscreenElement) {
304
- exitFullscreen()
305
- } else {
306
- enterFullscreen()
307
- }
308
- }, [exitFullscreen, enterFullscreen])
309
-
310
- // Listen for fullscreen changes
311
- useEffect(() => {
312
- const handleFullscreenChange = () => {
313
- setFullscreen(!!document.fullscreenElement)
314
- }
315
-
316
- document.addEventListener("fullscreenchange", handleFullscreenChange)
317
- return () => {
318
- document.removeEventListener("fullscreenchange", handleFullscreenChange)
319
- }
320
- }, [setFullscreen])
321
-
322
- const controls: MediaPlayerControls = {
323
- play,
324
- pause,
325
- toggle,
326
- seek,
327
- setCurrentTime: seek,
328
- setVolume: setVolumeControl,
329
- mute,
330
- unmute,
331
- toggleMute,
332
- enterFullscreen,
333
- exitFullscreen,
334
- toggleFullscreen
335
- }
336
-
337
- return {
338
- state,
339
- controls,
340
- mediaRef,
341
- containerRef
342
- }
343
- }